web-dev-qa-db-ja.com

PySparkデータフレームの新しい列として列の合計を追加します

私はPySparkを使用していますが、数値列の束を持つSparkデータフレームがあります。他のすべての列の合計である列を追加します。

データフレームに列「a」、「b」、および「c」があるとします。私はこれができることを知っています:

df.withColumn('total_col', df.a + df.b + df.c)

問題は、特に多くの列がある場合、各列を個別に入力して追加したくないことです。これを自動的に、または追加したい列名のリストを指定して実行できるようにしたいのです。これを行う別の方法はありますか?

23
plam

これは明らかではありませんでした。 spark Dataframes APIで定義された列の行ベースの合計は表示されません。

バージョン2

これは非常に簡単な方法で実行できます。

newdf = df.withColumn('total', sum(df[col] for col in df.columns))

df.columnsはpysparkによってSpark Dataframeのすべての列名を与える文字列のリストとして提供されます。別の合計については、代わりに他の列名のリストを提供できます。

私はこれが最初の解決策として試されませんでした。しかし、それは機能します。

バージョン1

これは非常に複雑ですが、同様に機能します。

あなたはこれを行うことができます:

  1. つかいます df.columns列の名前のリストを取得する
  2. その名前リストを使用して、列のリストを作成します
  3. そのリストを、列のオーバーロードされたadd関数を fold-type機能的に で呼び出すものに渡します

Pythonの reduce を使用すると、演算子のオーバーロードがどのように機能するかについての知識と、列のpysparkコード here は次のようになります。

def column_add(a,b):
     return  a.__add__(b)

newdf = df.withColumn('total_col', 
         reduce(column_add, ( df[col] for col in df.columns ) ))

これはpython reduceであり、spark RDD reduceではなく、reduceの2番目のパラメーターの括弧用語にはリストジェネレーターであるため、括弧が必要です。表現。

テスト済み、動作中!

$ pyspark
>>> df = sc.parallelize([{'a': 1, 'b':2, 'c':3}, {'a':8, 'b':5, 'c':6}, {'a':3, 'b':1, 'c':0}]).toDF().cache()
>>> df
DataFrame[a: bigint, b: bigint, c: bigint]
>>> df.columns
['a', 'b', 'c']
>>> def column_add(a,b):
...     return a.__add__(b)
...
>>> df.withColumn('total', reduce(column_add, ( df[col] for col in df.columns ) )).collect()
[Row(a=1, b=2, c=3, total=6), Row(a=8, b=5, c=6, total=19), Row(a=3, b=1, c=0, total=4)]
37
Paul

ソリューション

_newdf = df.withColumn('total', sum(df[col] for col in df.columns))
_

@Paul Worksによる投稿。それにもかかわらず、私が見た他の多くのように、私はエラーを受け取っていました、

_TypeError: 'Column' object is not callable
_

しばらくして、問題を発見しました(少なくとも私の場合)。問題は、以前にいくつかのpyspark関数を次の行でインポートしたことです

_from pyspark.sql.functions import udf, col, count, sum, when, avg, mean, min
_

そのため、行はsum pysparkコマンドをインポートしましたが、df.withColumn('total', sum(df[col] for col in df.columns))は通常のpython sum関数を使用することになっています。

_del sum_を使用して、pyspark関数の参照を削除できます。

それ以外の場合、私の場合、インポートを

_import pyspark.sql.functions as F
_

そして、関数を_F.sum_として参照しました。

3
Francesco Boi

最も簡単な方法は、expr関数を使用することです

from pyspark.sql.functions import *
data = data.withColumn('total', expr("col1 + col2 + col3 + col4"))
2
Jonathan

私の問題は、PySparkデータフレームの新しい列として連続列の合計を追加する必要があるため、上記(少し複雑)に似ていました。このアプローチでは、上記のPaulのバージョン1のコードを使用します。

import pyspark
from pyspark.sql import SparkSession
import pandas as pd

spark = SparkSession.builder.appName('addColAsCumulativeSUM').getOrCreate()
df=spark.createDataFrame(data=[(1,2,3),(4,5,6),(3,2,1)\
                              ,(6,1,-4),(0,2,-2),(6,4,1)\
                              ,(4,5,2),(5,-3,-5),(6,4,-1)]\
                              ,schema=['x1','x2','x3'])
df.show()

+---+---+---+
| x1| x2| x3|
+---+---+---+
|  1|  2|  3|
|  4|  5|  6|
|  3|  2|  1|
|  6|  1| -4|
|  0|  2| -2|
|  6|  4|  1|
|  4|  5|  2|
|  5| -3| -5|
|  6|  4| -1|
+---+---+---+

colnames=df.columns

累積合計(連続)である新しい列を追加します。

for i in range(0,len(colnames)):
    colnameLst= colnames[0:i+1]
    colname = 'cm'+ str(i+1)
    df = df.withColumn(colname, sum(df[col] for col in colnameLst))

df.show()

+---+---+---+---+---+---+
| x1| x2| x3|cm1|cm2|cm3|
+---+---+---+---+---+---+
|  1|  2|  3|  1|  3|  6|
|  4|  5|  6|  4|  9| 15|
|  3|  2|  1|  3|  5|  6|
|  6|  1| -4|  6|  7|  3|
|  0|  2| -2|  0|  2|  0|
|  6|  4|  1|  6| 10| 11|
|  4|  5|  2|  4|  9| 11|
|  5| -3| -5|  5|  2| -3|
|  6|  4| -1|  6| 10|  9|
+---+---+---+---+---+---+

追加された「累積合計」列は次のとおりです。

cm1 = x1
cm2 = x1 + x2
cm3 = x1 + x2 + x3
0
Grant Shannon