优雅而有效的方法将数据帧重复合并到数据帧的单个列中

时间:2014-06-05 12:59:20

标签: python pandas scipy

我有一组包含键,值对(作为列)的数据帧,每个数据帧都有一个键的子集。我想将它们全部合并到一个初始数据帧的单个值列中,其中包含来自相同密钥空间的密钥(需要保留其他列)。

例如,给定:

DF:

   key   a
0    1  A1
1   20  A2
2   33  A3
3   44  A4

DF2:

   key  value
0    1  V21
1   20  NaN
2   33  V23
3   44  NaN
4   55  V25

DF3:

   key  value
0    1  NaN
1   20  V32

我能做到:

df['value'] = ''
df2.dropna(inplace=True)
df3.dropna(inplace=True)
df.loc[df.key.isin(df2.key), 'value'] = df2.value
df.loc[df.key.isin(df3.key), 'value'] = df3.value

并得到我想要的结果:

   key   a  value
0    1  A1  V21
1   20  A2  V32
2   33  A3  V23
3   44  A4     

键是int64,值是object(str)。

问题在于,当我在更大的df数据帧上使用此方案时,结果是值列设置为NaN(在Pandas 0.13& 0.14上)

例如:(此处提供的数据:df.csv& common.csv

# read data - both pre-sorted by key
df = pandas.read_csv('df.csv')  # ~110MB / ~3M rows
common = pandas.read_csv('common.csv')  # 83 rows

all(common.key.isin(df.key))   # True - all keys in common are in df

df['value']=''  # new col of empty strings

df.loc[df.key.isin(common.key), 'value'] = common.value  # set select df values (?)

any(common.value.isin(df.value))  # False - no values from common are in df (!)

all(pandas.isnull(df.value) | (df.value=='') )  # True - all either '' or null/NaN

感谢有关如何达到预期效果的任何帮助。

更新

正如@Jeff指出的那样,这不是Pandas的错误,而是预期的行为。将Pandas对象(例如DataFrame)分配给''一个DataFrame(例如,如上所述由.loc返回),Pandas试图对齐'这两个指数。也就是说,它通过Pandas索引将RHS上的行分配给LHS上的行。

在上面的例子中,RHS有一个简单的整数顺序索引 - 行号,而LHS索引是来自df的所选行号的子集( - 即df也有一个顺序整数索引,其中切片选择一个子集)。对于LHS上RHS上没有匹配索引的行,Pandas会分配NA。

优雅的解决方案是将密钥列的内容索引为df和common索引。然后,分配将按计划进行 - 根据密钥的值将行从RHS分配给LHS。这更自然,避免了需要预先排序或子集。

@ ct-zhu的答案之所以有效,是因为访问RHS的.values属性会返回一个Numpy数组,该数组没有Panda风格索引的概念。当Pandas在RHS上收到这样一个对象时,它会在没有尝试对齐索引的情况下进行分配( - 因为我的例子预先安排两个通过排序处于相同的顺序,所以这是有效的)

1 个答案:

答案 0 :(得分:1)

根据您刚才描述的内容,它应该是merge的经典案例吗?对于您的真实案例数据:

In [6]:

print pd.merge(df, common, left_on='key', right_on='key')
           key       a             datetime             value
0   3559045188  455.22  2013-01-03 02:18:00  0584e46e2ccefa0c
1   3559045189  378.23  2013-01-03 02:19:00  df15f61e8d2dbbd4
2   3559045191   13.49  2013-01-03 04:46:00  bc75d05dae4a1aaf
3   3559045192    7.69  2013-01-03 04:48:00  41db9e3f3c9996e0
4   3559045398  641.10  2013-01-02 22:45:00  4933d72213672819
5   3559045401  548.41  2013-01-02 22:49:00  f698ef75efda0af8
6   3559045412    6.22  2013-01-02 23:12:00  3d48cc4c1d0b6f26
7   3559045414   54.97  2013-01-02 23:19:00  a9b2b5b33be185a1
8   3559045417  275.95  2013-01-02 23:22:00  1a59cfcf32351d81
9   3559045418  407.95  2013-01-02 23:22:00  af81dac601f02f36
10  3559045419   10.00  2013-01-02 23:23:00  849a2fa3e46a0f2e
           ...     ...                  ...               ...

[83 rows x 4 columns]

应该是评论,但我想显示结果数据框

另外,要将df2df3(显示在上半部分或您的问题中)合并在一起,请使用pd.concat((df2, df3)).dropna()。如果您有重复密钥,则需要.drop_duplicate(),但它依赖于您计划保留的值。

编辑:

您只需再添加一个values

即可执行您所描述的操作
In [77]:

DF=df.copy()
DF['value']=np.nan
DF.ix[df.key.isin(common.key),'value']=common.ix[common.key.isin(DF.key), 'value'].values
#Assume their are both sorted and common may contain keys df doesn't.
print DF.dropna().head(10)
                key       a             datetime             value
2364554  3559045188  455.22  2013-01-03 02:18:00  0584e46e2ccefa0c
2364555  3559045189  378.23  2013-01-03 02:19:00  df15f61e8d2dbbd4
2364557  3559045191   13.49  2013-01-03 04:46:00  bc75d05dae4a1aaf
2364558  3559045192    7.69  2013-01-03 04:48:00  41db9e3f3c9996e0
2364764  3559045398  641.10  2013-01-02 22:45:00  4933d72213672819
2364767  3559045401  548.41  2013-01-02 22:49:00  f698ef75efda0af8
2364778  3559045412    6.22  2013-01-02 23:12:00  3d48cc4c1d0b6f26
2364780  3559045414   54.97  2013-01-02 23:19:00  a9b2b5b33be185a1
2364783  3559045417  275.95  2013-01-02 23:22:00  1a59cfcf32351d81
2364784  3559045418  407.95  2013-01-02 23:22:00  af81dac601f02f36

[10 rows x 4 columns]

这可能是个错误。我不确定。 但是,一次添加一个common的效率非常低,因为isin需要花费大量时间来处理非常大的df数据帧。我的机器上需要大约3秒钟。