Pyspark Crosstab数据透视挑战/问题

问题描述

很不幸,我找不到确切的解决方案。它与数据透视表和交叉表有关,但是我无法使用这些功能解决它。 我感觉自己缺少一个中间表,但是我不知何故无法提出解决方案。

问题描述:

一个带有客户的表,指示他们从哪个类别购买了产品。如果客户从类别中购买了产品,则类别ID将显示在其姓名旁边。

有4个类别1-4和3个客户A,B,C

+--------+----------+
|customer| category |
+--------+----------+
|       A|         1|
|       A|         2|
|       A|         3|
|       B|         1|
|       B|         4|
|       C|         1|
|       C|         3|
|       C|         4|
+--------+----------+

表为disTINCT,表示客户和类别只有一种组合

我想要的是按类别的交叉表,在这里我可以轻松阅读例如从类别1购买的人中有多少人也从类别4购买?

所需结果表:

+--------+---+---+---+---+
|        | 1 | 2 | 3 | 4 |
+--------+---+---+---+---+
|       1|  3|  1|  2|  2|
|       2|  1|  1|  1|  0|
|       3|  2|  1|  2|  1|
|       4|  2|  0|  1|  1|
+--------+---+---+---+---+

阅读示例: 第1列:购买产品1(A,B,C)的客户总数 第1列第2列:购买产品1和2的客户数量(A) 第3列:购买产品1和3(A,C)的客户数量 等等 如您所见,表格通过其对角线镜像。

有人建议如何创建所需的表吗?

其他挑战: 如何获得%的结果? 对于第一行,结果将是: 100%| 33%| 66%| 66%|

非常感谢!

解决方法

您可以使用customer作为连接标准将输入数据与其自身连接。这将返回给定客户存在的所有类别组合。之后,您可以使用crosstab获得结果。

df2 = df.withColumnRenamed("category","cat1").join(df.withColumnRenamed("category","cat2"),"customer") \
  .crosstab("cat1","cat2") \
  .orderBy("cat1_cat2") 
df2.show()

输出:

+---------+---+---+---+---+
|cat1_cat2|  1|  2|  3|  4|
+---------+---+---+---+---+
|        1|  3|  1|  2|  2|
|        2|  1|  1|  1|  0|
|        3|  2|  1|  2|  1|
|        4|  2|  0|  1|  2|
+---------+---+---+---+---+

要获得相对频率,可以对每一行求和,然后将每个元素除以该总和。

df2.withColumn("sum",sum(df2[col] for col in df2.columns if col != "cat1_cat2")) \
  .select("cat1_cat2",*(F.round(df2[col]/F.col("sum"),2).alias(col) for col in df2.columns if col != "cat1_cat2")) \
  .show()

输出:

+---------+----+----+----+----+
|cat1_cat2|   1|   2|   3|   4|
+---------+----+----+----+----+
|        1|0.38|0.13|0.25|0.25|
|        2|0.33|0.33|0.33| 0.0|
|        3|0.33|0.17|0.33|0.17|
|        4| 0.4| 0.0| 0.2| 0.4|
+---------+----+----+----+----+