Python 中高效、大规模的比赛评分

Efficient, large-scale competition scoring in Python

考虑包含如下条目的大型分数数据框 S。每行代表一部分参与者 ABCD 之间的一场竞赛。

 A     B    C   D
0.1  0.3  0.8    1
  1  0.2  NaN  NaN
0.7  NaN    2  0.5
NaN   4   0.6  0.8

阅读上面矩阵的方法是:查看第一行,参与者 A 在该轮中得分 0.1B 得分 0.3,并且等等。

我需要构建一个三角矩阵 C,其中 C[X,Y] 存储参与者 X 比参与者 Y 好多少。更具体地说,C[X,Y] 将在 XY 之间保持 mean % 的分数差异。

来自上面的例子:

C[A,B] = 100 * ((0.1 - 0.3)/0.3 + (1 - 0.2)/0.2) = 33%

我的矩阵 S 很大,所以我希望利用 JIT(Numba?)或 numpypandas 中的内置方法。我当然想避免嵌套循环,因为 S 有数百万行。

上面的高效算法有名字吗?

让我们看一下基于 NumPy 的解决方案,因此我们假设输入数据位于名为 a 的数组中。现在,4 个此类变量的成对组合数将为 4*3/2 = 6。我们可以用 np.triu_indices(). Then, we index into the columns of a with those indices. We perform the subtractions and divisions and simply add the columns ignoring the NaN affected results with np.nansum() 为所需的输出生成与此类组合对应的 ID。

因此,我们会有这样的实现 -

R,C = np.triu_indices(a.shape[1],1)
out = 100*np.nansum((a[:,R] - a[:,C])/a[:,C],0)

示例 运行 -

In [121]: a
Out[121]: 
array([[ 0.1,  0.3,  0.8,  1. ],
       [ 1. ,  0.2,  nan,  nan],
       [ 0.7,  nan,  2. ,  0.5],
       [ nan,  4. ,  0.6,  0.8]])

In [122]: out
Out[122]: 
array([ 333.33333333, -152.5       ,  -50.        ,  504.16666667,
        330.        ,  255.        ])

In [123]: 100 * ((0.1 - 0.3)/0.3 + (1 - 0.2)/0.2) # Sample's first o/p elem
Out[123]: 333.33333333333337

如果你需要输出为(4,4)数组,我们可以使用Scipy's squareform -

In [124]: from scipy.spatial.distance import squareform

In [125]: out2D = squareform(out)

让我们转换为 pandas 数据框以获得良好的视觉反馈 -

In [126]: pd.DataFrame(out2D,index=list('ABCD'),columns=list('ABCD'))
Out[126]: 
            A           B           C    D
A    0.000000  333.333333 -152.500000  -50
B  333.333333    0.000000  504.166667  330
C -152.500000  504.166667    0.000000  255
D  -50.000000  330.000000  255.000000    0

让我们手动计算 [B,C] 并返回查看 -

In [127]: 100 * ((0.3 - 0.8)/0.8 + (4 - 0.6)/0.6)
Out[127]: 504.1666666666667