在 Pandas 中替换每个单元格的多个值

Posted

技术标签:

【中文标题】在 Pandas 中替换每个单元格的多个值【英文标题】:Replacing multiple values per cell in Pandas 【发布时间】:2017-01-26 03:29:14 【问题描述】:

我在数据框中有以下列:

Q2
1 4
1 3
3 4 11 
1 4 6 15 16

我想替换单元格中的多个值(如果存在):1Facebook 替换,2 替换为 Instagram,等等。

我将值拆分如下:

columns_to_split = 'Q2'

for c in columns_to_split:
    df[c] = df[c].str.split(' ')

哪个输出

code                             
DSOKF31                          [1, 4]
DSOVH39                          [1, 3]
DSOVH05                          [3, 4, 16]
DSOVH23                          [1, 4, 6, 15, 16]
Name: Q2, dtype: object

但是当尝试用字典替换多个值时,如下所示:

social_media_2 = '1':'Facebook', '2':'Instagram', '3':'Twitter', '4':'Messenger (Google hangout, Tagg, WhatsAPP, MSG, Facetime, IMO)', '5':'SnapChat', '6':'Imo', '7':'Badoo', '8':'Viber', '9':'Twoo', '10':'Linkedin', '11':'Flickr', '12':'Meetup', '13':'Tumblr', '14':'Pinterest', '15':'Yahoo', '16':'Gmail', '17':'Hotmail', '18':'M-Pesa', '19':'M-Shwari', '20':'KCB-Mpesa', '21':'Equitel', '22':'MobiKash', '23':'Airtel money', '24':'Orange Money', '25':'Mobile Bankig Accounts', '26':'Other specify'

df['Q2'] = df['Q2'].replace(social_media_2)

我得到相同的输出:

code                             
DSOKF31                          [1, 4]
DSOVH39                          [1, 3]
DSOVH05                          [3, 4, 16]
DSOVH23                          [1, 4, 6, 15, 16]
Name: Q2, dtype: object

在这种情况下如何替换一个单元格中的多个值?

【问题讨论】:

为什么只有一列?它总是有两个值吗?那不应该是两个单独的列吗? 是的,[1, 2] 应该变成 ['Facebook', 'Instagram'][1, 3] 应该变成 ['Facebook', 'Twitter'],等等。它并不总是两个值,[3, 4, 16][1, 4, 6, 15, 16] 之类的值也在数据集中。我会更新问题。 【参考方案1】:

由于项目的数量各不相同,因此没有太多的结构。不过,在拆分字符串后,您可以apply 一个将列表映射到字典值的函数:

In [36]: df = pd.DataFrame('Q2': ['1 4', '1 3', '1 2 3'])

