我正在寻找一种更有效地分配numpy结构化数组列的方法。
示例:
my_col = fn_returning_1D_array(...)
在我的机器上执行的速度比对结构化数组的相同赋值快两倍以上:
test = np.ndarray(shape=(int(8e6),), dtype=dtype([('column1', 'S10'), ...more columns...]))
test['column1'] = fn_returning_1D_array(...)
我尝试使用fortran命令创建test
,但它没有帮助。据推测,这些字段在内存中保持交错。
有人有任何想法吗?如果他们可以提供帮助,我愿意使用低级numpy接口和cython。
只有在使用行主顺序创建后者时,才会显示重新排列列分配和“正常”数组列分配的明显等效性。对于列主要排序,两个分配远非等效:
行主
In [1]: import numpy as np
In [2]: M,N=int(1e7),10
In [4]: A1=np.zeros((M,N),'f')
In [9]: dt=np.dtype(','.join(['f' for _ in range(N)]))
In [10]: A2=np.zeros((M,),dtype=dt)
In [11]: X=np.arange(M+0.0)
In [13]: %timeit for n in range(N):A1[:,n]=X
1 loops, best of 3: 2.36 s per loop
In [15]: %timeit for n in dt.names: A2[n]=X
1 loops, best of 3: 2.36 s per loop
In [16]: %timeit A1[:,:]=X[:,None]
1 loops, best of 3: 334 ms per loop
In [8]: A1.flags
Out[8]:
C_CONTIGUOUS : True
F_CONTIGUOUS : False
OWNDATA : True
WRITEABLE : True
ALIGNED : True
UPDATEIFCOPY : False
列的主要
In [1]: import numpy as np
In [2]: M,N=int(1e7),10
In [3]: A1=np.zeros((M,N),'f', 'F')
In [4]: dt=np.dtype(','.join(['f' for _ in range(N)]))
In [5]: A2=np.zeros((M,),dtype=dt)
In [6]: X=np.arange(M+0.0)
In [8]: %timeit for n in range(N):A1[:,n]=X
1 loops, best of 3: 374 ms per loop
In [9]: %timeit for n in dt.names: A2[n]=X
1 loops, best of 3: 2.43 s per loop
In [10]: %timeit A1[:,:]=X[:,None]
1 loops, best of 3: 380 ms per loop
In [11]: A1.flags
Out[11]:
C_CONTIGUOUS : False
F_CONTIGUOUS : True
OWNDATA : True
WRITEABLE : True
ALIGNED : True
UPDATEIFCOPY : False
请注意,对于列主要排序,两个缓冲区不再相同:
In [6]: A3=np.zeros_like(A2)
In [7]: A3.data = A1.data
In [20]: A2[0]
Out[20]: (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
In [21]: A2[1]
Out[21]: (1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0)
In [16]: A3[0]
Out[16]: (0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0)
In [17]: A3[1]
Out[17]: (10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0)
答案 0 :(得分:1)
这些不是等效的行为。一个只生成一个数组(并将其分配给变量,一个小动作)。另一个生成数组并填充结构化数组的列。
my_col = fn_returning_1D_array(...)
test['column1'] = fn_returning_1D_array(...)
我认为更公平的比较,将填写2D数组的列。
In [38]: M,N=1000,10
In [39]: A1=np.zeros((M,N),'f') # 2D array
In [40]: dt=np.dtype(','.join(['f' for _ in range(N)]))
In [41]: A2=np.zeros((M,),dtype=dt) # structured array
In [42]: X=np.arange(M+0.0)
In [43]: A1[:,0]=X # fill a column
In [44]: A2['f0']=X # fill a field
In [45]: timeit for n in range(N):A1[:,n]=X
10000 loops, best of 3: 65.3 µs per loop
In [46]: timeit for n in dt.names: A2[n]=X
10000 loops, best of 3: 40.6 µs per loop
我对填充结构化阵列更快感到有点惊讶。
当然,填充2D阵列的快速方法是广播:
In [50]: timeit A1[:,:]=X[:,None]
10000 loops, best of 3: 29.2 µs per loop
但填补田地的改善并不是那么好。
我没有看到按字段填充结构化数组字段的任何重大错误。它必须比生成一个元组列表以填充整个数组更快。
我相信A1
和A2
具有相同的数据缓冲区。例如,如果我制作A2的零副本,我可以用A1's
替换其数据缓冲区,并获得有效的结构化数组
In [64]: A3=np.zeros_like(A2)
In [65]: A3.data=A1.data
因此填充结构化阵列的更快方法是进行最快的2D填充,然后进行data
分配。
但在一般情况下,挑战是创建兼容的2D阵列。当所有字段dtypes都相同时,这很容易。混合使用dtypes时,您必须在字节级别工作。有一些高级dtype
规范(带偏移等),可以促进这种映射。
现在您已将焦点转移到Fortran订单。在2d阵列的情况下,它有所帮助。但它会以牺牲面向行的操作为代价。
In [89]: A1=np.zeros((M,N),'f',order='F')
In [90]: timeit A1[:,:]=X[:,None]
100000 loops, best of 3: 18.2 µs per loop
你没有提到的一件事,至少在最后一次重写之前没有提到过,你打算如何使用这个数组。如果它只是一个按名称存储多个数组的地方,你可以使用一个直接的Python字典:
In [96]: timeit D={name:X.copy() for name in dt.names}
10000 loops, best of 3: 25.2 µs per loop
虽然这确实是X.copy()
的时间测试。
在任何情况下,处理dtypes时都没有任何等效的Fortran命令。像reshape
,swapaxes
,strides
这样的数组操作都不会跨越'dtype'边界进行广播。