在Spark中更新数据框列


72

查看新的spark数据框api,尚不清楚是否可以修改数据框列。

我怎么会去改变行的值xy一个数据帧的?

pandas这将是df.ix[x,y] = new_value

编辑:合并以下内容,您将无法修改现有数据框,因为它是不可变的,但是您可以返回具有所需修改的新数据框。

如果您只想根据条件替换列中的值,例如np.where

from pyspark.sql import functions as F

update_func = (F.when(F.col('update_col') == replace_val, new_value)
                .otherwise(F.col('update_col')))
df = df.withColumn('new_column_name', update_func)

如果要对列执行某些操作并创建一个添加到数据框的新列:

import pyspark.sql.functions as F
import pyspark.sql.types as T

def my_func(col):
    do stuff to column here
    return transformed_value

# if we assume that my_func returns a string
my_udf = F.UserDefinedFunction(my_func, T.StringType())

df = df.withColumn('new_column_name', my_udf('update_col'))

如果希望新列的名称与旧列的名称相同,则可以添加其他步骤:

df = df.drop('update_col').withColumnRenamed('new_column_name', 'update_col')

如果要按索引访问DataFrame,则需要先建立一个索引。参见例如stackoverflow.com/questions/26828815/…。或使用自己的索引添加索引列。
fanfabbb

Answers:


70

虽然您不能这样修改列,但是您可以对列进行操作并返回反映该更改的新DataFrame。为此,您首先要创建一个UserDefinedFunction实施操作以应用,然后有选择地将该功能仅应用到目标列。在Python中:

from pyspark.sql.functions import UserDefinedFunction
from pyspark.sql.types import StringType

name = 'target_column'
udf = UserDefinedFunction(lambda x: 'new_value', StringType())
new_df = old_df.select(*[udf(column).alias(name) if column == name else column for column in old_df.columns])

new_df现在具有相同的模式old_df(假设old_df.target_column是类型StringType以及),但在列中的所有值target_column将是new_value


1
这是问题的实际答案,谢谢!但是,火花工作对我来说还没有完成,所有执行者都败了。您能想到另一种方法吗?我将它与更复杂的UDF结合使用,在那里我可以转换为字符串。有没有类似new_df = old_df.col1.apply(lambda x:func(x))的类似熊猫的语法?
fanfabbb

24
还有:new_df = old_df.withColumn('target_column', udf(df.name))
fanfabbb,2015年

2
是的,应该可以。请记住,UDF只能将列作为参数。如果要将其他数据传递到函数中,则必须先部分应用它。
卡尔森,2015年

1
@KatyaHandler如果只想复制一列,一种方法是简单地选择两次:df.select([df[col], df[col].alias('same_column')]),其中col要复制的列的名称是。在最新的Spark版本中,我使用过UDF的很多东西都可以通过pyspark.sql.functions。在Pyspark UDF表现真的很差,因此这可能真的是值得探讨:spark.apache.org/docs/latest/api/python/...
卡尔森

1
它是StringType 不是Stringtypeudf = UserDefinedFunction(lambda x: 'new_value', Stringtype())
Namit Juneja

48

通常,在更新列时,我们希望将旧值映射到新值。这是在没有UDF的情况下在pyspark中执行此操作的方法:

# update df[update_col], mapping old_value --> new_value
from pyspark.sql import functions as F
df = df.withColumn(update_col,
    F.when(df[update_col]==old_value,new_value).
    otherwise(df[update_col])).

当我的update_col是列表Ex- =:时如何使用update_cols=['col1','col2','col3']
GeekFactory '17

使用for循环。
保罗

13

DataFrames基于RDD。RDD是不可变的结构,不允许在现场更新元素。要更改值,您将需要使用类似于SQL的DSL或RDD操作(如)来转换原始数据框,从而创建一个新的DataFrame map

强烈推荐的幻灯片:在Spark中引入DataFrame以进行大规模数据科学


3
那么,数据表抽象究竟添加了什么,而这又不能在与表相同的行数中完成呢?
路加福音

“ DataFrames引入了新的简化运算符,用于对大型数据集进行过滤,聚合和投影。在内部,DataFrames利用Spark SQL逻辑优化器智能地规划了物理操作的物理执行情况,以在大型数据集上正常工作” -databricks.com/blog/2015/ 03/13
announcing

11

正如maasg所说,您可以根据应用于旧DataFrame的映射结果创建一个新的DataFrame。给定DataFramedf具有两行的示例:

val newDf = sqlContext.createDataFrame(df.map(row => 
  Row(row.getInt(0) + SOMETHING, applySomeDef(row.getAs[Double]("y")), df.schema)

请注意,如果列的类型发生变化,则需要为其提供正确的架构,而不是df.schema。查看api的org.apache.spark.sql.Row可用方法:https : //spark.apache.org/docs/latest/api/java/org/apache/spark/sql/Row.html

[更新]或在Scala中使用UDF:

import org.apache.spark.sql.functions._

val toLong = udf[Long, String] (_.toLong)

val modifiedDf = df.withColumn("modifiedColumnName", toLong(df("columnName"))).drop("columnName")

如果列名需要保持不变,则可以将其重命名:

modifiedDf.withColumnRenamed("modifiedColumnName", "columnName")

2

pyspark.sql.functions导入col并将基于字符串(字符串a,字符串b,字符串c)的第五列更新为integer(0,1,2)到新的DataFrame中。

from pyspark.sql.functions import col, when 

data_frame_temp = data_frame.withColumn("col_5",when(col("col_5") == "string a", 0).when(col("col_5") == "string b", 1).otherwise(2))
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.