Groupby 并计算具有多个值的列
Groupby and count columns with multiple values
鉴于此数据框:
df = pd.DataFrame({
"names": [["Kevin, Jack"], ["Antoine, Mary, Johanne, Iv"], ["Ali"]],
"commented": [["Kevin, Antoine, Iv"], ["Antoine, Mary, Ali"], ["Mary, Jack"]],
}, index=["1", "2", "3"])
看起来像这样:
names commented
1 [Kevin, Jack] [Kevin, Antoine, Iv]
2 [Antoine, Mary, Johanne, Iv] [Antoine, Mary, Ali]
3 [Ali] [Mary, Jack]
我想要一个新的数据框来计算所有人发表的所有评论。类似于:
Kevin
Jack
Antoine
Mary
Johanne
Iv
Ali
Kevin
1
0
1
0
0
1
0
Jack
1
0
1
0
0
1
0
Antoine
0
0
1
1
0
0
1
Mary
0
0
1
1
0
0
1
Johanne
0
0
1
1
0
0
1
Iv
0
0
1
1
0
0
1
Ali
0
1
0
1
0
0
0
这个数据框可能太小而没有意义,但我的原始数据框是 100k 行,并且会有大于 0 和 1 的数字。
我已经使用 pivot_table 和分组依据的几种变体查看了各种选项,但我似乎无法弄清楚。
df.pivot_table(index = 'names', columns= 'commented', aggfunc= 'count')
df.groupby('names').commented.apply(list).reset_index()
df.explode('names')['commented'].value_counts()
df.set_index('names').apply(pd.Series.explode).reset_index()
我尝试过的几乎所有解决方案都给我错误:TypeError: unhashable type: 'list'
在您的示例输入中,names
和 commented
列中的每个元素都是一个只有 1 个元素(字符串)的数组。不确定您的真实数据是否属于这种情况。
您可以用逗号分隔每个字符串,然后展开并旋转数据框:
split = lambda x: x[0].split(", ")
(
df.assign(
names=df["names"].apply(split),
commented=df["commented"].apply(split),
dummy=1
)
.explode("names")
.explode("commented")
.pivot_table(index="names", columns="commented", values="dummy", aggfunc="count", fill_value=0)
)
您可以尝试将字符串列表分解为行,然后使用 pandas.crosstab
df = (df.explode(df.columns.tolist())
.apply(lambda col: col.str.split(', '))
.explode('names')
.explode('commented'))
out = pd.crosstab(df['names'], df['commented'])
print(df)
names commented
1 Kevin Kevin
1 Kevin Antoine
1 Kevin Iv
1 Jack Kevin
1 Jack Antoine
1 Jack Iv
2 Antoine Antoine
2 Antoine Mary
2 Antoine Ali
2 Mary Antoine
2 Mary Mary
2 Mary Ali
2 Johanne Antoine
2 Johanne Mary
2 Johanne Ali
2 Iv Antoine
2 Iv Mary
2 Iv Ali
3 Ali Mary
3 Ali Jack
print(out)
commented Ali Antoine Iv Jack Kevin Mary
names
Ali 0 0 0 1 0 1
Antoine 1 1 0 0 0 1
Iv 1 1 0 0 0 1
Jack 0 1 1 0 1 0
Johanne 1 1 0 0 0 1
Kevin 0 1 1 0 1 0
Mary 1 1 0 0 0 1
这是使用 str.get_dummies()
的另一种方法
(df.assign(names = df['names'].str[0].str.split(', '))
.explode('names')
.set_index('names')
.squeeze()
.str[0]
.str.get_dummies(sep=', '))
输出:
Ali Antoine Iv Jack Kevin Mary
names
Kevin 0 1 1 0 1 0
Jack 0 1 1 0 1 0
Antoine 1 1 0 0 0 1
Mary 1 1 0 0 0 1
Johanne 1 1 0 0 0 1
Iv 1 1 0 0 0 1
Ali 0 0 0 1 0 1
鉴于此数据框:
df = pd.DataFrame({
"names": [["Kevin, Jack"], ["Antoine, Mary, Johanne, Iv"], ["Ali"]],
"commented": [["Kevin, Antoine, Iv"], ["Antoine, Mary, Ali"], ["Mary, Jack"]],
}, index=["1", "2", "3"])
看起来像这样:
names commented
1 [Kevin, Jack] [Kevin, Antoine, Iv]
2 [Antoine, Mary, Johanne, Iv] [Antoine, Mary, Ali]
3 [Ali] [Mary, Jack]
我想要一个新的数据框来计算所有人发表的所有评论。类似于:
Kevin | Jack | Antoine | Mary | Johanne | Iv | Ali | |
---|---|---|---|---|---|---|---|
Kevin | 1 | 0 | 1 | 0 | 0 | 1 | 0 |
Jack | 1 | 0 | 1 | 0 | 0 | 1 | 0 |
Antoine | 0 | 0 | 1 | 1 | 0 | 0 | 1 |
Mary | 0 | 0 | 1 | 1 | 0 | 0 | 1 |
Johanne | 0 | 0 | 1 | 1 | 0 | 0 | 1 |
Iv | 0 | 0 | 1 | 1 | 0 | 0 | 1 |
Ali | 0 | 1 | 0 | 1 | 0 | 0 | 0 |
这个数据框可能太小而没有意义,但我的原始数据框是 100k 行,并且会有大于 0 和 1 的数字。
我已经使用 pivot_table 和分组依据的几种变体查看了各种选项,但我似乎无法弄清楚。
df.pivot_table(index = 'names', columns= 'commented', aggfunc= 'count')
df.groupby('names').commented.apply(list).reset_index()
df.explode('names')['commented'].value_counts()
df.set_index('names').apply(pd.Series.explode).reset_index()
我尝试过的几乎所有解决方案都给我错误:TypeError: unhashable type: 'list'
在您的示例输入中,names
和 commented
列中的每个元素都是一个只有 1 个元素(字符串)的数组。不确定您的真实数据是否属于这种情况。
您可以用逗号分隔每个字符串,然后展开并旋转数据框:
split = lambda x: x[0].split(", ")
(
df.assign(
names=df["names"].apply(split),
commented=df["commented"].apply(split),
dummy=1
)
.explode("names")
.explode("commented")
.pivot_table(index="names", columns="commented", values="dummy", aggfunc="count", fill_value=0)
)
您可以尝试将字符串列表分解为行,然后使用 pandas.crosstab
df = (df.explode(df.columns.tolist())
.apply(lambda col: col.str.split(', '))
.explode('names')
.explode('commented'))
out = pd.crosstab(df['names'], df['commented'])
print(df)
names commented
1 Kevin Kevin
1 Kevin Antoine
1 Kevin Iv
1 Jack Kevin
1 Jack Antoine
1 Jack Iv
2 Antoine Antoine
2 Antoine Mary
2 Antoine Ali
2 Mary Antoine
2 Mary Mary
2 Mary Ali
2 Johanne Antoine
2 Johanne Mary
2 Johanne Ali
2 Iv Antoine
2 Iv Mary
2 Iv Ali
3 Ali Mary
3 Ali Jack
print(out)
commented Ali Antoine Iv Jack Kevin Mary
names
Ali 0 0 0 1 0 1
Antoine 1 1 0 0 0 1
Iv 1 1 0 0 0 1
Jack 0 1 1 0 1 0
Johanne 1 1 0 0 0 1
Kevin 0 1 1 0 1 0
Mary 1 1 0 0 0 1
这是使用 str.get_dummies()
(df.assign(names = df['names'].str[0].str.split(', '))
.explode('names')
.set_index('names')
.squeeze()
.str[0]
.str.get_dummies(sep=', '))
输出:
Ali Antoine Iv Jack Kevin Mary
names
Kevin 0 1 1 0 1 0
Jack 0 1 1 0 1 0
Antoine 1 1 0 0 0 1
Mary 1 1 0 0 0 1
Johanne 1 1 0 0 0 1
Iv 1 1 0 0 0 1
Ali 0 0 0 1 0 1