【问题标题】:Python pairwise comparison of elements in a array or list数组或列表中元素的Python成对比较
【发布时间】:2017-01-21 14:50:56
【问题描述】:

让我用一个简单的例子来详细说明我的问题。我有 a=[a1,a2,a3,a4],所有的 ai 都是一个数值。

我想要得到的是“a”内的成对比较,例如 I(a1>=a2), I(a1>=a3), I(a1>=a4), ,,,,I(a4>=a1), I(a4>=a2), I(a4>=a3 ),其中 I 是指示函数。所以我使用了以下代码。

res=[x>=y for x in a for y in a]

但它也给出了比较结果,例如 I(a1>=a1),..,I(a4>=a4),它始终为 1。为了摆脱这些麻烦,我将 res 转换为一个 numpy 数组并找到对角线以外的元素。

res1=numpy.array(res)

这给出了我想要的结果,但我认为应该有更有效或更简单的方法来进行成对比较并提取非对角线元素。你对此有什么想法吗?提前致谢。

【问题讨论】:

    标签: python arrays numpy comparison rank


    【解决方案1】:

    我想将@Divakar 的解决方案应用于熊猫对象。这里有两种计算成对绝对差的方法。

    (Python 3.6.2 上的 IPython 6.1.0)

    In [1]: import pandas as pd
       ...: import numpy as np
       ...: import itertools
    
    In [2]: n = 256
       ...: labels = range(n)
       ...: ser = pd.Series(np.random.randn(n), index=labels)
       ...: ser.head()
    Out[2]: 
    0    1.592248
    1   -1.168560
    2   -1.243902
    3   -0.133140
    4   -0.714133
    dtype: float64
    

    循环

    In [3]: %%time
       ...: result = dict()
       ...: for pair in itertools.combinations(labels, 2):
       ...:     a, b = pair
       ...:     a = ser[a]  # retrieve values
       ...:     b = ser[b]
       ...:     result[pair] = a - b
    
       ...: result = pd.Series(result).abs().reset_index()
       ...: result.columns = list('ABC')
       ...: df1 = result.pivot('A', 'B, 'C').reindex(index=labels, columns=labels)
       ...: df1 = df1.fillna(df1.T).fillna(0.)
    CPU times: user 18.2 s, sys: 468 ms, total: 18.7 s
    Wall time: 18.7 s
    

    NumPy 广播

    In [4]: %%time
       ...: arr = ser.values
       ...: arr = arr[:, None] - arr
       ...: df2 = pd.DataFrame(arr, labels, labels).abs()
    CPU times: user 816 µs, sys: 432 µs, total: 1.25 ms
    Wall time: 675 µs
    

    验证它们是否相等:

    In [5]: df1.equals(df2)
    Out[5]: True
    

    使用循环比聪明的 NumPy 方法慢大约 20000 倍。 NumPy 有很多优化,但有时它们需要不同的思维方式。 :-)

    【讨论】:

      【解决方案2】:

      你可以使用NumPy broadcasting -

      # Get the mask of comparisons in a vectorized manner using broadcasting
      mask = a[:,None] >= a
      
      # Select the elements other than diagonal ones
      out = mask[~np.eye(a.size,dtype=bool)]
      

      如果您更愿意在mask 中将对角元素设置为False,然后mask 将是输出,就像这样 -

      mask[np.eye(a.size,dtype=bool)] = 0
      

      示例运行 -

      In [56]: a
      Out[56]: array([3, 7, 5, 8])
      
      In [57]: mask = a[:,None] >= a
      
      In [58]: mask
      Out[58]: 
      array([[ True, False, False, False],
             [ True,  True,  True, False],
             [ True, False,  True, False],
             [ True,  True,  True,  True]], dtype=bool)
      
      In [59]: mask[~np.eye(a.size,dtype=bool)] # Selecting non-diag elems
      Out[59]: 
      array([False, False, False,  True,  True, False,  True, False, False,
              True,  True,  True], dtype=bool)
      
      In [60]: mask[np.eye(a.size,dtype=bool)] = 0 # Setting diag elems as False
      
      In [61]: mask
      Out[61]: 
      array([[False, False, False, False],
             [ True, False,  True, False],
             [ True, False, False, False],
             [ True,  True,  True, False]], dtype=bool)
      

      运行时测试

      使用NumPy broadcasting 的原因?表现!让我们看看如何处理大型数据集 -

      In [34]: def pairwise_comp(A): # Using NumPy broadcasting    
          ...:     a = np.asarray(A) # Convert to array if not already so
          ...:     mask = a[:,None] >= a
          ...:     out = mask[~np.eye(a.size,dtype=bool)]
          ...:     return out
          ...: 
      
      In [35]: a = np.random.randint(0,9,(1000)).tolist() # Input list
      
      In [36]: %timeit [x >= y for i,x in enumerate(a) for j,y in enumerate(a) if i != j]
      1 loop, best of 3: 185 ms per loop # @Sixhobbits's loopy soln
      
      In [37]: %timeit pairwise_comp(a)
      100 loops, best of 3: 5.76 ms per loop
      

      【讨论】:

      • 看起来很棒!欣赏它。
      【解决方案3】:

      您为什么担心a1>=a1 比较。它可能是可预测的,但跳过它可能不值得额外的工作。

      列出 100 个数字

      In [17]: a=list(range(100))
      

      将它们与简单的双循环进行比较;产生 10000 个值 (100*100)

      In [18]: len([x>=y for x in a for y in a])
      Out[18]: 10000
      In [19]: timeit [x>=y for x in a for y in a]
      1000 loops, best of 3: 1.04 ms per loop
      

      现在使用 @Moinuddin Quadri's 枚举循环跳过 100 个 eye 值:

      In [20]: len([x>=y for i,x in enumerate(a) for j, y in enumerate(a) if i!=j])
      Out[20]: 9900
      In [21]: timeit [x>=y for i,x in enumerate(a) for j, y in enumerate(a) if i!=j]
      100 loops, best of 3: 2.12 ms per loop
      

      需要 2 倍的时间。一半的额外时间是枚举,一半是if

      在这种情况下,使用 numpy 数组会快得多,即使包括创建数组的时间也是如此。

      xa = np.array(x); Z = xa[:,None]>=xa
      

      但是你不能摆脱对角线的值。他们将True;它们可以翻转到False,但是为什么。在一个布尔数组中只有 2 个值。

      最快的解决方案是编写一个不受这些对角线值影响的指标函数。

      【讨论】:

        【解决方案4】:

        您可以通过以下方式实现:

        [x >= y for i,x in enumerate(a) for j,y in enumerate(a) if i != j]
        

        您的代码有问题

        您在列表中迭代两次。如果您将comprehension 转换为loop,它将像这样工作:

        for x in a:
            for y in a:
                x>=y # which is your condition
        

        因此,执行顺序为:(a1, a1), (a1, a2), ... , (a2, a1), (a2, a2), ... , (a4, a4 )

        【讨论】:

          【解决方案5】:

          也许你想要:

           [x >= y for i,x in enumerate(a) for j,y in enumerate(a) if i != j]
          

          这不会将任何项目与自身进行比较,而是将每个项目相互比较。

          【讨论】:

          • [x >= y for i,x in enumerate(a) for j,y in enumerate(a) if i > j]怎么样
          • @Jean-FrançoisFabre OP 也希望得到相反的结果:I(a1>=a4)I(a4>=a1)
          • 是的。我两个都需要。
          猜你喜欢
          • 1970-01-01
          • 2015-08-01
          • 1970-01-01
          • 2020-09-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多