我有一张带有客户购买产品类别的表。 我正在尝试建立一个交叉销售矩阵,计算出每对产品类别中的唯一客户,并同时得出一个具有唯一计数的总数。
pandas.crosstab
是很好的开始方式,但在小计(即margins=True
)上却失败了
df = pd.DataFrame({
'cust': ['1', '1', '2', '3', '3', '4', '5'], # customer ID
'categ': ['a', 'b', 'a', 'a', 'b', 'b', 'b'] # category ID
})
# have 2 columns to make the crosstab
dd = pd.merge(df, df, on='cust')
然后pd.crosstab(dd.categ_x, dd.categ_y, margins=True)
给出:
| categ_x | a | b | All |
|---------|---|---|-----|
| a | 3 | 2 | 5 |
| b | 2 | 4 | 6 |
| All | 5 | 6 | 11 |
pd.merge
有助于在交叉表中用正确的数字填充单元格,但会导致小计/边距计算错误
我期望的是:
| categ_x | a | b | All |
|---------|---|---|-----|
| a | 3 | 2 | 3 | -- I have 3 unique clients with 'a'
| b | 2 | 4 | 4 | -- I have 4 unique clients with 'b'
| All | 3 | 4 | 5 | -- I have 5 unique clients in total
我已经尝试了一些计数,独特性...到目前为止还没有成功。
编辑
jezrael的答案很好,但是我想知道他们是否是使用正确的crosstab
通过aggfunc
直接完成此操作的一种方法。
答案 0 :(得分:1)
您可以按groupby.nunique
计数值,并按join
和append
手动添加值:
s = df2.groupby(['categ'])['cust'].nunique().rename('All')
s1 = s.append(pd.Series({'All': df2['cust'].nunique()}, name='All'))
df = pd.crosstab(dd.categ_x, dd.categ_y).join(s).append(s1)
print (df)
a b All
categ_x
a 3 2 3
b 2 4 4
All 3 4 5
另一个类似的解决方案:
s = df2.groupby(['categ'])['cust'].nunique().rename('All')
df = pd.crosstab(dd.categ_x, dd.categ_y).join(s).append(s)
df.loc['All','All'] = df2['cust'].nunique()
df = df.astype(int)
print (df)
a b All
categ_x
a 3 2 3
b 2 4 4
All 3 4 5
答案 1 :(得分:0)
我认为这满足了我的需求
pd.crosstab(
dd.categ_x, dd.categ_y, margins=True,
values=dd.cust, aggfunc=pd.Series.nunique
)
给予:
| categ_x | a | b | All |
|---------|---|---|-----|
| a | 3 | 2 | 3 |
| b | 2 | 4 | 4 |
| All | 3 | 4 | 5 |
pd.Series.nunique
给出values
(此处为dd.cust
)的唯一值的长度/大小。