Apache spark TypeError:列不可编辑-如何在ArrayType()上迭代?

Apache spark TypeError:列不可编辑-如何在ArrayType()上迭代?,apache-spark,pyspark,spark-dataframe,pyspark-sql,Apache Spark,Pyspark,Spark Dataframe,Pyspark Sql,考虑以下数据帧: +---+------------+ |键入|名称| +---+------------+ |person |[约翰、萨姆、简]| |宠物|[胡须,罗孚,菲多]| +---+------------+ 可以使用以下代码创建: 导入pyspark.sql.f函数 数据=[ “person”[“john”、“sam”、“jane”], “宠物”、“胡须”、“漫游者”、“菲多”] ] df=sqlCtx.createDataFramedata,[类型,名称] df.showtrunc

考虑以下数据帧:

+---+------------+ |键入|名称| +---+------------+ |person |[约翰、萨姆、简]| |宠物|[胡须,罗孚,菲多]| +---+------------+ 可以使用以下代码创建:

导入pyspark.sql.f函数 数据=[ “person”[“john”、“sam”、“jane”], “宠物”、“胡须”、“漫游者”、“菲多”] ] df=sqlCtx.createDataFramedata,[类型,名称] df.showtruncate=False 有没有一种方法可以直接修改ArrayType列名,即对每个元素应用一个函数,而不使用udf

例如,假设我想将函数foo应用于names列。我将使用foo是str.upper的示例只是为了说明,但我的问题是关于可以应用于iterable元素的任何有效函数

foo=lambda x:x.upper将其定义为str.upper作为示例 df.withColumn'X',[foox代表f.colnames中的X].show TypeError:列不可编辑

我可以使用udf来实现这一点:

foo_udf=f.udflambda行:[foox代表行中的x],ArrayTypeStringType df.withColumn'names',foo_udff.col'names'。showtruncate=False +---+------------+ |键入|名称| +---+------------+ |person |[约翰、萨姆、简]| |宠物|[胡须,罗孚,菲多]| +---+------------+ 在这个特定示例中,我可以通过分解列、调用pyspark.sql.functions.upper,然后调用groupBy和collect_list来避免udf:

df.选择'type',f.分解'names'。别名'name'\ .withColumn'name',f.upperf.col'name'\ .groupBy'type'\ .aggf.collect_列表'name'。别名'name'\ .showtruncate=False +---+------------+ |键入|名称| +---+------------+ |person |[约翰、萨姆、简]| |宠物|[胡须,罗孚,菲多]| +---+------------+
但是这是很多代码来做一些简单的事情。是否有更直接的方法使用spark数据帧函数迭代ArrayType的元素?

是的,您可以通过将其转换为RDD,然后再转换回DF来实现

>>> df.show(truncate=False)
+------+-----------------------+
|type  |names                  |
+------+-----------------------+
|person|[john, sam, jane]      |
|pet   |[whiskers, rover, fido]|
+------+-----------------------+

>>> df.rdd.mapValues(lambda x: [y.upper() for y in x]).toDF(["type","names"]).show(truncate=False)
+------+-----------------------+
|type  |names                  |
+------+-----------------------+
|person|[JOHN, SAM, JANE]      |
|pet   |[WHISKERS, ROVER, FIDO]|
+------+-----------------------+
在Spark<2.4中,您可以使用用户定义的功能:

从pyspark.sql.functions导入udf 从pyspark.sql.types导入ArrayType、DataType、StringType def transformf,t=StringType: 如果不是isinstancet,则数据类型: raise TYPEERROR无效类型{}.formattypet @乌德法雷特 def_xs: 如果xs不是None: 返回[x中x的外汇] 返回_ foo_udf=transformstr.upper df.withColumn'names',foo_udff.col'names'。showtruncate=False +---+------------+ |键入|名称| +---+------------+ |person |[约翰、萨姆、简]| |宠物|[胡须,罗孚,菲多]| +---+------------+ 考虑到+惯用语的高成本,这种方法几乎是唯一的首选方法,尽管它具有内在成本

在Spark 2.4或更高版本中,您可以将*用于查看:

从pyspark.sql.functions导入expr df.withColumn “名称”,expr'transformnames,x->upperx' .showtruncate=False +---+------------+ |键入|名称| +---+------------+ |person |[约翰、萨姆、简]| |宠物|[胡须,罗孚,菲多]| +---+------------+ 也可以使用pandas_udf

从pyspark.sql.functions导入pandasuudf,PandasUDFType def transform_pandasf,t=StringType: 如果不是isinstancet,则数据类型: raise TYPEERROR无效类型{}.formattypet @pandas_udfArrayTypet,PandasUDFType.SCALAR def_xs: 如果xs不是其他xs,则返回xs.applylambda xs:[fx for x in xs] 返回_ foo_udf_pandas=transform_pandasstr.upper df.withColumn'names',foo_udff.col'names'。showtruncate=False +---+------------+ |键入|名称| +---+------------+ |person |[约翰、萨姆、简]| |宠物|[胡须,罗孚,菲多]| +---+------------+ 尽管只有最新的Arrow/PySpark组合支持处理ArrayType列。尽管如此,此选项应该比标准UDF更有效,尤其是在支持任意Python函数的同时,serde开销更低

*,包括但不限于和。例如,见

. .
谢谢你的回复。我也知道这种方法,但我一直在寻找只使用spark数据帧语法的方法。您知道到rdd和返回的序列化与使用udf相比有什么不同吗?我的理解是,使用udf是首选,但我没有文档来支持它。据我所知,一旦数据进入Python,Spark就无法管理worker的内存。JVM和Python都在一台机器上竞争内存,创建资源约束,这可能会导致工作程序失败。@pault感谢您的编辑。可能是一个更好的目标,虽然没有按类别分组-你认为呢?