我有一个数据框(在Pyspark中),其中有一个行值作为字典:
df.show()
它看起来像:
+----+---+-----------------------------+
|name|age|info |
+----+---+-----------------------------+
|rob |26 |{color: red, car: volkswagen}|
|evan|25 |{color: blue, car: mazda} |
+----+---+-----------------------------+
根据评论提供更多信息:
df.printSchema()
类型是字符串
root
|-- name: string (nullable = true)
|-- age: string (nullable = true)
|-- dict: string (nullable = true)
是否可以从字典(颜色和汽车)中获取键并在数据框中将它们设置为列,并将值作为那些列的行?
预期结果:
+----+---+-----------------------------+
|name|age|color |car |
+----+---+-----------------------------+
|rob |26 |red |volkswagen |
|evan|25 |blue |mazda |
+----+---+-----------------------------+
我不知道我必须使用df.withColumn()并以某种方式遍历字典以选择每个字典,然后在其中创建一列吗?到目前为止,我一直试图找到一些答案,但是大多数答案是使用Pandas而不是Spark,因此我不确定是否可以应用相同的逻辑。
答案 0 :(得分:1)
您的字符串:
"{color: red, car: volkswagen}"
"{color: blue, car: mazda}"
不是python友好格式。无法使用json.loads
解析它们,也不能使用ast.literal_eval
对其求值。
但是,如果您提前知道键并可以假设字符串始终采用这种格式,则应该可以使用pyspark.sql.functions.regexp_extract
:
例如:
from pyspark.sql.functions import regexp_extract
df.withColumn("color", regexp_extract("info", "(?<=color: )\w+(?=(,|}))", 0))\
.withColumn("car", regexp_extract("info", "(?<=car: )\w+(?=(,|}))", 0))\
.show(truncate=False)
#+----+---+-----------------------------+-----+----------+
#|name|age|info |color|car |
#+----+---+-----------------------------+-----+----------+
#|rob |26 |{color: red, car: volkswagen}|red |volkswagen|
#|evan|25 |{color: blue, car: mazda} |blue |mazda |
#+----+---+-----------------------------+-----+----------+
模式是:
(?<=color: )
:文字字符串"color: "
的正向查找\w+
:一个或多个单词字符(?=(,|}))
:表示正面的逗号或大括号。这里是如何针对两个以上的键进行泛化,并处理键在字符串中不存在的情况。
from pyspark.sql.functions import regexp_extract, when, col
from functools import reduce
keys = ["color", "car", "year"]
pat = "(?<=%s: )\w+(?=(,|}))"
df = reduce(
lambda df, c: df.withColumn(
c,
when(
col("info").rlike(pat%c),
regexp_extract("info", pat%c, 0)
)
),
keys,
df
)
df.drop("info").show(truncate=False)
#+----+---+-----+----------+----+
#|name|age|color|car |year|
#+----+---+-----+----------+----+
#|rob |26 |red |volkswagen|null|
#|evan|25 |blue |mazda |null|
#+----+---+-----+----------+----+
在这种情况下,在尝试提取匹配项之前,我们使用pyspark.sql.functions.when
和pyspark.sql.Column.rlike
来测试字符串是否包含模式。
如果您不提前知道密钥,则必须编写自己的解析器或尝试修改上游数据。
答案 1 :(得分:0)
火花data_frame
colum_name
是info,下面是输入字符串,它是info列的值:
input_value is :-"[{Charge_Power:2.3, EVSE_PhaseAmp:10, charging_id:230V10A1X}, {Charge_Power:3.7, EVSE_PhaseAmp:16, charging_id:230V16A1X}]"
预期输出:
#+------------+-------------+-----------+
#|Charge_Power|EVSE_PhaseAmp|charging_id|
#+------------+-------------+-----------+
#|2.3 |10 |230V10A1X |
#|3.7 |16 |230V16A1X |
#+------------+-------------+-----------+
答案 2 :(得分:0)
通过printSchema函数可以看到,字典被Spark理解为字符串。分割字符串并创建新列的函数为split(),因此可以简单地解决此问题。
创建具有以下功能的UDF
:应用拆分并根据字典的新格式创建两个新列
代码:
@udf()
def transform_dict(dict_str):
str_of_dict_values = dict_str.\
replace("}", "").\
replace("{", ""). \
replace("color:", ""). \
replace(" car: ", ""). \
strip()
# output example: 'red,volkswagen'
return str_of_dict_values
# Create new column with our UDF with the dict values converted to str
df = df.withColumn('info_clean', clean("info"))
# Split these values and store in a tmp variable
split_col = split(df['info_clean'], ',')
# Create new columns with the split values
df = df.withColumn('color', split_col.getItem(0))
df = df.withColumn('car', split_col.getItem(1))
仅当我们假设字典元素总是按相同顺序排列并且键是固定的时,此解决方案才是正确的。 对于其他更复杂的情况,我们可以在UDF函数中创建一个字典,并通过显式调用每个字典键来形成值列表的字符串,这样我们就可以确保输出链中的顺序得以保持。