Pandas数据框架上的向量化查找

3
我有两个数据框。df1是一个表格,我需要使用从df2中检索的索引、列对来提取值。我发现有一个函数get_value,当给定索引和列值时,它可以完美地工作,但是当尝试将此函数向量化以创建新列时,我失败了……
df1 = pd.DataFrame(np.arange(20).reshape((4, 5)))

df1.columns = list('abcde')

df1.index = ['cat', 'dog', 'fish', 'bird']

        a   b   c   d   e
cat     0   1   2   3   4
dog     5   6   7   8   9
fish    10  11  12  13  14
bird    15  16  17  18  19

df1.get_value('bird, 'c')

17

现在我需要做的是在`df2`上创建一个全新的列 - 当从`df2`中指定的`animal`,`letter`列索引`df1`时,有效地向量化上面的`pd.get_value`函数。
df2 = pd.DataFrame(np.arange(20).reshape((4, 5)))

df2['animal'] = ['cat', 'dog', 'fish', 'bird']

df2['letter'] = list('abcd')

    0   1   2   3   4   animal  letter
0   0   1   2   3   4   cat     a
1   5   6   7   8   9   dog     b
2   10  11  12  13  14  fish    c
3   15  16  17  18  19  bird    d

导致. . .
    0   1   2   3   4   animal  letter   looked_up
0   0   1   2   3   4   cat     a        0
1   5   6   7   8   9   dog     b        6
2   10  11  12  13  14  fish    c        12
3   15  16  17  18  19  bird    d        18
3个回答

5

弃用通知: lookup 在 v1.2.0 版本中已被弃用

有一个名为lookup的函数可以准确地完成这个任务。

df2['looked_up'] = df1.lookup(df2.animal, df2.letter)

df2
 
    0   1   2   3   4 animal letter  looked_up
0   0   1   2   3   4    cat      a          0
1   5   6   7   8   9    dog      b          6
2  10  11  12  13  14   fish      c         12
3  15  16  17  18  19   bird      d         18

这里锁定是赢家。当有内置时,无法想到其他方法。如果尝试使用 apply,会降低性能。 - Bharath M Shetty
我之前尝试过查找,但在您的回答后发现我的问题出在无法在表格中查找到的值上 - 有没有处理无法查找的值的方法? - Aran Freel
@AranFreel 我想你可以看一下John Galt的答案。 - cs95

4
如果希望更快地处理数据框,可以使用zip,尤其是对于小型数据框。
k = list(zip(df2['animal'].values,df2['letter'].values))
df2['looked_up'] = [df1.get_value(*i) for i in k]

输出:

   0   1   2   3   4 animal letter  looked_up
0   0   1   2   3   4    猫      a          0
1   5   6   7   8   9    狗      b          6
2  10  11  12  13  14   鱼      c         12
3  15  16  17  18  19   鸟      d         18

正如John所建议的那样,您可以简化代码,这样会更快。

 df2['looked_up'] = [df1.get_value(r, c) for r, c in zip(df2.animal, df2.letter)]

如果数据丢失,则使用if else,即:

df2['looked_up'] = [df1.get_value(r, c) if not pd.isnull(c) | pd.isnull(r) else pd.np.nan for r, c in zip(df2.animal, df2.letter) ]

对于小型数据框

%%timeit
df2['looked_up'] = df1.lookup(df2.animal, df2.letter)
1000 loops, best of 3: 801 µs per loop

k = list(zip(df2['animal'].values,df2['letter'].values))
df2['looked_up'] = [df1.get_value(*i) for i in k]
1000 loops, best of 3: 399 µs per loop

[df1.get_value(r, c) for r, c in zip(df2.animal, df2.letter)]
10000 loops, best of 3: 87.5 µs per loop

对于大数据框:

df3 = pd.concat([df2]*10000)

%%timeit
k = list(zip(df3['animal'].values,df3['letter'].values))
df2['looked_up'] = [df1.get_value(*i) for i in k]
1 loop, best of 3: 185 ms per loop


df2['looked_up'] = [df1.get_value(r, c) for r, c in zip(df3.animal, df3.letter)]
1 loop, best of 3: 165 ms per loop

df2['looked_up'] = df1.lookup(df3.animal, df3.letter)
100 loops, best of 3: 8.82 ms per loop

1
还有其他的写法吗? [df1.get_value(r, c) for r, c in zip(df2.animal, df2.letter)] - Zero
这里的 df2.shape 是什么?对于更大的数据,我期望 lookup 更快。 - Zero
确实如此。如果df很小,zip方法是快速的。这里df2.shape为(4,6)。正如我所说,查找是赢家,因为它是内置的。 - Bharath M Shetty

2

lookupget_value是很好的答案,如果你的值存在于查找数据框中。

然而,如果你有(行,列)对没有出现在查找数据框中,并且想要查找值为NaN--mergestack是一种方法来实现它。

In [206]: df2.merge(df1.stack().reset_index().rename(columns={0: 'looked_up'}),
                    left_on=['animal', 'letter'], right_on=['level_0', 'level_1'],
                    how='left').drop(['level_0', 'level_1'], 1)
Out[206]:
    0   1   2   3   4 animal letter  looked_up
0   0   1   2   3   4    cat      a          0
1   5   6   7   8   9    dog      b          6
2  10  11  12  13  14   fish      c         12
3  15  16  17  18  19   bird      d         18

测试添加不存在的 (动物,字母) 对

In [207]: df22
Out[207]:
      0     1     2     3     4 animal letter
0   0.0   1.0   2.0   3.0   4.0    cat      a
1   5.0   6.0   7.0   8.0   9.0    dog      b
2  10.0  11.0  12.0  13.0  14.0   fish      c
3  15.0  16.0  17.0  18.0  19.0   bird      d
4   NaN   NaN   NaN   NaN   NaN  dummy    NaN

In [208]: df22.merge(df1.stack().reset_index().rename(columns={0: 'looked_up'}),
                    left_on=['animal', 'letter'], right_on=['level_0', 'level_1'],
                    how='left').drop(['level_0', 'level_1'], 1)
Out[208]:
      0     1     2     3     4 animal letter  looked_up
0   0.0   1.0   2.0   3.0   4.0    cat      a        0.0
1   5.0   6.0   7.0   8.0   9.0    dog      b        6.0
2  10.0  11.0  12.0  13.0  14.0   fish      c       12.0
3  15.0  16.0  17.0  18.0  19.0   bird      d       18.0
4   NaN   NaN   NaN   NaN   NaN  dummy    NaN        NaN

我使用的方法是找到 df1 列和 df2 列值之间的交集... 从那里,我将 dataframe 分成了对齐和未对齐的两部分... 在对齐的数据帧上使用了 lookup,然后将它们拼接在一起... - Aran Freel

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