Python Fortran有序(列主)numpy结构化数组是否可能?

Python Fortran有序(列主)numpy结构化数组是否可能?,python,arrays,numpy,recarray,structured-array,Python,Arrays,Numpy,Recarray,Structured Array,我正在寻找一种更有效地分配numpy结构化数组列的方法 my_col = fn_returning_1D_array(...) test['column1'] = fn_returning_1D_array(...) 例如: my_col = fn_returning_1D_array(...) 在我的计算机上执行的速度比对结构化数组列的相同赋值快两倍以上: test = np.ndarray(shape=(int(8e6),), dtype=dtype([('column1', 'S10'

我正在寻找一种更有效地分配numpy结构化数组列的方法

my_col = fn_returning_1D_array(...)
test['column1'] = fn_returning_1D_array(...)
例如:

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


编辑1:回应hpaulj的回答 仅当使用行主顺序创建“正常”数组列指定时,才会产生重新排列列指定和“正常”数组列指定的明显等效性。对于主列排序,这两个赋值远远不相等:

行主修科目

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 [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)

这些不是等价的行动。只需生成一个数组(并将其分配给一个变量,一个次要操作)。另一个生成数组并填充结构化数组的一列

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的
替换它的数据缓冲区,并获得一个有效的结构化数组

In [64]: A3=np.zeros_like(A2)
In [65]: A3.data=A1.data
因此,填充结构化数组的更快方法是进行最快的2D填充,然后进行
数据
赋值

但在一般情况下,挑战在于创建兼容的2D阵列。当所有字段数据类型都相同时,这很容易。对于混合的数据类型,您必须在字节级别工作。有一些高级的
dtype
规范(带有偏移量等),可以促进这种映射


现在,您已经将重点转移到Fortran顺序。对于二维阵列,这确实有帮助。但这样做的代价是以行为导向的操作

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()
的时间测试


在任何情况下,在处理数据类型时都没有任何与Fortran order等价的东西。像
重塑
交换
跨步
、广播等数组操作都没有跨越“数据类型”边界。

my\u col=fn\u returning\u 1D\u array(…)
只是为函数返回的数据分配了一个标签。分配没有复制或分配任何数组数据。@MrE同意。不复制也非常适合列指定。即使在没有复制的情况下无法进行赋值,复制过程也比内存到内存的复制要花费更多的时间。我怀疑这是因为数据不是整体复制的,而是与记录中的其他字段交织在一起。将其与填充
np.empty((int(8e6),许多列),dtype='S10')
@hpaulj的列相比较,这会创建一个“正常”数组(即具有相同的数据类型)。结构化数组允许不同列使用不同的数据类型。我认为结构化数组是元组数组,其中每个元组包含一行中所有字段的值。您正在为每个“元组”分配一个值。它可能比这要精简一些,但这是一般的想法。没有区块复制。感谢您花时间制作此示例。两点意见:1。分配时间是可比较的,因为正如您所提到的,两者的内存布局是相同的。fortran顺序的“普通”数组的计时是完全不同的。2.计时不相等,因为您使用的数组太小,并且您可能会看到不同的python侦听。请参阅我的问题,以说明时间安排。-谢谢你!对,Fortran顺序仅适用于数组的常规维度。数据类型添加的任何额外维度都是独立的,不能进行转置、重新排序或重塑。