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

时间:2016-09-13 21:54:23

标签: python arrays numpy comparison rank

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

我想得到的是'a'中的成对比较,例如 I(a1> = a2),I(a1> = a3),I(a1> = a4),,,, I(a4> = a1),I(a4> = a2),I(a4> = a3) ),我在哪里是一个指标功能。所以我使用了以下代码。

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

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

res1=numpy.array(res)

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

5 个答案:

答案 0 :(得分:7)

您可以使用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)]

如果您更愿意在False中将对角元素设置为mask,那么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

答案 1 :(得分:5)

也许你想要:

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

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

答案 2 :(得分:1)

您可以通过使用:

来实现这一目标
[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 )

答案 3 :(得分:1)

为什么你担心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 :(得分:1)

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

(Python 3.6.2上的IPython 6.1.0)

@OnOpen
public void open(final Session session, @PathParam("room") final String room) {
    Principal userPrincipal = session.getUserPrincipal();
    if (userPrincipal == null) {
        log.info("The user is not logged in.");
    } else {
        String user = userPrincipal.getName();
        // now that your have your user here, your can do whatever other operation you want.
    }
}

循环

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

NumPy广播

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

验证它们是否相等:

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

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