数组或列表中元素的Python成对比较

Posted

技术标签:

【中文标题】数组或列表中元素的Python成对比较【英文标题】:Python pairwise comparison of elements in a array or list 【发布时间】: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)

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

【问题讨论】:

【参考方案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) 是的。我两个都需要。

以上是关于数组或列表中元素的Python成对比较的主要内容,如果未能解决你的问题,请参考以下文章

JS数组方法与python列表方法的比较

元素成对比较的高效算法

如何用O(nlogn)或O(n)的时间复杂度在python中解决对和问题?

python pop() ,如何在Python的列表或数组中移除元素

将一个数字与 Python 中列表(或数组)中的每个元素相加

位运算-查找数组中唯一成对的数