如何实现自定义Pyspark爆炸(针对结构体数组),四列合一爆炸?

时间:2020-10-05 21:57:50

标签: python-3.x apache-spark pyspark apache-spark-sql

我正在尝试在Pyspark中实现自定义爆炸。我有4列实际上是具有相同架构的结构数组(一列结构包含的字段比其他三个少)。

对于我DataFrame中的每一行,我有4列是结构数组。这些列是学生,teaching_assantants,老师,管理员。

学生,助教和老师是具有idstudent_levelname字段的结构数组。

例如,这是DataFrame中的示例行。

enter image description here

学生,教学助手和教师结构均具有相同的架构(“ id”,“ student_level”,“ name”),而管理员结构具有“ id”和“ name”字段,但缺少学生级别。 / p>

我想执行一个自定义爆炸,以便在每行中,每个学生,助教,教授和管理员都有一个条目以及原始列名,以防万一我必须按“人员类型”进行搜索。 因此,对于上一行的屏幕截图,输出为8行:

+-----------+---------------------+----+---------------+----------+
| School_id |        type         | id | student_level |   name   |
+-----------+---------------------+----+---------------+----------+
|      1999 | students            |  1 | 0             | Brian    |
|      1999 | students            |  9 | 2             | Max      |
|      1999 | teaching_assistants | 19 | 0             | Xander   |
|      1999 | teachers            | 21 | 0             | Charlene |
|      1999 | teachers            | 12 | 2             | Rob      |
|      1999 | administrators      | 23 | None          | Marsha   |
|      1999 | administrators      | 11 | None          | Ryan     |
|      1999 | administrators      | 14 | None          | Bob      |
+-----------+---------------------+----+---------------+----------+

对于管理员来说,student_level列将为空。问题是,如果我使用爆炸功能,最终所有这些项目都放在不同的列中。

是否可以在Pyspark中做到这一点?我曾经想过要弄清楚如何将4个数组列合并为1个数组,然后对该数组进行爆炸,尽管我不确定将结构体数组合并并获取字段名称作为字段是否可行(我已经尝试了各种方法),而且我也不清楚管理员是否缺少字段。

过去,我是通过转换为RDD并使用flatmap /自定义udf来完成此操作的,但对于数百万行而言效率很低。

1 个答案:

答案 0 :(得分:2)

想法是使用stack将列studentsteaching_assistantsteachersadministrators转换为单独的行,每个{ {1}}。之后,可以展开包含数据的列,然后将单个结构的元素转换为单独的列。

使用type要求堆叠的所有列都具有相同的类型。这意味着所有列必须包含相同结构的数组,并且该结构所有元素的可空性必须匹配。因此,stack列必须首先转换为正确的结构类型。

administrators

打印

df.withColumn("administrators", F.expr("transform(administrators, " +
        "a -> if(1<2,named_struct('id', a.id, 'name', a.name, 'student_level', "+
              "cast(null as long)),null))"))\
  .select("School_id", F.expr("stack(4, 'students', students, "+
          "'teaching_assistants', teaching_assistants, 'teachers', teachers, "+
          "'administrators', administrators) as (type, temp1)")) \
  .withColumn("temp2", F.explode("temp1"))\
  .select("School_id", "type", "temp2.id", "temp2.name", "temp2.student_level")\
  .show()

第一行看起来很奇怪的+---------+-------------------+---+--------+-------------+ |School_id| type| id| name|student_level| +---------+-------------------+---+--------+-------------+ | 1999| students| 1| Brian| 0| | 1999| students| 9| Max| 2| | 1999|teaching_assistants| 19| Xander| 0| | 1999| teachers| 21|Charlene| 0| | 1999| teachers| 12| Rob| 2| | 1999| administrators| 23| Marsha| null| | 1999| administrators| 11| Ryan| null| | 1999| administrators| 14| Bob| null| +---------+-------------------+---+--------+-------------+ 对于为if(1<2, named_struct(...), null)数组的元素设置正确的空位是必要的。

此解决方案适用于Spark 2.4+。如果可以在上一步中使用transform administrators结构,则此解决方案也适用于早期版本。