web-dev-qa-db-ja.com

SPARK DataFrame:同じ列の値に基づいて各グループのデータフレームを効率的に分割する方法

次のように生成されたDataFrameがあります。

_df.groupBy($"Hour", $"Category")
  .agg(sum($"value").alias("TotalValue"))
  .sort($"Hour".asc,$"TotalValue".desc))
_

結果は次のようになります。

_+----+--------+----------+
|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|
| ...|    ....|      ....|
+----+--------+----------+
_

col("Hour")のすべての一意の値に基づいて新しいデータフレームを作成したいと考えています。

  • hour == 0のグループの場合
  • hour == 1のグループの場合
  • hour == 2などのグループの場合...

したがって、望ましい出力は次のようになります。

_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|
+----+--------+----------+
_

同様に、

_df2 as:

+----+--------+----------+
|Hour|Category|TotalValue|
+----+--------+----------+
|   2|   cat56|      39.6|
|   2|   cat40|      29.7|
|   2|  cat187|      27.9|
|   2|   cat68|       9.8|
+----+--------+----------+
_

どんな助けでも大歓迎です。

EDIT 1:

私が試したこと:

_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/")
  }
_

この戦略の問題:

100万行を超えるデータフレームdfで実行すると8時間かかり、sparkジョブに約10 GBが割り当てられましたRAMしたがって、joinは非常に非効率的です。

警告:各データフレームmydfは、維持する必要がある(フラット化されていない)ネストされたスキーマを持つパーケットとして書き込む必要があります。

8
shubham rajput

私のコメントで述べたように、この問題への潜在的に簡単なアプローチの1つは、以下を使用することです。

df.write.partitionBy("hour").saveAsTable("myparquet")

前述のように、フォルダー構造は、myparquet/hour=1myparquet/hour=2、...、myparquet/hour=24ではなく、myparquet/1myparquet/2、...、myparquet/24になります。

フォルダ構造を変更するには、次のことができます

  1. 明示的なHiveContext内でHive構成設定hcat.dynamic.partitioning.custom.patternを使用する可能性があります。詳細は HCatalog DynamicPartitions を参照してください。
  2. 別のアプローチは、for f in *; do mv $f ${f/${f:0:5}/} ; doneのようなものでdf.write.partitionBy.saveAsTable(...)コマンドを実行した直後にファイルシステムを変更することです。これにより、フォルダー名からHour=テキストが削除されます。

フォルダーの名前付けパターンを変更することにより、そのフォルダーでspark.read.parquet(...)を実行している場合、Sparkは欠落しているため、動的パーティションを自動的に理解しません。 partitionKey(つまりHour)情報。

7
Denny Lee

Spark(Scala):

SCALA and SPARK で、データフレームを同じ列値のデータフレームに分割するにはどうすればよいですか?

そしてここpysparkのために:

PySpark-列の値でDataFrameを分割/フィルター

2
O. Gindele