【问题标题】:Extract specific struct field of a decoded column in Spark Dataframe在 Spark Dataframe 中提取解码列的特定结构字段
【发布时间】:2020-05-12 18:05:33
【问题描述】:

我在包含 JSON 数据的 spark 数据框中有一个解码列。例如,

第 1 行:{'id': 1, 'name': 'Slatan', 'surname': 'Yav', 'age': 24}
第 2 行:{'id': 2, 'name': 'Nikos', 'surname': 'Stef', 'age': 34}
第 3 行:{'id': 3, 'name': 'Panos', 'surname': 'Rodes', 'age': 44}

我只想提取“姓名”、“姓氏”、“年龄”并将它们添加到 Spark 数据框中。

我的方法

schema = StructType(
    [
      StructField('name', StringType(), True),
      StructField('surname', StringType(), True),
      StructField('age', IntegerType(), True)
    ]
)

final_df=None
final_df = spark.createDataFrame([], schema)
columns_selected=schema.fieldNames()

final_df=final_df.union(avro_decoded.withColumn("Body_decoded", from_json("Body_decoded", schema)).select(col( 'Body_decoded.*columns_selected')))

但我得到了错误。如何仅使用名称、姓氏和排除 id 值字段填充 final_df(spark 数据框)?

错误

'No such struct field *columns_selected in name, surname;'

解码前的正文列

avro_decoded=read_avro_data.withColumn('Body_decoded', decode(read_avro_data.Body, charset="UTF-8")).select("Body_decoded")

avro_decoded.printSchema()

返回:

root
 |-- Body_decoded: string (nullable = true)

比你提前任何建议。

【问题讨论】:

  • 错误是什么?
  • 'No such struct field *columns_selected in name, surname;'
  • 尝试打印你的dataFrame,看看里面有什么。有时它在读取 dataFrame 时将第一个字母大写
  • 当我打印 Body_decoded 列时,我得到了一个 spark 系列,其中的行类似于我的问题开头所写的值。 (json 行)...Body_decoded 是 avro_decoded spark df 的一列
  • 你能把代码贴在这里吗?我不知道您是如何尝试访问它的。

标签: json apache-spark pyspark


【解决方案1】:

您在尝试使用 from_json 时采用了正确的方法,但是,您的 json 格式不适合 spark, 因为您有一个 @987654322 @ 因此,只需使用 regexp_replace 将整数用引号括起来,然后使用 from_jsonselect 您的所需的列,如下所示。

df.show() #sample dataframe

#+---------------------------------------------------------+
#|Body_decoded                                             |
#+---------------------------------------------------------+
#|{'id': 1, 'name': 'Slatan', 'surname': 'Yav', 'age': 24} |
#|{'id': 2, 'name': 'Nikos', 'surname': 'Stef', 'age': 34} |
#|{'id': 3, 'name': 'Panos', 'surname': 'Rodes', 'age': 44}|
#+---------------------------------------------------------+
#root
 #|-- Body_decoded: string (nullable = true) #schema


from pyspark.sql.types import *
from pyspark.sql import functions as F

schema=MapType(StringType(),StringType())
columns_selected=['name','surname']

df.withColumn("Body_decoded", F.from_json(F.regexp_replace("Body_decoded",'[^"\d,]?(\d+)',"'$1'")\
                                              ,schema))\
  .select(*["Body_decoded.{}".format(x) for x in columns_selected]).show()

#+------+-------+
#|name  |surname|
#+------+-------+
#|Slatan|Yav    |
#|Nikos |Stef   |
#|Panos |Rodes  |
#+------+-------+

【讨论】:

  • 好的,我明白你的意思,但是如果我有不同类型的数据类型(StringType()、IntegerType())怎么办?为什么你使用 MapType 而不是 StructField?
  • Also MapType() 给我错误:TypeError: __init__() takes from 3 to 4 positional arguments but 15 were given ....我给 15 因为我有 14 列要迭代,而不仅仅是我提出的示例中的 3。我的数据很敏感,这就是我提供虚拟数据的原因。在我的示例中,我有多种数据类型。不仅是 StringType 数据
  • MapType(StringType(),StringType()) 表示所有列的键是字符串,值是字符串,不需要给15个位置参数,2就足够了,只要每个键和值都用单/双包装引用 from_json 将起作用。你可以使用structfield,但是当你可以告诉maptype所有键和值都是字符串时,逐行编写所有这些有什么意义。一旦你的列对齐,你就可以应用 .cast 来转换成你喜欢的任何格式
  • 是的,你说得有道理。虽然这个*["Body_decoded.{}".format(x) for x in columns_selected]救了我
  • 正则表达式不是我的强项,但代码基本上会找到整个字符串中存在数字的任何位置,并将其用单引号括起来,因此1232 213 my 将转换为'1232' '213' my
猜你喜欢
  • 2017-10-10
  • 1970-01-01
  • 2020-10-28
  • 1970-01-01
  • 1970-01-01
  • 2019-12-08
  • 2020-11-20
  • 2019-01-12
  • 1970-01-01
相关资源
最近更新 更多