如何将一个系列值逐个与另一个整个系列进行比较

时间:2018-06-13 09:15:46

标签: python pandas

我有一个像下面这样的pandas数据框,

   col1  col2
0    12     1
1     1     7
2    54    17
3    11   191
4     3    39
5    76     2
6    18     6

生成df的代码:

df=pd.DataFrame({'col1':[12,1,54,11,3,76,18],'col2':[1,7,17,191,39,2,6]})

我想将col1值逐个与完整的col2系列进行比较。 即,将12与col2进行比较,在col2中找到小于12并计算值,然后对1执行相同的操作,然后对54执行相同操作,然后将结果存储在另一个系列中。

到目前为止,我尝试过如下,

df['res']=df.apply(lambda x:len(df[df['col2']<x['col1']]),axis=1)

它按我的预期工作。但是,当系列很大时,解决这个问题的方法效果很差。

我需要有效的方法来解决这个问题。因为实际数据集包含超过百万条记录。

预期产出:

   col1  col2  res
0    12     1    4
1     1     7    0
2    54    17    6
3    11   191    4
4     3    39    2
5    76     2    6
6    18     6    5

2 个答案:

答案 0 :(得分:5)

以下使用numpy(通过使用广播隐式将向量扩展到矩阵)并且比您提出的答案工作得快得多:

df['res'] = (df['col2'].values.reshape(1,-1) < df['col1'].values.reshape(-1,1)).sum(axis=1)

(在10k行的测试df中,在我的机器上需要0.3s而不是8s)。然而,它在行数中使用二次记忆,所以如果你的df有数百万行并不是很好......

[编辑] O(n * log(n))(n是行数)的解决方案在时间和空间上都可能接近最优(上面是O(n ^ 2)in两者,在C中实现它将在时间上是O(n ^ 2)但在空间中只有O(n)),但我还没有编写代码,因为它变得无聊,尤其是处理相等情况等等。伪代码如下:

  • 对col1进行排序并获取其索引。说,这给你一个词典原始索引 - &gt;排序索引。
  • 对并置的矢量[col1,col2]进行排序并获取索引。这给出了另一个映射,原始索引 - &gt;排序索引。
  • 答案应该是第二个矢量减去第一个矢量的差异。

[EDIT2]:实现它实际上比我想象的容易得多,它只是:

idx1 = np.argsort(np.argsort(df['col1'], kind='mergesort'), kind='mergesort')
idx2 = np.argsort(np.argsort(np.concatenate((df['col1'], df['col2'])), kind='mergesort'), kind='mergesort')[:len(idx1)]
df['res'] = idx2-idx1

如上所述,这在时间和空间上都只是O(n * log(n)),因此即使使用大df也只需要很少的时间(100k行为0.1s,1M行为1.5s)额外的空间。

双argsort是因为numpy排序约定,np.argsort没有给出排序向量中元素的索引,而是索引使得x [idx]被排序。两次执行argsort的小技巧给出了原始元素在排序向量中的位置。我添加了那种=&#39; mergesort&#39;使用稳定排序,这本身就没用了,但是如果一个值出现在col1和col2中就应该解决问题(这是因为我们想要计算当col2是&lt; col1;如果我们想要&lt; =,那么在连接中col2应该在col1之前。

答案 1 :(得分:1)

使用 np.less 逻辑函数的替代解决方案:

In [119]: vals = df['col2'].values
In [120]: df['res'] = df.apply(lambda x: np.less(vals, x['col1']).sum(), axis=1)

In [121]: df
Out[121]: 
   col1  col2  res
0    12     1    4
1     1     7    0
2    54    17    6
3    11   191    4
4     3    39    2
5    76     2    6
6    18     6    5

效果比较:

In [122]: %timeit df['res'] = df.apply(lambda x: np.less(vals, x['col1']).sum(), axis=1)
2.09 ms ± 308 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [123]: %timeit df['res']=df.apply(lambda x:len(df[df['col2']<x['col1']]),axis=1)
8.57 ms ± 132 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [124]: %timeit df['res'] = (df['col2'].values.reshape(1,-1) < df['col1'].values.reshape(-1,1)).sum(axis=1)
420 µs ± 26.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

https://docs.scipy.org/doc/numpy-1.14.0/reference/generated/numpy.less.html#numpy.less