我有一个像下面这样的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
答案 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)),但我还没有编写代码,因为它变得无聊,尤其是处理相等情况等等。伪代码如下:
[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