请查看此问题的摘要以及底部的实际完整数据。
我正在尝试将两个Pandas DataFrames与多对一关系合并。
这通常是一个使用LEFT
连接的类似SQL的过程,但是我得到了一个意想不到的结果。
一个DataFrame包含药物名称及其成分列表(左侧)以及一些额外信息(不相关的ID和制造药物的公司),另一个包含成分列表及其数字ID(右侧) )。
显然,我想得到的是合并的DataFrame,其中每种成分的ID列在成分名称旁边,考虑到这是多对一的情况,一些药物将会重复使用相同的成分。
两个DataFrames的设计示例(为简洁起见省略索引):
药物(df1):
someid name ingredient company
1 Antivert meclizine hydrochloride Drug and Co
2 Antivert meclizine hydrochloride Pharma Inc
3 Antivert meclizine hydrochloride Medicine Limited
4 Antizol fomepizole Drug and Co
5 Antizol fomepizole Medicine Limited
6 Anzeme toxyphenonium bromide Pharma Inc
7 Anzemet toxyphenonium bromide Drug and Co
... ...
[51397 rows x 4 columns]
成分(df2):
ingredient id
meclizine hydrochloride 1
fomepizole 2
toxyphenonium bromide 3
... ...
[3354 rows x 2 columns]
要进行合并,我只需执行以下操作:
df1.merge(df2, on='ingredient', how='left')
预期的输出是:
someid name ingredient company id
1 Antivert meclizine hydrochloride Drug and Co 1
2 Antivert meclizine hydrochloride Pharma Inc 1
3 Antivert meclizine hydrochloride Medicine Limited 1
4 Antizol fomepizole Drug and Co 2
5 Antizol fomepizole Medicine Limited 2
6 Anzeme toxyphenonium bromide Pharma Inc 3
7 Anzemet toxyphenonium bromide Drug and Co 3
... ...
[51397 rows x 5 columns]
然而,实际输出是:
someid name ingredient company id
1 Antivert meclizine hydrochloride Drug and Co 1
2 Antivert meclizine hydrochloride Pharma Inc 2
3 Antivert meclizine hydrochloride Medicine Limited 3
4 Antizol fomepizole Drug and Co 4
5 Antizol fomepizole Medicine Limited 5
6 Anzeme toxyphenonium bromide Pharma Inc 6
7 Anzemet toxyphenonium bromide Drug and Co 7
... ...
[3354 rows x 5 columns]
这里需要注意的两件重要事情:
id
列只会向上计数,ID不会重复。两个DataFrame上的info()
向我显示两个ingredient
列的类型相同(只是为了排除比较问题):
药物(df1):
<class 'pandas.core.frame.DataFrame'>
Int64Index: 51397 entries, 0 to 51396
Data columns (total 4 columns):
someid 51397 non-null int64
name 51397 non-null object
ingredient 51397 non-null object
company 51397 non-null object
dtypes: int64(1), object(3)
memory usage: 1.2+ MB
None
成分(df2):
<class 'pandas.core.frame.DataFrame'>
Int64Index: 3354 entries, 0 to 3353
Data columns (total 2 columns):
id 3354 non-null int64
ingredient 3354 non-null object
dtypes: int64(1), object(1)
memory usage: 78.6+ KB
None
现在有趣的部分。为了找出这可能出错的地方,我尝试使用head()
仅对两个DataFrame的子集进行合并:
df1 = df1.head(100)
df2 = df2.head(100)
如果我然后执行合并,我会得到所需的输出没有问题,只有100行。如果我继续增加head()
的行数,即使它超过了存在的行数,输出仍然是完美的。例如,这完美地运作:
df1 = df1.head(100000)
df2 = df2.head(100000)
请注意,我包含前100,000行(df1中只有53.000行)。这也将给我预期的合并输出。
有趣的位:在包含head(100000)
的大量行之后,只有Drugs DataFrame(df1)的信息会稍微改变:
<class 'pandas.core.frame.DataFrame'>
Int64Index: 51397 entries, 0 to 51396
Data columns (total 4 columns):
someid 51397 non-null int64
name 51397 non-null object
ingredient 51397 non-null object
company 51397 non-null object
dtypes: int64(1), object(3)
memory usage: 2.0+ MB
None
请注意,内存使用量从1.2 MB增加到2.0 MB。除此之外,它们仍然是DataFrame对象,其结构与我通过该操作之前的结构相同。
这里可能会发生什么?为什么合并会产生如此奇怪的结果,除非我首先通过head()
拉出DataFrame?
总结:在不使用head()
的情况下,merge()
输出显示减少的行(#rows =#右侧行),并且每个成分ID都只是“粘贴”在以连续的方式。但是,如果我使用head()
,即使有足够多的数字来包含两边的所有行,连接也会按预期工作。
Python版本:2.7.10 - Pandas版本:0.17.1 - NumPy版本:1.10.1
完整数据:对于想要尝试完全相同数据的人,我在Github上创建了两个要点,其中包含药物和上面提到的成分DataFrame的完整数据(字典形式)
使用DataFrame.to_dict()
导出它们,可以使用DataFrame.from_dict(data)
将其导入/转换回DataFrame。