我有一个数据帧,我想按id和公共的"顶级"字符串进行分组,其中包括"低级"字符串的计数。例如,
id name
1 AA-BB-CC-DD
1 AA-BB-CC
1 AA-BB-CC-DD-EE
1 AA-BB-UU-VV
1 AA-BB-UU
1 FF-MM-NN
1 FF-MM-NN-PP
2 XX-YY
2 XX-ZZ
2 XX-ZZ-AA
对于id1,列名的第一个顶级是AA,第二个BB[/em>,第一个CC等等。数据帧中id 1的常见"最高"级别是AA-BB-CC//em>。
所需输出为
id name count
1 AA-BB-CC 3
1 AA-BB-UU 2
1 FF-MM-NN 2
2 XX-YY 1
2 XX-ZZ 2
谢谢。
IIUC,您可以使用第一个级别组成组,然后使用自定义函数groupby.agg
:
group = df['name'].str.extract('^([^-]+)', expand=False)
def min_string(s):
return min(s, key=lambda x: x.count('-'))
out = (df
.groupby(['id', group], as_index=False)
.agg(name=('name', min_string),
count=('name', 'count')
)
)
输出:
id name count
0 1 AA-BB-CC 3
1 1 FF-MM-NN 2
2 2 XX-YY 3
更新:处理第一级的最小集合
将min_string
更改为:
def min_string(s):
return '-'.join(x[0] for x in zip(*s.str.split('-'))
if len(set(x)) == 1)
示例输入:
id name
0 1 AA-BB-CC-DD
1 1 AA-BB-CC
2 1 AA-BB-CC-DD-EE
3 1 AA-BB-UU-VV
4 1 AA-BB-UU
5 1 FF-MM-NN
6 1 FF-MM-NN-PP
7 2 XX-YY
8 2 XX-ZZ
9 2 XX-ZZ-AA
输出:
id name count
0 1 AA-BB 5
1 1 FF-MM-NN 2
2 2 XX 3
update2:最大公共子群
def make_groups(s, sep='-'):
d = {}
s = s.str.split(sep).sort_values()
prev = s.iloc[0]
for idx, val in s.items():
if val[:len(prev)] != prev:
prev = val
d[idx] = sep.join(prev)
return pd.Series(d, index=s.index)
group = df.groupby('id', group_keys=False)['name'].apply(make_groups)
out = (df
.groupby(['id', group], as_index=False)
.agg(name=('name', min_string),
count=('name', 'count')
)
)
输出:
id name count
0 1 AA-BB-CC 3
1 1 AA-BB-UU 2
2 1 FF-MM-NN 2
3 2 XX-YY 1
4 2 XX-ZZ 2
使用的输入:
id name
0 1 AA-BB-CC-DD
1 1 AA-BB-CC
2 1 AA-BB-CC-DD-EE
3 1 AA-BB-UU-VV
4 1 AA-BB-UU
5 1 FF-MM-NN
6 1 FF-MM-NN-PP
7 2 XX-YY
8 2 XX-ZZ
9 2 XX-ZZ-AA