Python 中高效、大规模的比赛评分
Efficient, large-scale competition scoring in Python
考虑包含如下条目的大型分数数据框 S
。每行代表一部分参与者 A
、B
、C
和 D
之间的一场竞赛。
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.1
,B
得分 0.3
,并且等等。
我需要构建一个三角矩阵 C
,其中 C[X,Y]
存储参与者 X
比参与者 Y
好多少。更具体地说,C[X,Y]
将在 X
和 Y
之间保持 mean % 的分数差异。
来自上面的例子:
C[A,B] = 100 * ((0.1 - 0.3)/0.3 + (1 - 0.2)/0.2) = 33%
我的矩阵 S
很大,所以我希望利用 JIT(Numba?)或 numpy
或 pandas
中的内置方法。我当然想避免嵌套循环,因为 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
考虑包含如下条目的大型分数数据框 S
。每行代表一部分参与者 A
、B
、C
和 D
之间的一场竞赛。
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.1
,B
得分 0.3
,并且等等。
我需要构建一个三角矩阵 C
,其中 C[X,Y]
存储参与者 X
比参与者 Y
好多少。更具体地说,C[X,Y]
将在 X
和 Y
之间保持 mean % 的分数差异。
来自上面的例子:
C[A,B] = 100 * ((0.1 - 0.3)/0.3 + (1 - 0.2)/0.2) = 33%
我的矩阵 S
很大,所以我希望利用 JIT(Numba?)或 numpy
或 pandas
中的内置方法。我当然想避免嵌套循环,因为 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