在 PySpark 中删除嵌套列

3
我有一个PySpark数据框,其中包含列“results”。我希望在结果列中删除“Attributes”列。
数据框的模式(有更多列,但为了方便起见,我没有显示它们):
 |-- results: struct (nullable = true)
 |    |-- l: array (nullable = true)
 |    |    |-- element: struct (containsNull = true)
 |    |    |    |-- m: struct (nullable = true)
 |    |    |    |    |-- Attributes: struct (nullable = true)
 |    |    |    |    |    |-- m: struct (nullable = true)
 |    |    |    |    |    |    |-- Score: struct (nullable = true)
 |    |    |    |    |    |    |    |-- n: string (nullable = true)
 |    |    |    |    |-- OtherInfo: struct (nullable = true)
 |    |    |    |    |    |-- l: array (nullable = true)
 |    |    |    |    |    |    |-- element: struct (containsNull = true)
 |    |    |    |    |    |    |    |-- m: struct (nullable = true)
 |    |    |    |    |    |    |    |    |-- Name: string (nullable = true)

如何在PySpark中不使用udf完成此操作?
数据的一行:
{
   "results" : {
        "l" : [
            {
              "m":{
                  "Attributes" : {
                      "m" : {
                           "Score" : {"n" : "85"}
                       }
                  },
                   "OtherInfo":{
                      "l" : [
                           {
                             "m" : {
                               "Name" : {"john"}
                             }
                          },
                          {
                             "m" : {
                                "Name" : "Cena"}
                          }
                       ]
                   }
             }
           }   
         ]
    }
}
2个回答

4
自从 Spark 3.1 版本起,可以使用dropFields,而不必重新创建大型结构的其余部分。
df = df.withColumn(
    "results",
    F.struct(F.transform(
        F.col("results.l"),
        lambda x: x.m.dropFields("Attributes")
    )).alias("l")
)

结果:

df.printSchema()
# root
#  |-- results: struct (nullable = false)
#  |    |-- l: array (nullable = false)
#  |    |    |-- element: struct (containsNull = false)
#  |    |    |    |-- OtherInfo: struct (nullable = false)
#  |    |    |    |    |-- l: array (nullable = false)
#  |    |    |    |    |    |-- element: struct (containsNull = false)
#  |    |    |    |    |    |    |-- m: struct (nullable = false)
#  |    |    |    |    |    |    |    |-- Name: string (nullable = true)

2
要从结构类型中删除字段,您需要创建一个新的结构,其中包含原始结构中要删除的所有元素,但不包括该元素。在这里,由于results下的l字段是一个数组,因此您可以使用transform函数(Spark 2.4+)来更新其所有结构元素,如下所示:
from pyspark.sql.functions import struct, expr


t_expr = "transform(results.l, x -> struct(struct(x.m.OtherInfo as OtherInfo) as m))"
df = df.withColumn("results", struct(expr(t_expr).alias("l")))

对于数组中的每个元素x,我们创建一个新的结构体,只保存x.m.OtherInfo字段。
df.printSchema()

#root
# |-- results: struct (nullable = false)
# |    |-- l: array (nullable = true)
# |    |    |-- element: struct (containsNull = false)
# |    |    |    |-- m: struct (nullable = false)
# |    |    |    |    |-- OtherInfo: struct (nullable = true)
# |    |    |    |    |    |-- l: array (nullable = true)
# |    |    |    |    |    |    |-- element: struct (containsNull = true)
# |    |    |    |    |    |    |    |-- m: struct (nullable = true)
# |    |    |    |    |    |    |    |    |-- Name: string (nullable = true)

好的解决方案!如果还有一个字段otherinfo2,如何在transform中传递它? - mightyMouse
1
只需将其添加到新结构体中:struct(x.m.OtherInfo as OtherInfo, x.m.OtherInfo2 as OtherInfo2)。如果您有许多字段并且不想硬编码它们,可以从模式中获取字段名称,并使用Python循环构建转换表达式字符串。 - blackbishop
如果现在某个项目没有其他信息(在某些情况下为null或不存在),那么这是否有效?转换后的框架中其他信息将如何填充? - mightyMouse
@kmkhan 是的,这也应该可以工作。那些字段将为空。 - blackbishop
ParseException: "\n不匹配的输入'as',期望{')',','} (第1行,位置70)\n\n== SQL ==\ntransform(results.l,x->f.struct(f.struct(x.m.OtherInfo作为OtherInfo)作为m))我得到了这个异常。 - mightyMouse

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接