我是PySpark的新手。
我有一个Spark DataFrame
df
,其列有'device_type'。
我想将“Tablet”或“Phone”中的所有值替换为“Phone”,并将“PC”替换为“Desktop”。
在Python中,我可以执行以下操作,
deviceDict = {'Tablet':'Mobile','Phone':'Mobile','PC':'Desktop'}
df['device_type'] = df['device_type'].replace(deviceDict,inplace=False)
如何使用PySpark实现这一目标?谢谢!
答案 0 :(得分:4)
经过大量搜索和替代,我认为使用 python dict 替换的最简单方法是使用 pyspark 数据框方法 replace
:
deviceDict = {'Tablet':'Mobile','Phone':'Mobile','PC':'Desktop'}
df_replace = df.replace(deviceDict,subset=['device_type'])
这将用 dict 替换所有值,如果您将 dict 参数与子集参数相结合,您可以使用 df.na.replace()
获得相同的结果。他的 docs 不够清楚,因为如果你搜索函数 replace
你会得到两个引用,一个在 pyspark.sql.DataFrame.replace
里面,另一个在 pyspark.sql.DataFrameNaFunctions.replace
里面,但是两个参考的示例代码都使用 df.na.replace
,因此不清楚您是否可以实际使用 df.replace
。
答案 1 :(得分:2)
最简单的方法是在数据框上应用udf
:
from pyspark.sql.functions import col , udf
deviceDict = {'Tablet':'Mobile','Phone':'Mobile','PC':'Desktop'}
map_func = udf(lambda row : deviceDict.get(row,row))
df = df.withColumn("device_type", map_func(col("device_type")))
答案 2 :(得分:1)
您也可以使用df.withColumn
进行此操作:
from itertools import chain
from pyspark.sql.functions import create_map, lit
deviceDict = {'Tablet':'Mobile','Phone':'Mobile','PC':'Desktop'}
mapping_expr = create_map([lit(x) for x in chain(*deviceDict.items())])
df = df.withColumn('device_type', mapping_expr[df['dvice_type']])
df.show()
答案 3 :(得分:1)
这是一个受R recode
函数启发的辅助函数,它抽象了先前的答案。作为奖励,它添加了默认值选项。
from itertools import chain
from pyspark.sql.functions import col, create_map, lit, when, isnull
from pyspark.sql.column import Column
df = spark.createDataFrame([
('Tablet', ), ('Phone', ), ('PC', ), ('Other', ), (None, )
], ["device_type"])
deviceDict = {'Tablet':'Mobile','Phone':'Mobile','PC':'Desktop'}
df.show()
+-----------+
|device_type|
+-----------+
| Tablet|
| Phone|
| PC|
| Other|
| null|
+-----------+
这是recode
的定义。
def recode(col_name, map_dict, default=None):
if not isinstance(col, Column):
col_name = col(col_name)
mapping_expr = create_map([lit(x) for x in chain(*map_dict.items())])
if default is None:
return mapping_expr.getItem(col_name)
else:
return when(~isnull(mapping_expr.getItem(col_name)), mapping_expr.getItem(col_name)).otherwise(default)
创建没有默认值的列会为null
/ None
提供所有不匹配的值。
df.withColumn("device_type", recode('device_type', deviceDict)).show()
+-----------+
|device_type|
+-----------+
| Mobile|
| Mobile|
| Desktop|
| null|
| null|
+-----------+
另一方面,为default
指定一个值将使用此默认值替换所有不匹配的值。
df.withColumn("device_type", recode('device_type', deviceDict, default='Other')).show()
+-----------+
|device_type|
+-----------+
| Mobile|
| Mobile|
| Desktop|
| Other|
| Other|
+-----------+
答案 4 :(得分:0)
您可以使用na.replace
:
df = spark.createDataFrame([
('Tablet', ), ('Phone', ), ('PC', ), ('Other', ), (None, )
], ["device_type"])
df.na.replace(deviceDict, 1).show()
+-----------+
|device_type|
+-----------+
| Mobile|
| Mobile|
| Desktop|
| Other|
| null|
+-----------+
或map literal:
from itertools import chain
from pyspark.sql.functions import create_map, lit
mapping = create_map([lit(x) for x in chain(*deviceDict.items())])
df.select(mapping[df['device_type']].alias('device_type'))
+-----------+
|device_type|
+-----------+
| Mobile|
| Mobile|
| Desktop|
| null|
| null|
+-----------+
请注意,后一种解决方案会将映射中不存在的值转换为NULL
。如果这不是您想要的行为,您可以添加coalesce
:
from pyspark.sql.functions import coalesce
df.select(
coalesce(mapping[df['device_type']], df['device_type']).alias('device_type')
)
+-----------+
|device_type|
+-----------+
| Mobile|
| Mobile|
| Desktop|
| Other|
| null|
+-----------+