从pyspark数据帧中减去均值

时间:2017-06-16 04:07:41

标签: apache-spark pyspark spark-dataframe

我正在尝试计算数据框中每列的平均值,并从列中的每个元素中减去。我创建了一个尝试这样做的函数,但是当我尝试使用UDF实现它时,我得到一个错误:'float'对象没有属性'map'。关于如何创建这样一个函数的任何想法?谢谢!

def normalize(data):
        average=data.map(lambda x: x[0]).sum()/data.count()
        out=data.map(lambda x: (x-average))
        return out

mapSTD=udf(normalize,IntegerType())     
dats = data.withColumn('Normalized', mapSTD('Fare'))

2 个答案:

答案 0 :(得分:3)

在您的示例中,UDF函数存在问题,无法应用于行和整个DataFrame。 UDF只能应用于单行,但Spark也可以实现在整个DataFrame上工作的UDAF(用户自定义聚合函数)。

要解决您的问题,您可以使用以下功能:

from pyspark.sql.functions import mean

def normalize(df, column):
    average = df.agg(mean(df[column]).alias("mean")).collect()[0]["mean"]
    return df.select(df[column] - average)

像这样使用:

normalize(df, "Fare")

请注意,上面仅适用于单列,但可以实现更通用的内容:

def normalize(df, columns):
    selectExpr = []
    for column in columns:
       average = df.agg(mean(df[column]).alias("mean")).collect()[0]["mean"]
       selectExpr.append(df[column] - average)
    return df.select(selectExpr)

使用它像:

normalize(df, ["col1", "col2"])

这样可行,但是您需要为每个列运行聚合,因此对于许多列,可能会出现性能问题,但是只能生成一个聚合表达式:

def normalize(df, columns):
    aggExpr = []
    for column in columns:
        aggExpr.append(mean(df[column]).alias(column))
    averages = df.agg(*aggExpr).collect()[0]
    selectExpr = []
    for column in columns:
        selectExpr.append(df[column] - averages[column])
    return df.select(selectExpr)

答案 1 :(得分:2)

添加到Piotr的答案。如果您需要保留现有数据框并添加带有别名的规范化列,则可以将该函数修改为:

def normalize(df, columns):
    aggExpr = []
    for column in columns:
        aggExpr.append(mean(df[column]).alias(column))
    averages = df.agg(*aggExpr).collect()[0]
    selectExpr = ['*']
    for column in columns:
        selectExpr.append((df[column] - averages[column]).alias('normalized_'+column))
    return df.select(selectExpr)