私はこれを持っていますpython pandasデータフレームでローカルに実行されるコード:
df_result = pd.DataFrame(df
.groupby('A')
.apply(lambda x: myFunction(Zip(x.B, x.C), x.name))
これをPySparkで実行したいのですが、pyspark.sql.group.GroupedDataオブジェクトの処理に問題があります。
私は次を試しました:
sparkDF
.groupby('A')
.agg(myFunction(Zip('B', 'C'), 'A'))
返す
KeyError: 'A'
「A」は列ではなく、x.nameに相当するものが見つからないためだと思います。
その後
sparkDF
.groupby('A')
.map(lambda row: Row(myFunction(Zip('B', 'C'), 'A')))
.toDF()
ただし、次のエラーが発生します。
AttributeError: 'GroupedData' object has no attribute 'map'
どんな提案でも本当に感謝されます!
しようとしているのは、UDF(ユーザー定義関数)ではなく、UDAF(ユーザー定義集計関数)を記述することです。 UDAFは、キーでグループ化されたデータを処理する機能です。具体的には、グループ内の複数の値を単一のパーティションにマージする方法を定義し、次にキーのパーティション間で結果をマージする方法を定義する必要があります。現在、pythonにUDAFを実装する方法はありません。Scalaでのみ実装できます。
ただし、Pythonで回避できます。収集セットを使用してグループ化された値を収集し、通常のUDFを使用して必要な処理を実行できます。唯一の注意点は、collect_setがプリミティブ値でのみ機能するため、それらを文字列にエンコードする必要があることです。
from pyspark.sql.types import StringType
from pyspark.sql.functions import col, collect_list, concat_ws, udf
def myFunc(data_list):
for val in data_list:
b, c = data.split(',')
# do something
return <whatever>
myUdf = udf(myFunc, StringType())
df.withColumn('data', concat_ws(',', col('B'), col('C'))) \
.groupBy('A').agg(collect_list('data').alias('data'))
.withColumn('data', myUdf('data'))
重複排除が必要な場合は、collect_setを使用します。また、いくつかのキーに多くの値がある場合、キーのすべての値をクラスター上のどこかの単一パーティションに収集する必要があるため、これは遅くなります。最終結果が何らかの方法でキーごとの値を組み合わせて(たとえば合計することで)構築する値である場合、中間値を構築できる RDD aggregateByKey メソッドを使用して実装する方が速い場合がありますデータをシャッフルする前に、パーティション内の各キーに対して。
編集:11/21/2018
この回答が書かれて以来、pysparkはPandasを使用したUDAFのサポートを追加しました。 PandaのUDFとUDAFをストレートpython関数をRDDで使用する場合に使用すると、ニースのパフォーマンスが向上します。フードの下で、列をベクトル化します。 こちら でより良い説明をご覧になるか、または ser6910411 の例をご覧ください。
Spark 2.3からpandas_udf
を使用できます。 GROUPED_MAP
はCallable[[pandas.DataFrame], pandas.DataFrame]
または、言い換えると、入力と同じ形状のPandas DataFrame
から出力DataFrame
にマッピングする関数です。
たとえば、データが次のようになっている場合:
df = spark.createDataFrame(
[("a", 1, 0), ("a", -1, 42), ("b", 3, -1), ("b", 10, -2)],
("key", "value1", "value2")
)
value1
value2
の間のペアワイズ最小値の平均値を計算するには、出力スキーマを定義する必要があります。
from pyspark.sql.types import *
schema = StructType([
StructField("key", StringType()),
StructField("avg_min", DoubleType())
])
pandas_udf
:
import pandas as pd
from pyspark.sql.functions import pandas_udf
from pyspark.sql.functions import PandasUDFType
@pandas_udf(schema, functionType=PandasUDFType.GROUPED_MAP)
def g(df):
result = pd.DataFrame(df.groupby(df.key).apply(
lambda x: x.loc[:, ["value1", "value2"]].min(axis=1).mean()
))
result.reset_index(inplace=True, drop=False)
return result
そしてそれを適用します:
df.groupby("key").apply(g).show()
+---+-------+
|key|avg_min|
+---+-------+
| b| -1.5|
| a| -0.5|
+---+-------+
スキーマ定義とデコレータを除くと、現在のPandasコードはそのまま適用できます。
Spark 2.4.0)には、 GROUPED_AGG
バリアントもあり、Callable[[pandas.Series, ...], T]
を取ります。ここで、T
はプリミティブスカラーです。
import numpy as np
@pandas_udf(DoubleType(), functionType=PandasUDFType.GROUPED_AGG)
def f(x, y):
return np.minimum(x, y).mean()
これは標準のgroup_by
/agg
コンストラクトで使用できます:
df.groupBy("key").agg(f("value1", "value2").alias("avg_min")).show()
+---+-------+
|key|avg_min|
+---+-------+
| b| -1.5|
| a| -0.5|
+---+-------+
GROUPED_MAP
もGROUPPED_AGG
pandas_udf
もUserDefinedAggregateFunction
またはAggregator
と同じように動作せず、groupByKey
に近いことに注意してください。または無制限のフレームを持つウィンドウ関数。データが最初にシャッフルされ、その後のみUDFが適用されます。
最適化された実行のためには、 implement Scala UserDefinedAggregateFunction
and add Python wrapper 。
私は答えの上に拡張するつもりです。
したがって、@ pandas_udfを使用してpysparkでpandas.groupby()。applyのような同じロジックを実装できます。これはベクトル化メソッドであり、単純なudfよりも高速です。
from pyspark.sql.functions import pandas_udf,PandasUDFType
df3 = spark.createDataFrame(
[("a", 1, 0), ("a", -1, 42), ("b", 3, -1), ("b", 10, -2)],
("key", "value1", "value2")
)
from pyspark.sql.types import *
schema = StructType([
StructField("key", StringType()),
StructField("avg_value1", DoubleType()),
StructField("avg_value2", DoubleType()),
StructField("sum_avg", DoubleType()),
StructField("sub_avg", DoubleType())
])
@pandas_udf(schema, functionType=PandasUDFType.GROUPED_MAP)
def g(df):
gr = df['key'].iloc[0]
x = df.value1.mean()
y = df.value2.mean()
w = df.value1.mean() + df.value2.mean()
z = df.value1.mean() - df.value2.mean()
return pd.DataFrame([[gr]+[x]+[y]+[w]+[z]])
df3.groupby("key").apply(g).show()
以下の結果が得られます。
+---+----------+----------+-------+-------+
|key|avg_value1|avg_value2|sum_avg|sub_avg|
+---+----------+----------+-------+-------+
| b| 6.5| -1.5| 5.0| 8.0|
| a| 0.0| 21.0| 21.0| -21.0|
+---+----------+----------+-------+-------+
したがって、グループ化されたデータの他のフィールド間でより多くの計算を実行し、それらをリスト形式でデータフレームに追加できます。