J'ai un DataFrame généré comme suit:
df.groupBy($"Hour", $"Category")
.agg(sum($"value").alias("TotalValue"))
.sort($"Hour".asc,$"TotalValue".desc))
Les résultats ressemblent à:
+----+--------+----------+
|Hour|Category|TotalValue|
+----+--------+----------+
| 0| cat26| 30.9|
| 0| cat13| 22.1|
| 0| cat95| 19.6|
| 0| cat105| 1.3|
| 1| cat67| 28.5|
| 1| cat4| 26.8|
| 1| cat13| 12.6|
| 1| cat23| 5.3|
| 2| cat56| 39.6|
| 2| cat40| 29.7|
| 2| cat187| 27.9|
| 2| cat68| 9.8|
| 3| cat8| 35.6|
| ...| ....| ....|
+----+--------+----------+
Je voudrais créer de nouveaux cadres de données basés sur chaque valeur unique de col("Hour")
, c'est-à-dire.
La sortie souhaitée serait donc:
df0 as:
+----+--------+----------+
|Hour|Category|TotalValue|
+----+--------+----------+
| 0| cat26| 30.9|
| 0| cat13| 22.1|
| 0| cat95| 19.6|
| 0| cat105| 1.3|
+----+--------+----------+
df1 as:
+----+--------+----------+
|Hour|Category|TotalValue|
+----+--------+----------+
| 1| cat67| 28.5|
| 1| cat4| 26.8|
| 1| cat13| 12.6|
| 1| cat23| 5.3|
+----+--------+----------+
et de même,
df2 as:
+----+--------+----------+
|Hour|Category|TotalValue|
+----+--------+----------+
| 2| cat56| 39.6|
| 2| cat40| 29.7|
| 2| cat187| 27.9|
| 2| cat68| 9.8|
+----+--------+----------+
Toute aide est grandement appréciée.
EDIT 1:
Ce que j'ai essayé:
df.foreach(
row => splitHour(row)
)
def splitHour(row: Row) ={
val Hour=row.getAs[Long]("Hour")
val HourDF= sparkSession.createDataFrame(List((s"$Hour",1)))
val hdf=HourDF.withColumnRenamed("_1","Hour_unique").drop("_2")
val mydf: DataFrame =df.join(hdf,df("Hour")===hdf("Hour_unique"))
mydf.write.mode("overwrite").parquet(s"/home/dev/shaishave/etc/myparquet/$Hour/")
}
PROBLÈME AVEC CETTE STRATÉGIE:
Cela a pris 8 heures quand il a été exécuté sur une trame de données df
qui avait plus de 1 million de lignes et spark a été donné environ 10 Go RAM sur un seul noeud. Ainsi, join
s'avère très inefficace.
Attention: je dois écrire chaque dataframe mydf
en tant que parquet qui a un schéma imbriqué qui doit être maintenu (pas aplati).
Comme indiqué dans mes commentaires, une approche potentiellement facile à ce problème serait d'utiliser:
df.write.partitionBy("hour").saveAsTable("myparquet")
Comme indiqué, la structure des dossiers serait myparquet/hour=1
, myparquet/hour=2
, ..., myparquet/hour=24
Par opposition à myparquet/1
, myparquet/2
,. .., myparquet/24
.
Pour modifier la structure des dossiers, vous pouvez
hcat.dynamic.partitioning.custom.pattern
Dans un HiveContext explicite; plus d'informations sur HCatalog DynamicPartitions .df.write.partitionBy.saveAsTable(...)
avec quelque chose comme for f in *; do mv $f ${f/${f:0:5}/} ; done
Qui supprimerait le texte Hour=
Du nom du dossier.Il est important de noter qu'en modifiant le modèle de dénomination des dossiers, lorsque vous exécutez spark.read.parquet(...)
dans ce dossier, Spark ne comprendra pas automatiquement les partitions dynamiques car il manque les informations de partitionKey (c'est-à-dire Hour
).
Cela a été répondu ici pour Spark (Scala):
et ici pour pyspark:
PySpark - Fractionner/filtrer DataFrame par les valeurs de la colonne