在PySpark数据帧中将列和添加为新列

时间:2015-08-12 02:59:13

标签: python apache-spark pyspark spark-dataframe

我使用的是PySpark,我有一个带有一堆数字列的Spark数据帧。我想添加一个列,它是所有其他列的总和。

假设我的数据框有列" a"," b"和" c"。我知道我可以这样做:

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

问题在于我不想单独输入每个列并添加它们,特别是如果我有很多列。我希望能够自动执行此操作,或者通过指定要添加的列名列表。还有另一种方法吗?

8 个答案:

答案 0 :(得分:36)

这并不明显。我看不到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. 将该列表传递给将在fold-type functional manner
  4. 中调用列重载的add函数的内容

    使用python的reduce,了解运算符重载的工作方式,以及列here的pyspark代码:

    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,而第二个要减少的参数中的括号术语需要括号,因为它是一个列表生成器表达式。

    经过测试,有效!

    $ 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)]
    

答案 1 :(得分:7)

将列表中的多列汇总为一列

PySpark的sum函数不支持列添加。 可以使用expr函数来实现。

from pyspark.sql.functions import expr

cols_list = ['a', 'b', 'c']

# Creating an addition expression using `join`
expression = '+'.join(cols_list)

df = df.withColumn('sum_cols', expr(expression))

这为我们提供了所需的列总和。

答案 2 :(得分:2)

解决方案

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

@Paul作品发布。然而,我遇到了我所看到的其他许多错误,

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 :(得分:1)

最直接的方法是使用expr函数

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

答案 4 :(得分:0)

我的问题与上述类似(有点复杂),因为我必须在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

答案 5 :(得分:0)

{{1}}

答案 6 :(得分:0)

一种非常简单的方法是仅使用select代替withcolumn,如下所示:

df = df.select('*', (col("a")+col("b")+col('c).alias("total"))

这应为您提供所需的总金额,并根据要求进行细微更改

答案 7 :(得分:0)

以下方法对我有用:

  1. 导入pyspark sql函数
    从pyspark.sql导入功能作为F
  2. 使用F.expr(list_of_columns)
    data_frame.withColumn('Total_Sum',F.expr('col_name 1 + col_name 2 + .. col_name n