Pyspark Crosstab Pivot 挑战/问题

Pyspark Crosstab Pivot Challenge / Problem

不幸的是,我找不到解决我的确切问题的方法。它与 pivot 和 crosstab 有关,但我无法用这些功能解决它。 我觉得我错过了一个中间人-table,但我不知怎么想不出一个解决方案。

问题描述:

A table 客户指出他们从哪个类别购买了产品。如果客户从该类别购买产品,类别 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|
+--------+----------+

table 是 DISTINCT,意思是客户和类别只有一种组合

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

想要的结果table:

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

阅读范例: row1 column1 : 购买产品 1 (A, B, C) 的客户总数 row1 column2 : 购买产品 1 和 2 (A) 的客户数量 row1 column3 : 购买产品 1 和 3 (A, C) 的客户数量 等等 如您所见,table 由其对角线镜像。

有什么关于如何创建所需 table 的建议吗?

额外的挑战: 如何得到%的结果? 对于第一行,结果将是: | 100% | 33% | 66% | 66% |

非常感谢!

您可以使用 customer 作为连接条件将输入数据与其自身连接起来。 returns 给定客户存在的所有类别组合。之后你可以使用 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|
+---------+----+----+----+----+