pandas-字符串列合并不起作用(bug?)

46

我正试图在两个数据框之间进行简单的合并。这些数据框来自两个不同的SQL表,其中连接键为字符串:

>>> df1.col1.dtype
dtype('O')
>>> df2.col2.dtype
dtype('O')

我尝试使用以下内容合并它们:

>>> merge_res = pd.merge(df1, df2, left_on='col1', right_on='col2')

内连接的结果为空,这首先提示我可能在交集中没有任何条目:

>>> merge_res.shape
(0, 19)

但是当我尝试匹配单个元素时,我看到了这种非常奇怪的行为。

# Pick random element in second dataframe
>>> df2.iloc[5,:].col2
'95498208100000'

# Manually look for it in the first dataframe
>>> df1[df1.col1 == '95498208100000']
0 rows × 19 columns
# Empty, which makes sense given the above merge result

# Now look for the same value as an integer
>>> df1[df1.col1 == 95498208100000]
1 rows × 19 columns
# FINDS THE ELEMENT!?!
因此,这些列被定义为“object”数据类型。 将它们作为字符串搜索不会产生任何结果。 将它们作为整数搜索会返回一个结果,我认为这就是上面的合并不起作用的原因.. 有什么想法吗? Pandas几乎像是将df1.col1转换为整数,只是因为它可以这样做,即使在匹配时应该将其视为字符串。(我尝试使用示例数据框来复制此过程,但是对于小示例,我看不到这种行为。如果您有关于如何找到更详细的示例的建议,也请告诉我。)

1
看起来你的列中混合了不同的数据类型,我建议首先尝试将所有值强制转换为数字,例如 df1['col1'] = df1['col1'].astype(int)。这可能会失败,这意味着你有一些字符串值无法表示为整数,所以接下来尝试 df1['col1'] = pd.to_numeric(df1['col1'], errors='coerce'),它将把无效的值强制转换为 NaN - EdChum
啊,谢谢@EdChum!其中一些值可以转换为“int”,但其他值则不能。因此,该列的正确数据类型应为“str”,但由于这不是一个正确的数据类型,所以我认为“object”会起作用。但当我手动将所有内容转换为“str”时,合并确实成功了。谢谢! - user1496984
7个回答

74
问题在于object dtype是具有误导性的。我以为它意味着所有的项都是字符串。但显然,在读取文件时,pandas会将某些元素转换为整数,并将其余部分保留为字符串。

解决方案是确保每个字段都是一个字符串:

>>> df1.col1 = df1.col1.astype(str)
>>> df2.col2 = df2.col2.astype(str)

然后合并按预期进行。

(我希望有一种指定str类型的dtype的方式......)


7
奇怪,你的解决方案行得通。但在此之前和此后,有问题的变量的数据类型都是'O'。我猜想,就像你所暗示的,这些对象类型的背后还有更多东西。希望它能更透明一些。 - Ben Ogorek
2
上帝保佑你,好人!你为我节省了数小时的烦恼! - Irka Irenka
当你在Excel中为这些列应用vlookup时,同样的情况也会发生。我在快速使用vlookup时得到了错误的结果,所以转而使用pandas,但在那里得到了相同的输出(nan)。 - Asad Rauf
谢谢!一旦我将我合并列转换为两列中的str格式,就成功了,感到非常欣慰! - vanetoj

27

我曾遇到一个情况,其中df.col = df.col.astype(str)的解决方案不起作用。原来问题出在编码上。

我的原始数据看起来像这样:

In [72]: df1['col1'][:3]
Out[73]: 
             col1
0  dustin pedroia
1  kevin youkilis
2     david ortiz

In [72]: df2['col2'][:3]
Out[73]: 
             col2
0  dustin pedroia
1  kevin youkilis
2     david ortiz

使用 .astype(str) 后,合并仍然无法正常工作,因此我执行了以下操作:

并在执行其他操作后得到了预期的结果。

df1.col1 = df1.col1.str.encode('utf-8')
df2.col2 = df2.col2.str.encode('utf-8')

并且能够找到不同之处:

In [95]: df1
Out[95]: 
                       col1
0  b'dustin\xc2\xa0pedroia'
1  b'kevin\xc2\xa0youkilis'
2     b'david\xc2\xa0ortiz'

In [95]: df2
Out[95]: 
                col2
0  b'dustin pedroia'
1  b'kevin youkilis'
2     b'david ortiz'

只需在解码后的df1.col1变量(即在运行.str.encode('utf-8')之前)上运行 df1.col1 = df1.col1.str.replace('\xa0',' ') ,然后合并就可以完美地工作了。

注意:无论替换了什么,我总是使用.str.encode('utf-8')来检查它是否起作用。

或者

使用正则表达式和Anaconda中的Spyder IDE的变量资源管理器,我发现了以下差异。

import re
#places the raw string into a list
df1.col1 = df1.col1.apply(lambda x: re.findall(x, x))  
df2.col2 = df2.col2.apply(lambda x: re.findall(x, x))

我的df1数据变成了这样(从Spyder中复制并粘贴):

['dustin\xa0pedroia']
['kevin\xa0youkilis']
['david\xa0ortiz']

这只是有稍微不同的解决方案。我不知道在哪种情况下第一个示例不起作用而第二个示例可以,但我想提供两个示例以防万一出现此类情况 :)


非常有帮助!我遇到了同样的问题,结果是编码问题,这让我意想不到!谢谢! - Diego Duarte
谢谢,这是由于使用beautifulsoup进行网络爬虫的问题。替换它解决了这个问题。 - Efkan

15

感谢@seeiespi,使用..str.encode('utf-8')帮助我找出我的字符串需要被剥离,如下所示

20                 b'Belize '   ...     0,612
21                  b'Benin '   ...     0,546
解决方法是使用带状物。
df1.col1 = df1.col1.str.strip()
df1.col1 = df1.col1.str.strip()

这个 .strip() 救了我的一天!!而且实际上只有在编码后才可见,所以我建议这样做。非常感谢! - ranemak

4

以上解决方案都不适用于我,因为合并实际上已经完成了,但是索引出现了问题。删除索引对我有帮助:

df['sth'] = df.merge(df2, how='left', on=['x', 'y'])['sth'].values

哇,这真是出乎意料。谢谢,这个答案对我帮助很大;合并两个字符串列,其中一个是唯一的。pd.merge 有它自己的小问题。 - Krisselack

3

2

可能你的列存在一些差异或空格,导致出现此错误。

首先检查你的列类型以及项目之间是否存在任何不同之处。

df1.col1 = df1.col1.str.encode('utf-8')
df2.col2 = df2.col2.str.encode('utf-8')

如果它们之间有任何不同,你可以使用

df1.col1 = df1.col1.str.replace("this", "for that")

或者如果有任何空格

df1.col1 = df1.col1.apply(str).str.strip()
# This apply(str) is being used because without it, the program returns an error related to being enable to convert from byte.

0

这一步:

df1.col1 = df1.col1.str.strip()
df1.col1 = df1.col1.str.strip()

然后执行这个步骤:

pd.merge(df1.assign(x=df1.x.astype(str)), 
         df2.assign(x=df2.x.astype(str)), 
         how='left', on='x')

对我有用,我的意思是两个都在一起。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接