在 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
我想替换单元格中的多个值(如果存在):1
被 Facebook
替换,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 倍!
【讨论】:
我认为如果需要输出为列表,最快更好的解决方案是将列表理解应用为stack
和unstack
,但我没有测试它。也许你可以添加时间。
@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 转换为每个单元格的均值和标准差