Apache spark TypeError:列不可编辑-如何在ArrayType()上迭代?
考虑以下数据帧: +---+------------+ |键入|名称| +---+------------+ |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 |[约翰、萨姆、简]| |宠物|[胡须,罗孚,菲多]| +---+------------+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
但是这是很多代码来做一些简单的事情。是否有更直接的方法使用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感谢您的编辑。可能是一个更好的目标,虽然没有按类别分组-你认为呢?