J'ai un cas d'utilisation particulier où j'ai plusieurs lignes pour le même client où chaque objet ligne ressemble:
root
-c1: BigInt
-c2: String
-c3: Double
-c4: Double
-c5: Map[String, Int]
Maintenant, je dois faire le groupe par colonne c1 et collecter toutes les lignes sous forme de liste pour le même client comme:
c1, [Row1, Row3, Row4]
c2, [Row2, Row5]
J'ai essayé de faire de cette façon dataset.withColumn("combined", array("c1","c2","c3","c4","c5")).groupBy("c1").agg(collect_list("combined"))
mais j'obtiens une exception:
Exception in thread "main" org.Apache.spark.sql.AnalysisException: cannot resolve 'array(`c1`, `c2`, `c3`, `c4`, `c5`)' due to data type mismatch: input to function array should all be the same type, but it's [bigint, string, double, double, map<string,map<string,double>>];;
Au lieu de array
, vous pouvez utiliser la fonction struct
pour combiner les colonnes et utiliser groupBy
et collect_list
fonction d'agrégation comme
import org.Apache.spark.sql.functions._
df.withColumn("combined", struct("c1","c2","c3","c4","c5"))
.groupBy("c1").agg(collect_list("combined").as("combined_list"))
.show(false)
de sorte que vous avez ensemble de données groupé avec schema
as
root
|-- c1: integer (nullable = false)
|-- combined_list: array (nullable = true)
| |-- element: struct (containsNull = true)
| | |-- c1: integer (nullable = false)
| | |-- c2: string (nullable = true)
| | |-- c3: string (nullable = true)
| | |-- c4: string (nullable = true)
| | |-- c5: map (nullable = true)
| | | |-- key: string
| | | |-- value: integer (valueContainsNull = false)
J'espère que la réponse est utile
Si vous voulez que le résultat se compose de collections de Row
s, envisagez de vous transformer en RDD comme suit:
import org.Apache.spark.sql.functions._
import org.Apache.spark.sql.Row
def df = Seq(
(BigInt(10), "x", 1.0, 2.0, Map("a"->1, "b"->2)),
(BigInt(10), "y", 3.0, 4.0, Map("c"->3)),
(BigInt(20), "z", 5.0, 6.0, Map("d"->4, "e"->5))
).
toDF("c1", "c2", "c3", "c4", "c5").
// as[(BigInt, String, Double, Double, Map[String, Int])]
df.rdd.map(r => (r.getDecimal(0), r)).groupByKey.collect
// res1: Array[(Java.math.BigDecimal, Iterable[org.Apache.spark.sql.Row])] = Array(
// (10,CompactBuffer([10,x,1.0,2.0,Map(a -> 1, b -> 2)], [10,y,3.0,4.0,Map(c -> 3)])),
// (20,CompactBuffer([20,z,5.0,6.0,Map(d -> 4, e -> 5)]))
// )
Ou, si vous êtes bon avec des collections de lignes de type struct
- dans un DataFrame, voici une approche alternative:
val cols = ds.columns
df.groupBy("c1").agg(collect_list(struct(cols.head, cols.tail: _*)).as("row_list")).
show(false)
// +---+----------------------------------------------------------------+
// |c1 |row_list |
// +---+----------------------------------------------------------------+
// |20 |[[20,z,5.0,6.0,Map(d -> 4, e -> 5)]] |
// |10 |[[10,x,1.0,2.0,Map(a -> 1, b -> 2)], [10,y,3.0,4.0,Map(c -> 3)]]|
// +---+----------------------------------------------------------------+