问题描述
很不幸,我找不到确切的解决方案。它与数据透视表和交叉表有关,但是我无法使用这些功能解决它。 我感觉自己缺少一个中间表,但是我不知何故无法提出解决方案。
问题描述:
一个带有客户的表,指示他们从哪个类别购买了产品。如果客户从类别中购买了产品,则类别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|
+---------+----+----+----+----+