【问题标题】:How to compare one series values one by one with another whole series如何将一个系列值与另一个整个系列一一比较
【发布时间】:2018-06-13 09:15:45
【问题描述】:

我有一个如下所示的 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

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    以下使用 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

    【讨论】:

    • apply 解决方案好 100 倍。好一个
    • 感谢您的回答。它真的很快。对于这个数据框df=pd.DataFrame(np.random.randint(0,100,size=(10**5,2)),columns=['col1','col2']),它给出了内存错误。你知道为什么吗?任何方式+1
    • 正如我所说,它在内存中是二次的。您正在使用 10^5 行,即 10^5^2 = 10^10 = 100 亿浮点 = 10GB。如果您只有 4 或 8 GB 内存,您可能会收到错误(取决于您的交换)
    • @MohamedThasinah 希望更新后的答案能给出您想要的内容:-) 一些检查可能仍然有助于确保在存在重复值时它可以正常工作,但在我非常有限的测试中它有效正如预期的那样。
    【解决方案2】:

    具有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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-09-15
      • 2018-06-02
      • 2019-02-22
      • 1970-01-01
      • 1970-01-01
      • 2021-11-06
      • 1970-01-01
      相关资源
      最近更新 更多