繁体   English   中英

在 pandas 中,如果列(或列的子集)中的任何值是常见的,如何将行组合在一起?

[英]In pandas, how to group row together if any value in the columns (or subset of columns) is common?

我想根据任何列中的共同值将行分组在一起。

我有一张看起来像这样的桌子

指数 email 电话 用户身份
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)

指数 email 电话 用户身份
1 abc@gmail.com 123456 1
3 123456
5 1

并将索引 2、4 组合在一起(因为索引 2 和 4 有共同的电子邮件)

指数 email 电话 用户身份
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 电话 用户身份 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM