[英]How to select pandas row with maximum value in one column, from a group of rows that share two common columns?
[英]In pandas, how to group row together if any value in the columns (or subset of columns) is common?
我想根据任何列中的共同值将行分组在一起。
我有一张看起来像这样的桌子
指数 | 电话 | 用户身份 | |
---|---|---|---|
1 | abc@gmail.com | 123456 | 1 |
2 | def@gmail.com | 钠 | 2 |
3 | 钠 | 123456 | 钠 |
4 | def@gmail.com | 987654 | 钠 |
5 | 钠 | 钠 | 1 |
如何将索引 1、3、5 组合在一起(因为索引 1 和 3 有共同的电话号码,而索引 1 和 5 有共同的 UserID)
指数 | 电话 | 用户身份 | |
---|---|---|---|
1 | abc@gmail.com | 123456 | 1 |
3 | 钠 | 123456 | 钠 |
5 | 钠 | 钠 | 1 |
并将索引 2、4 组合在一起(因为索引 2 和 4 有共同的电子邮件)
指数 | 电话 | 用户身份 | |
---|---|---|---|
2 | def@gmail.com | 钠 | 2 |
4 | def@gmail.com | 987654 | 钠 |
谢谢你。
由于您希望继续在同一个 dataframe 中工作,并且由于组类型之间存在重叠的可能性,我建议创建两个带有编号组的额外列:
df['email_groups'] = df.groupby(df.email).ngroup()
df['phone_groups'] = df.groupby(df.phone).ngroup()
结果:
指数 | 电话 | 用户身份 | email_groups | 电话组 | ||
---|---|---|---|---|---|---|
0 | 1 | abc@gmail.com | 123456 | 1 | 0 | 0 |
1 | 2 | def@gmail.com | 楠 | 2 | 1 | -1 |
2 | 3 | 楠 | 123456 | 楠 | -1 | 0 |
3 | 4 | def@gmail.com | 987654 | 楠 | 1 | 1 |
4 | 5 | 楠 | 楠 | 1 | -1 | -1 |
请注意,空值将使用-1
进行分类。 您可以使用例如df['phone_groups'].value_counts()
来计算组大小,并按组号等进行过滤。
我不确定是否存在优雅的 pandas-only 解决方案。 在这里,我们首先创建几个辅助函数,然后应用到 df. 主要思想是有一个字典,我们根据任何字段中的部分匹配来跟踪我们分配给元组(email,phone,UserID)
的组ID
首先我们加载数据
import pandas as pd
import numpy as np
from io import StringIO
data = StringIO(
"""
index email phone UserID
1 abc@gmail.com 123456 1
2 def@gmail.com NaN 2
3 NaN 123456 NaN
4 def@gmail.com 987654 NaN
5 NaN NaN 1
""")
df = pd.read_csv(data, delim_whitespace=True)
接下来我们定义partial_match
function 并测试它
def partial_match(key1, key2):
'''
Return True if any of the elements of key1 and key2 match
'''
for f1, f2 in zip(key1, key2):
if f1 == f2:
return True
return False
# a bit of testing
print(partial_match(('abc@gmail.com',123456.0,.0),(np.NaN,123456.0,np.NaN))) # True
print(partial_match(('abc@gmail.com',123456.0,.0),('def@gmail.com', np.NaN, 2.0))) # False
接下来我们定义一个全局字典,我们将在其中保留组 ID 和 function 来更新它,并进行一些测试
# global dictionary of group ids
groups = {}
def assign_group(key):
'''
Assign a group number to a new key, either existing if there is a partial match
or a new one. Also return the group number for the key
'''
# first element is assigned 0
if len(groups) == 0:
groups[key] = 0
return groups[key]
# see if we already have a partial match
for k in groups:
if partial_match(k,key):
groups[key] = groups[k]
return groups[key]
# no match -- new group
groups[key] = max(groups.values())+1
return groups[key]
# a bit of testing
assign_group(('abc@gmail.com',123456.0,.0))
assign_group((np.NaN,123456.0,np.NaN))
assign_group(('def@gmail.com', np.NaN, 2.0))
print(groups)
测试返回
{('abc@gmail.com', 123456.0, 0.0): 0, (nan, 123456.0, nan): 0, ('def@gmail.com', nan, 2.0): 1}
现在准备开始主要表演。 我们依次对每一行应用assign_group
,将结果记录在df['group_id']
# populate 'groups' with the data from the df, and add the group id to the df
groups = {}
df['group_id'] =df.apply(lambda row: assign_group((row['email'],row['phone'],row['UserID'])), axis=1)
df
我们得到了这个
index email phone UserID group_id
-- ------- ------------- ------- -------- ----------
0 1 abc@gmail.com 123456 1 0
1 2 def@gmail.com nan 2 1
2 3 nan 123456 nan 0
3 4 def@gmail.com 987654 nan 1
4 5 nan nan 1 0
现在您可以在group_id
上进行分组,例如:
df.groupby('group_id').count()
返回
index email phone UserID
group_id
0 3 1 2 2
1 2 2 1 1
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.