在 numpy 中转换一组数字,以便每个数字转换成小于它的多个其他数字
- 2025-04-15 09:16:00
- admin 原创
- 18
问题描述:
考虑一组数字:
In [8]: import numpy as np
In [9]: x = np.array([np.random.random() for i in range(10)])
In [10]: x
Out[10]:
array([ 0.62594394, 0.03255799, 0.7768568 , 0.03050498, 0.01951657,
0.04767246, 0.68038553, 0.60036203, 0.3617409 , 0.80294355])
现在我想把这个集合转换成另一个集合y
,方法如下:对于i
中的每个元素,中的x
对应元素就是 中小于 的其他元素的数量。例如,上面给出的集合如下所示:j
`yx
i`x
In [25]: y
Out[25]: array([ 6., 2., 8., 1., 0., 3., 7., 5., 4., 9.])
现在,我可以使用简单的 Python 循环来完成此操作:
In [16]: for i in range(len(x)):
...: tot = 0
...: for j in range(len(x)):
...: if x[i] > x[j]: tot += 1
...: y[i] = int(tot)
然而,当 的长度x
非常大时,代码会变得非常慢。我想知道是否有任何 Numpy 魔法可以解决这个问题。例如,如果我必须过滤所有小于 的元素0.5
,我只需使用布尔掩码即可:
In [19]: z = x[x < 0.5]
In [20]: z
Out[20]: array([ 0.03255799, 0.03050498, 0.01951657, 0.04767246, 0.3617409 ])
是否可以使用类似的东西来更快地实现同样的事情?
解决方案 1:
您实际上需要做的是获取数组排序顺序的逆序:
import numpy as np
x = np.random.rand(10)
y = np.empty(x.size,dtype=np.int64)
y[x.argsort()] = np.arange(x.size)
示例运行(在 ipython 中):
In [367]: x
Out[367]:
array([ 0.09139335, 0.29084225, 0.43560987, 0.92334644, 0.09868977,
0.90202354, 0.80905083, 0.4801967 , 0.99086213, 0.00933582])
In [368]: y
Out[368]: array([1, 3, 4, 8, 2, 7, 6, 5, 9, 0])
或者,如果你想获取大于中每个对应元素的元素数量x
,则必须将排序从升序反转为降序。一种可行的方法是简单地交换索引的构造:
y_rev = np.empty(x.size,dtype=np.int64)
y_rev[x.argsort()] = np.arange(x.size)[::-1]
另一个方法是将原始数组映射到新数组:
y_rev = x.size - y - 1
解决方案 2:
以下是使用以下方法的一种方法np.searchsorted
-
np.searchsorted(np.sort(x),x)
另一个主要基于@Andras Deak's solution
使用argsort()
-
x.argsort().argsort()
样本运行 -
In [359]: x
Out[359]:
array([ 0.62594394, 0.03255799, 0.7768568 , 0.03050498, 0.01951657,
0.04767246, 0.68038553, 0.60036203, 0.3617409 , 0.80294355])
In [360]: np.searchsorted(np.sort(x),x)
Out[360]: array([6, 2, 8, 1, 0, 3, 7, 5, 4, 9])
In [361]: x.argsort().argsort()
Out[361]: array([6, 2, 8, 1, 0, 3, 7, 5, 4, 9])
解决方案 3:
除了其他答案之外,使用布尔索引的另一种解决方案可能是:
sum(x > i for i in x)
例如:
In [10]: x
Out[10]:
array([ 0.62594394, 0.03255799, 0.7768568 , 0.03050498, 0.01951657,
0.04767246, 0.68038553, 0.60036203, 0.3617409 , 0.80294355])
In [10]: y = sum(x > i for i in x)
In [11]: y
Out[10]: array([6, 2, 8, 1, 0, 3, 7, 5, 4, 9])
解决方案 4:
我想通过对@Andras Deak 的解决方案进行一些测试来为这篇文章做出贡献argsort
。
对于短数组来说,似乎argsort
again 更快。简单的想法是评估我们看到平衡偏移的数组的长度。
我将定义三个函数
construct
这是 Andras Deak 的解决方案argsortagain
这是显而易见的attempted_optimal
这权衡了len(a) == 400
函数
def argsortagain(s):
return s.argsort()
def construct(s):
u = np.empty(s.size, dtype=np.int64)
u[s] = np.arange(s.size)
return u
def attempted_optimal(s):
return argsortagain(s) if len(s) < 400 else construct(s)
测试
results = pd.DataFrame(
index=pd.RangeIndex(10, 610, 10, 'len'),
columns=pd.Index(['construct', 'argsortagain', 'attempted_optimal'], name='function'))
for i in results.index:
a = np.random.rand(i)
s = a.argsort()
for j in results.columns:
results.set_value(
i, j,
timeit(
'{}(s)'.format(j),
'from __main__ import {}, s'.format(j),
number=10000)
)
results.plot()
结论
attempted_optimal
它确实完成了它该做的事情。但我不确定它在阵列长度(低于 400)范围内获得的边际效益是否值得,因为在这种范围内,阵列长度几乎无关紧要。我完全支持constructed
只使用一个。
这个分析帮助我得出了这个结论。
扫码咨询,免费领取项目管理大礼包!