如何根据非常大的df中的名称有效地将唯一ID分配给具有多个条目的个人

Posted

技术标签:

【中文标题】如何根据非常大的df中的名称有效地将唯一ID分配给具有多个条目的个人【英文标题】:How to efficiently assign unique ID to individuals with multiple entries based on name in very large df 【发布时间】:2018-01-22 22:13:23 【问题描述】:

我想获取一个包含一组不同的唯一个人的数据集,每个人都有多个条目,并为每个人的所有条目分配一个唯一的 ID。以下是 df 的示例:

      FirstName LastName  id
0     Tom       Jones     1
1     Tom       Jones     1
2     David     Smith     1
3     Alex      Thompson  1
4     Alex      Thompson  1

所以,基本上我希望 Tom Jones 的所有条目都有 id=1,David Smith 的所有条目都有 id=2,Alex Thompson 的所有条目都有 id=3,依此类推。

所以我已经有了一个解决方案,这是一个简单的 Python 循环迭代两个值(一个用于 id,一个用于索引)并根据它们是否与前一个个体匹配为个体分配一个 id:

x = 1
i = 1

while i < len(df_test):
    if (df_test.LastName[i] == df_test.LastName[i-1]) & 
    (df_test.FirstName[i] == df_test.FirstName[i-1]):
        df_test.loc[i, 'id'] = x
        i = i+1
    else:
        x = x+1
        df_test.loc[i, 'id'] = x
        i = i+1

我遇到的问题是 DataFrame 有大约 900 万个条目,因此使用该循环将花费大量时间来运行。谁能想到一个更有效的方法来做到这一点?我一直将 groupby 和 multiindexing 视为潜在的解决方案,但还没有找到合适的解决方案。

【问题讨论】:

【参考方案1】:

这种方法使用.groupby().ngroup()(Pandas 0.20.2 中的新功能)来创建id 列:

df['id'] = df.groupby(['LastName','FirstName']).ngroup()
>>> df

   First    Second  id
0    Tom     Jones   0
1    Tom     Jones   0
2  David     Smith   1
3   Alex  Thompson   2
4   Alex  Thompson   2

我检查了时间,对于这个例子中的小数据集,Alexander 的答案更快:

%timeit df.assign(id=(df['LastName'] + '_' + df['FirstName']).astype('category').cat.codes)
1000 loops, best of 3: 848 µs per loop

%timeit df.assign(id=df.groupby(['LastName','FirstName']).ngroup())
1000 loops, best of 3: 1.22 ms per loop

但是,对于较大的数据帧,groupby() 方法似乎更快。为了创建一个具有代表性的大型数据集,我使用faker 创建了一个包含 5000 个名称的数据框,然后将前 2000 个名称连接到该数据框以创建一个包含 7000 个名称的数据框,其中 2000 个名称是重复的。

import faker
fakenames = faker.Faker()
first = [ fakenames.first_name() for _ in range(5000) ]
last = [ fakenames.last_name() for _ in range(5000) ]
df2 = pd.DataFrame('FirstName':first, 'LastName':last)
df2 = pd.concat([df2, df2.iloc[:2000]])

在这个更大的数据集上运行时间给出:

%timeit df2.assign(id=(df2['LastName'] + '_' + df2['FirstName']).astype('category').cat.codes)
100 loops, best of 3: 5.22 ms per loop

%timeit df2.assign(id=df2.groupby(['LastName','FirstName']).ngroup())
100 loops, best of 3: 3.1 ms per loop

您可能希望在您的数据集上测试这两种方法,以确定哪种方法最适合您的数据大小。

【讨论】:

我喜欢你的回答,但有必要使用sort=False 就像df.groupby(['LastName','FirstName'], sort=False).ngroup() 一样(之前两列的值也已排序)。 第二,为了测试,最好不要使用像faker(好名字;)这样的特殊库,试试np.random.seed(123) N = 1000 df = pd.DataFrame('FirstName': np.random.randint(20,size=N), 'LastName': np.random.randint(20,size=N)) df['FirstName'] = 'a' + df['FirstName'].astype(str) df['LastName'] = 'a' + df['LastName'].astype(str) df = df.sort_values(['FirstName','LastName']) 对于更大的 df np.random.seed(123) N = 1000000 df = pd.DataFrame('FirstName': np.random.randint(2000,size=N), 'LastName': np.random.randint(2000,size=N)) df['FirstName'] = 'a' + df['FirstName'].astype(str) df['LastName'] = 'a' + df['LastName'].astype(str) df = df.sort_values(['FirstName','LastName']) - 它真的非常快 ;) 祝你好运!【参考方案2】:

您可以将姓氏和名字连接起来,将其转换为类别,然后获取代码。

当然,多个同名的人会拥有相同的id

df = df.assign(id=(df['LastName'] + '_' + df['FirstName']).astype('category').cat.codes)
>>> df
  FirstName  LastName  id
0       Tom     Jones   0
1       Tom     Jones   0
2     David     Smith   1
3      Alex  Thompson   2
4      Alex  Thompson   2

【讨论】:

【参考方案3】:

此方法允许使用变量定义“id”列名。另外,与 assign 或 groupby 方法相比,我发现它更易于阅读。

# Create Dataframe
df = pd.DataFrame(
    'FirstName': ['Tom','Tom','David','Alex','Alex'],
    'LastName': ['Jones','Jones','Smith','Thompson','Thompson'],
    )

newIdName = 'id'   # Set new name here.

df[newIdName] = (df['LastName'] + '_' + df['FirstName']).astype('category').cat.codes     

输出:

>>> df
          FirstName  LastName  id
        0       Tom     Jones   0
        1       Tom     Jones   0
        2     David     Smith   1
        3      Alex  Thompson   2
        4      Alex  Thompson   2

【讨论】:

以上是关于如何根据非常大的df中的名称有效地将唯一ID分配给具有多个条目的个人的主要内容,如果未能解决你的问题,请参考以下文章

Powershell根据给定的计数选择一个随机字母,并动态地将每个字母分配给一个唯一的变量?

如何为 pandas 数据框中的不同组分配唯一 ID?

根据两列分配唯一 ID [重复]

如何有效地将数字字符串值分配给整数? [复制]

在比较两个数据帧时,有没有有效的方法为单元格分配id?

如何使用python有效地填充给定一个非常大的表的矩阵?