python - 如何将一个系列值与另一个整个系列一一比较
问题描述
我有一个如下所示的熊猫数据框,
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
解决方案
以下使用 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 之前)。
推荐阅读
- c - 我在 C 二进制搜索代码中做错了什么?(迭代和递归)
- laravel - Laravel Eloquent 返回具有所有关系的模型
- jquery - Jquery加载并从加载和刷新中获取值
- azure-eventhub - 多个 EventGrid 订阅指向同一个端点
- html - 如何在页面布局中正确添加形状/装饰
- javascript - 我收到使用节点命令无法识别的 .html 扩展名错误
- mysql - 验证 SQL Server 表中的迁移记录
- google-sheets - 如何在堆积条形图中使用数据范围公式 - 谷歌表格
- jestjs - AWS CDK Jest 单元测试资源具有 DeletionPolicy
- python-pptx - python-pptx:如何在新插入的幻灯片中查询文本占位符的“默认”值