In [37]: df.Q2.str.split(' ').apply(lambda l: [social_media_2[e] for e in l])
Out[37]: 
0    [Facebook, Messenger (Google hangout, Tagg, Wh...
1                                  [Facebook, Twitter]
2                       [Facebook, Instagram, Twitter]
Name: Q2, dtype: object

编辑在 Jezrael 的出色评论之后,这里有一个版本也解释了缺失值:

In [58]: df = pd.DataFrame('Q2': ['1 4', '1 3', '1 2 3', None])

In [59]: df.Q2.str.split(' ').apply(lambda l: [] if type(l) != list else [social_media_2[e] for e in l])
Out[59]: 
0    [Facebook, Messenger (Google hangout, Tagg, Wh...
1                                  [Facebook, Twitter]
2                       [Facebook, Instagram, Twitter]
3                                                   []
Name: Q2, dtype: object

【讨论】:

谢谢!然而,我在那个循环上得到了TypeError: 'float' object is not iterable @JohnBoss 你是像我上面那样运行它,还是像for c in columns_to_split: 那样运行它?循环将尝试将其应用于列'Q''2',因为columns_to_split 是一个字符串。 我像上面一样运行它,得到TypeError: 'float' object is not iterable。 (好吧,我还从一个 excel 文件中读取了我的数据框,当然,social_media_2 变量是用一个列表定义的。) @JohnBoss 你能写出完整的错误信息吗?应该是几行。 Traceback (most recent call last): File "/replace.py", line 17, in <module> df.Q2.str.split(' ').apply(lambda l: [social_media_2[e] for e in l]) File "/usr/local/lib/python2.7/site-packages/pandas/core/series.py", line 2220, in apply mapped = lib.map_infer(values, f, convert=convert_dtype) File "pandas/src/inference.pyx", line 1088, in pandas.lib.map_infer (pandas/lib.c:62658) File "replace.py", line 17, in <lambda> df.Q2.str.split(' ').apply(lambda l: [social_media_2[e] for e in l]) TypeError: 'float' object is not iterable [Finished in 1.6s with exit code 1]【参考方案2】:

这是一个替代解决方案:

In [45]: df
Out[45]:
            Q2
0          1 4
1          1 3
2       3 4 16
3  1 4 6 15 16

In [47]: (df.Q2.str.split(expand=True)
   ....:    .stack()
   ....:    .map(social_media_2)
   ....:    .unstack()
   ....:    .apply(lambda x: x.dropna().values.tolist(), axis=1)
   ....: )
Out[47]:
0                       [Facebook, Messenger (Google hangout, Tagg, WhatsAPP, MSG, Facetime, IMO)]
1                                                                              [Facebook, Twitter]
2                 [Twitter, Messenger (Google hangout, Tagg, WhatsAPP, MSG, Facetime, IMO), Gmail]
3    [Facebook, Messenger (Google hangout, Tagg, WhatsAPP, MSG, Facetime, IMO), Imo, Yahoo, Gmail]
dtype: object

解释:

In [50]: df.Q2.str.split(expand=True).stack().map(social_media_2)
Out[50]:
0  0                                                          Facebook
   1    Messenger (Google hangout, Tagg, WhatsAPP, MSG, Facetime, IMO)
1  0                                                          Facebook
   1                                                           Twitter
2  0                                                           Twitter
   1    Messenger (Google hangout, Tagg, WhatsAPP, MSG, Facetime, IMO)
   2                                                             Gmail
3  0                                                          Facebook
   1    Messenger (Google hangout, Tagg, WhatsAPP, MSG, Facetime, IMO)
   2                                                               Imo
   3                                                             Yahoo
   4                                                             Gmail
dtype: object

In [51]: df.Q2.str.split(expand=True).stack().map(social_media_2).unstack()
Out[51]:
          0                                                               1      2      3      4
0  Facebook  Messenger (Google hangout, Tagg, WhatsAPP, MSG, Facetime, IMO)   None   None   None
1  Facebook                                                         Twitter   None   None   None
2   Twitter  Messenger (Google hangout, Tagg, WhatsAPP, MSG, Facetime, IMO)  Gmail   None   None
3  Facebook  Messenger (Google hangout, Tagg, WhatsAPP, MSG, Facetime, IMO)    Imo  Yahoo  Gmail

时间针对 40K 行 DF:

In [86]: big = pd.concat([df] * 10**4, ignore_index=True)

In [87]: big.shape
Out[87]: (40000, 1)

In [88]: %%timeit
   ....: (big.Q2.str.split(expand=True)
   ....:     .stack()
   ....:     .map(social_media_2)
   ....:     .unstack()
   ....:     .apply(lambda x: x.dropna().values.tolist(), axis=1)
   ....: )
   ....:
1 loop, best of 3: 19.6 s per loop

In [89]: %timeit big.Q2.str.split(' ').apply(lambda l: [social_media_2[e] for e in l])
10 loops, best of 3: 72.6 ms per loop

结论: Ami 的解决方案约为。快 270 倍!

【讨论】:

我认为如果需要输出为列表,最快更好的解决方案是将列表理解应用为stackunstack,但我没有测试它。也许你可以添加时间。 @jezrael,你的意思是 Ami 的解决方案还是其他的? 是的,我认为 Ami 解决方案与您的解决方案相比。你的更熊猫式 ;),但更慢,但也许我错了,因为我现在无法测试它。 感谢您的测试。几个月前,我使用了类似的解决方案,但只有在需要输出为DataFrame 时,您的解决方案才会更精巧。祝你好运,+1 Ami 的解决方案可能更快,但这个替代答案对我更好地理解 Panda 有很大帮助。非常感谢!【参考方案3】:

如果不需要list 作为输出,只需将regex=True 添加到replace

import pandas as pd
import numpy as np

df = pd.DataFrame('Q2': ['1 4', '1 3', '3 4 11'])
print (df)
       Q2
0     1 4
1     1 3
2  3 4 11

social_media_2 = '1':'Facebook', '2':'Instagram', '3':'Twitter', '4':'Messenger (Google hangout, Tagg, WhatsAPP, MSG, Facetime, IMO)', '5':'SnapChat', '6':'Imo', '7':'Badoo', '8':'Viber', '9':'Twoo', '10':'Linkedin', '11':'Flickr', '12':'Meetup', '13':'Tumblr', '14':'Pinterest', '15':'Yahoo', '16':'Gmail', '17':'Hotmail', '18':'M-Pesa', '19':'M-Shwari', '20':'KCB-Mpesa', '21':'Equitel', '22':'MobiKash', '23':'Airtel money', '24':'Orange Money', '25':'Mobile Bankig Accounts', '26':'Other specify'
df['Q2'] = df['Q2'].replace(social_media_2, regex=True)
print (df)

                                                  Q2
0  Facebook Messenger (Google hangout, Tagg, What...
1                                   Facebook Twitter
2  Twitter Messenger (Google hangout, Tagg, Whats...

如果需要lists,请使用其他解决方案。

通过评论编辑:

您可以通过; 使用replace 空格,然后效果很好:

df = pd.DataFrame('Q2': ['1 4', '1 3', '3 4 11'])
print (df)
       Q2
0     1 4
1     1 3
2  3 4 11

df['Q2'] = df['Q2'].str.replace(' ',';')
print (df)
       Q2
0     1;4
1     1;3
2  3;4;11

social_media_2 = '1':'Facebook', '2':'Instagram', '3':'Twitter', '4':'Messenger (Google hangout, Tagg, WhatsAPP, MSG, Facetime, IMO)', '5':'SnapChat', '6':'Imo', '7':'Badoo', '8':'Viber', '9':'Twoo', '10':'Linkedin', '11':'Flickr', '12':'Meetup', '13':'Tumblr', '14':'Pinterest', '15':'Yahoo', '16':'Gmail', '17':'Hotmail', '18':'M-Pesa', '19':'M-Shwari', '20':'KCB-Mpesa', '21':'Equitel', '22':'MobiKash', '23':'Airtel money', '24':'Orange Money', '25':'Mobile Bankig Accounts', '26':'Other specify'
df['Q2'] = df['Q2'].replace(social_media_2, regex=True)
print (df)
                                                  Q2
0  Facebook;Messenger (Google hangout, Tagg, What...
1                                   Facebook;Twitter
2  Twitter;Messenger (Google hangout, Tagg, Whats...

编辑1:

你也可以通过将;添加到keys然后替换为双;来稍微更改dict

df = pd.DataFrame('Q2': ['1 2', '1 3', '3 2 11'])
print (df)
       Q2
0     1 2
1     1 3
2  3 2 11

df['Q2'] = df['Q2'].str.replace(' ',';;') + ';'
print (df)
          Q2
0      1;;2;
1      1;;3;
2  3;;2;;11;

social_media_2 = '1':'Fa', '2':'I', '3':'T', '11':'KL'
#add ; to keys in dict
social_media_2 = dict((key + ';', value) for (key, value) in social_media_2.items())
print (social_media_2)
'1;': 'Fa', '2;': 'I', '3;': 'T', '11;': 'KL'
df['Q2'] = df['Q2'].replace(social_media_2, regex=True)
print (df)
        Q2
0     Fa;I
1     Fa;T
2  T;I;1Fa

【讨论】:

谢谢。我想要一个不是空格的答案之间的分隔符,例如“;”,但否则这也可以满足我的需要。 我添加解决方案,请检查。 我刚刚发现我可以很容易地做到这一点,首先用df['Q2'] = df['Q2'].replace(' ','; ', regex=True) 用分号替换空格。非常感谢! 成功了!唯一的问题是像“19”这样的两位数现在在字典中映射到“1”和“9”(所以“19”变成“FacebookTwoo”,它们是“1”和“9”的值,而不是'19' 的值。所以我认为我毕竟需要列表,除非我能找到一个智能正则表达式字符串,在这种情况下占单位数和双位数。 我添加了新的解决方案,请检查一下。

以上是关于在 Pandas 中替换每个单元格的多个值的主要内容,如果未能解决你的问题,请参考以下文章

在不使用索引的情况下替换 pandas DataFrame 中选定单元格的值

带有多个值的单元格的 Microsoft Excel 电子表格筛选器

Pandas:将 DataFrame 转换为每个单元格的均值和标准差

将多个值写入一个单元格 - VBA

Pandas:使用合并的单元格和空白值解析 Excel 电子表格

Python,Pandas,数据框 - 拆分和删除单元格的某些部分