Python 在numpy中按行获取范围

Python 在numpy中按行获取范围,python,numpy,Python,Numpy,我有一个生成如下数组的函数: my_array = np.array([list(str(i).zfill(4)) for i in range(10000)], dtype=int) 哪些产出: array([[0, 0, 0, 0], [0, 0, 0, 1], [0, 0, 0, 2], ..., [9, 9, 9, 7], [9, 9, 9, 8], [9, 9, 9, 9]]) 通过将ints转换为

我有一个生成如下数组的函数:

my_array = np.array([list(str(i).zfill(4)) for i in range(10000)], dtype=int)
哪些产出:

array([[0, 0, 0, 0],
       [0, 0, 0, 1],
       [0, 0, 0, 2],
       ...,
       [9, 9, 9, 7],
       [9, 9, 9, 8],
       [9, 9, 9, 9]])
通过将
int
s转换为字符串和列表,然后返回到
int
,您可以看到,这是非常低效的,我真正需要的是更大的数组(更大的范围)。我试图研究numpy以找到一种更有效的方法来生成这个数组/列表,但没有找到一种方法。到目前为止,我得到的最好的结果是
arange
,它将给出1到9999之间的范围,但不分为列表


有什么想法吗?

我会结合使用
np.tile
np.repeat
并尝试组合行,然后将它们组合起来

这种纯Numpy解决方案几乎变成了一条直线,然后:

n = 10000

x = np.arange(10)

a = [np.tile(np.repeat(x, 10 ** k), n/(10 ** (k+1))) for k in range(int(np.log10(n)))]

y = np.column_stack(a[::-1]) # flip the list, first entry is rightmost row
一个更详细的版本,看看会发生什么,可以这样写

n = 10000

x = np.arange(10)

x0 = np.tile(np.repeat(x, 1), n/10)
x1 = np.tile(np.repeat(x, 10), n/100)
x2 = np.tile(np.repeat(x, 100), n/1000)
现在用指数替换数字,并使用log10获得列数

速度测试:

import timeit

s = """
    n = 10000
    x = np.arange(10)
    a = [np.tile(np.repeat(x, 10 ** k), n/(10 ** (k+1))) for k in range(int(np.log10(n)))]
    y = np.column_stack(a[::-1])
    """
n_runs = 100000
t = timeit.timeit(s,
                  "import numpy as np",
                  number=n_runs)

print(t, t/n_runs)
在我的慢速机器(7岁)上大约260µs。

您可以使用它。 只需提供
range(10)
作为参数,并提供所需的位数作为
repeat
的参数

方便的是,itertools迭代器按排序顺序返回元素,因此您不必自己执行第二个排序步骤

下面是对我的代码的评估:

import timeit


if __name__ == "__main__":
    # time run: 14.20635
    print(timeit.timeit("np.array([list(str(i).zfill(4)) for i in range(10000)], dtype=int)",
                  "import numpy as np",
                  number=1000))

    # time run: 5.00319
    print(timeit.timeit("np.array(list(itertools.product(range(10), r=4)))",
                        "import itertools; import numpy as np",
                        number=1000))
这是一个基于-

样本运行-

In [116]: cartesian_product_ranges([3,2,4])
Out[116]: 
array([[0, 0, 0],
       [0, 0, 1],
       [0, 0, 2],
       [0, 0, 3],
       [0, 1, 0],
       [0, 1, 1],
       [0, 1, 2],
       [0, 1, 3],
       [1, 0, 0],
       [1, 0, 1],
       [1, 0, 2],
       [1, 0, 3],
       [1, 1, 0],
       [1, 1, 1],
       [1, 1, 2],
       [1, 1, 3],
       [2, 0, 0],
       [2, 0, 1],
       [2, 0, 2],
       [2, 0, 3],
       [2, 1, 0],
       [2, 1, 1],
       [2, 1, 2],
       [2, 1, 3]])
使用
4
cols在
10范围
阵列上运行和计时-

In [119]: cartesian_product_ranges([10]*4)
Out[119]: 
array([[0, 0, 0, 0],
       [0, 0, 0, 1],
       [0, 0, 0, 2],
       ...,
       [9, 9, 9, 7],
       [9, 9, 9, 8],
       [9, 9, 9, 9]])

In [120]: cartesian_product_ranges([10]*4).shape
Out[120]: (10000, 4)

In [121]: %timeit cartesian_product_ranges([10]*4)
10000 loops, best of 3: 105 µs per loop

In [122]: %timeit np.array([list(str(i).zfill(4)) for i in range(10000)], dtype=int)
100 loops, best of 3: 16.7 ms per loop

In [123]: 16700.0/105
Out[123]: 159.04761904761904
大约
160x
加速

对于包含
9列的
10个范围
数组,我们可以使用精度较低的
uint8
dtype-

In [7]: %timeit cartesian_product_ranges([10]*9, out_dtype=np.uint8)
1 loop, best of 3: 3.36 s per loop

快速解决方案是使用
np.meshgrid
创建所有列。然后对实例元素123或1234上的列进行排序,以便它们的顺序正确。然后用它们做一个数组

n_digits = 4
digits = np.arange(10)
columns = [c.ravel() for c in np.meshgrid(*[digits]*n_digits)]
out_array = columns.sort(key=lambda x: x[int("".join(str(d) for d in range(n_digits)))])
out_array = np.array(columns).T
np.all(out_array==my_array)

还有其他一行程序可以解决这个问题

import numpy as np
y = np.array([index for index in np.ndindex(10, 10, 10, 10)])
这似乎要慢得多


这似乎比公认的答案稍慢。

非常好的解决方案,大约快3倍:)谢谢。一般来说,如果你想提高性能,itertools是最好的选择!通常,为了性能,您会牺牲一些可读性,但在您的情况下,这也不是什么大问题!还要注意的是,这将比您提供的方法(例如,使用10k元素)的伸缩性稍好一些。这就是我现在正在测试的,因为我正在寻找的解决方案必须是9位数字(而不是4位),所以我正在尝试不同之处。遗憾的是,尝试使用9位数字会导致
内存错误
,这很有意义。。。我试着用其他方法来测试你是对的。我只是在一台48GB内存的机器上试过,但仍然不足以完成运行。对8位数字进行同样的操作,可在约100秒内运行。+1表示疯狂加速。当运行的列太多时,也会立即给出一个
MemoryError
,这样它就不会像我的解决方案那样溢出。@Denninger检查编辑,并在文章末尾编辑9列。哇,这真是疯狂的加速,尽管我对代码本身不太了解。。。但这是我要学习的:)谢谢你的帮助!选择这个答案纯粹是为了疯狂的速度,尽管我喜欢其他人想出的所有不同的答案和方法。这很好。你的答案肯定是这里速度最快的答案之一,我选择了其他答案之一只是为了疯狂的速度,但是这也提供了一些可读性,我和其他使用代码的人可以更好地理解。你比较了你机器上的答案的速度吗?我比较了,是的,也是一台旧机器。顺便说一句,你的是一台紧随其后的机器。argh:)让我们看看是否有优化的潜力
import numpy as np
y = np.array([index for index in np.ndindex(10, 10, 10, 10)])
import numpy as np
from sklearn.utils.extmath import cartesian

x = np.arange(10)
y = cartesian((x, x, x, x))