创建随机生成的唯一字母列到 pandas 数据帧?

Create column of unique randomly generated letters to pandas dataframe?

提问人:mjoy 提问时间:1/7/2023 更新时间:1/7/2023 访问量:66

问:

我有一个数据框,其中有一列随机字母和数字,然后是一列需要添加到第一列随机字符串中的随机字母/数字的列。像这样,但我的数据帧是 3+ 百万行:

  id     missing
XK39J       4
NI94N       4
9IN3        5
MN83D       4
IUN2        5

我正在使用以下代码来生成新的随机序列:

def id_generator(size, chars=string.ascii_uppercase + string.digits):
    return ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(size))

data['new_id'] = data['missing'].apply(lambda x: id_generator(size = x))
data['final_id'] = data['id'] + data['new_id']

但是,当我使用它时,我最终会在“final_id”列中得到几个重复的值。但是,我需要“final_id”列中的每个值都是唯一的。喜欢:

id     missing     new_id      final_id
XK39J       4       NJI4       XK39JNJI4 
NI94N       4       BNER       NI94NBNER
9IN3        5       ER41J      9IN3ER41J
MN83D       4       9D4S       MN83D9D4S
IUN2        5       MNST3      IUN2MNST3

我的想法是将所有 ID 存储在一个列表中,然后如果它匹配,则获得一个新的随机生成的序列,但考虑到将有 300 万+ 个 id,它不起作用,因为遍历一行 3m 将花费太长时间。喜欢:

def id_generator(size, chars=string.ascii_uppercase + string.digits):
    val_ls = []
    val = ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(size))
    while val in val_ls:
       val = ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(size))
    else:
       val_ls.append(val)
       return val
     

如何确保没有重复?

Python 熊猫 列表 序列

评论

0赞 mozway 1/7/2023
如果你想为每个值添加唯一的字符串,你需要循环,不幸的是......
2赞 Dani Mesejo 1/7/2023
尝试使用集合而不是列表,这应该可以加快速度
0赞 Dani Mesejo 1/7/2023
另外,您想包含所有字母吗?一些字母和所有数字就足够了吗?

答:

1赞 Emma 1/7/2023 #1

这仍然是一种蛮力,但你可以尝试这样的事情。

from uuid import uuid4

# First generate final_id for all without caring about duplicates
df['new_id'] = df.missing.transform(lambda x: str(uuid4()).upper().replace('-', '')[:x])
df['final_id'] = df.id + df.new_id

# final_ids that are unique and already good
id_good = df.final_id.unique().tolist()

# Try re-generating final_id until we get no more duplicates
while(len(df[df.final_id.duplicated()]) > 0):
    dupe_mask = df.final_id.duplicated()

    # Regenerate final_id, store in temp column
    df.loc[dupe_mask, 'new_id'] = df.loc[dupe_mask].missing.transform(lambda x: str(uuid4()).upper().replace('-', '')[:x])
    df.loc[dupe_mask, 'temp'] = df.loc[dupe_mask].id + df.loc[dupe_mask].new_id

    # If the new final_id is not duplicates with currently good final_ids, keep it.
    df.loc[dupe_mask & ~df.temp.isin(id_good), 'final_id'] = df.loc[dupe_mask & ~df.temp.isin(id_good), 'temp']
    
    id_good += df.loc[dupe_mask & ~df.temp.isin(id_good), 'final_id'].unique().tolist()
    
    df = df.drop('temp', axis=1)     

当我使用 3M 行进行测试时,它只需要执行 1 个循环,但是,您可能希望添加超时,因为理论上它可以永远运行。

评论

0赞 mjoy 1/14/2023
谢谢,这个解决方案没有花太长时间,比检查列表中的每个值都要好