首页 > 解决方案 > 如何将一个系列值与另一个整个系列一一比较

问题描述

我有一个如下所示的熊猫数据框,

   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

标签: pythonpandas

解决方案


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

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

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

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

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

[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 的小技巧给出了原始元素在排序向量中的位置。我添加了 kind='mergesort' 来使用稳定的排序,这本身没什么用,但是如果 col1 和 col2 中都出现了一个值,应该可以解决问题(这是因为我们想在 col2 < col1 时计数;如果我们想要 < =,那么在连接中 col2 应该在 col1 之前)。


推荐阅读