Python 带偏移量的numpy结构化阵列视图

Python 带偏移量的numpy结构化阵列视图,python,numpy,casting,structured-array,Python,Numpy,Casting,Structured Array,我有以下numpy结构化数组: In [250]: x Out[250]: array([(22, 2, -1000000000, 2000), (22, 2, 400, 2000), (22, 2, 804846, 2000), (44, 2, 800, 4000), (55, 5, 900, 5000), (55, 5, 1000, 5000), (55, 5, 8900, 5000), (55, 5, 11400, 5000), (33, 3,

我有以下numpy结构化数组:

In [250]: x
Out[250]: 
array([(22, 2, -1000000000, 2000), (22, 2, 400, 2000),
       (22, 2, 804846, 2000), (44, 2, 800, 4000), (55, 5, 900, 5000),
       (55, 5, 1000, 5000), (55, 5, 8900, 5000), (55, 5, 11400, 5000),
       (33, 3, 14500, 3000), (33, 3, 40550, 3000), (33, 3, 40990, 3000),
       (33, 3, 44400, 3000)], 
      dtype=[('f1', '<i4'), ('f2', '<i4'), ('f3', '<i4'), ('f4', '<i4')])
[250]中的
:x
出[250]:
数组([(22,2,-10000000002000),(22,24002000),
(22, 2, 804846, 2000), (44, 2, 800, 4000), (55, 5, 900, 5000),
(55, 5, 1000, 5000), (55, 5, 8900, 5000), (55, 5, 11400, 5000),
(33, 3, 14500, 3000), (33, 3, 40550, 3000), (33, 3, 40990, 3000),
(33, 3, 44400, 3000)], 

dtype=[('f1','f3','f3',下面有点让人困惑-但要点是,这种
视图要工作,它必须能够以规则的数组步长和形状访问字段。从['f1','f3']获取视图失败的原因基本上与
np.ones((12,4))[:,[0,2]]生成副本的原因相同

========

在结构化数组中,每条记录存储为4*‘i4’字节。该布局与(n,4)‘i4’数组兼容:

In [381]: x.__array_interface__['data']    # databuffer pointer
Out[381]: (160925352, False)
In [382]: x.view(('i',4)).__array_interface__['data']
Out[382]: (160925352, False)          # same buffer
In [387]: x.view(('i',4)).shape
Out[387]: (12, 4)
但是当我对这个数组进行不同的切片时

In [383]: x.view(('i',4))[:,[0,1]].__array_interface__['data']
Out[383]: (169894184, False)       # advance indexing - a copy

In [384]: x.view(('i',4))[:,:2].__array_interface__['data']
Out[384]: (160925352, False)       # same buffer
但是选择['f1','f3']相当于:
x.view(('i',4))[:,[0,2]]
,另一个副本

或者看看跨步。前两个领域

In [404]: y2=x.getfield(np.dtype({name: x.dtype.fields[name] for name in ['f1','f2']}))
In [405]: y2.dtype
Out[405]: dtype([('f1', '<i4'), ('f2', '<i4')])
In [406]: y2.strides
Out[406]: (16,)
In [407]: y2.view(('i',2)).strides
Out[407]: (16, 4)
但是将
getfield
与这两个字段一起使用会产生与['f1','f3'相同的错误:

In [388]: y2=x.getfield(np.dtype({name: x.dtype.fields[name] for name in ['f2','f3']})).view(('i',2))
...
ValueError: new type not compatible with array.
视图
无法实现切片可以实现的数据缓冲偏移

========

再次查看中间的两个字段:

In [412]: y2=x.getfield(np.dtype({name: x.dtype.fields[name] for name in ['f2','f3']}))
     ...:  
In [413]: y2
Out[413]: 
array([(2, -1000000000), (2, 400), (2, 804846), (2, 800), (5, 900),
       (5, 1000), (5, 8900), (5, 11400), (3, 14500), (3, 40550),
       (3, 40990), (3, 44400)], 
      dtype={'names':['f2','f3'], 'formats':['<i4','<i4'], 'offsets':[4,8], 'itemsize':12})
In [414]: y2.__array_interface__['data']
Out[414]: (160925352, False)
[412]中的
:y2=x.getfield(np.dtype({name:x.dtype.fields[name]表示['f2','f3']}中的名称))
...:  
In[413]:y2
Out[413]:
数组([(2,-100000000),(2400),(2804846),(2800),(5900),
(5, 1000), (5, 8900), (5, 11400), (3, 14500), (3, 40550),
(3, 40990), (3, 44400)], 

dtype={'names':['f2','f3'],'formats':['以下内容有点让人困惑-但要点是,对于这种
视图
来说,它必须能够以规则的数组步长和形状访问字段。从['f1','f3']获取视图失败的原因基本上与
np.ones((12,4))[:,[0,2]]
生成一份副本

========

在结构化数组中,每条记录存储为4*‘i4’字节。该布局与(n,4)‘i4’数组兼容:

In [381]: x.__array_interface__['data']    # databuffer pointer
Out[381]: (160925352, False)
In [382]: x.view(('i',4)).__array_interface__['data']
Out[382]: (160925352, False)          # same buffer
In [387]: x.view(('i',4)).shape
Out[387]: (12, 4)
但是当我对这个数组进行不同的切片时

In [383]: x.view(('i',4))[:,[0,1]].__array_interface__['data']
Out[383]: (169894184, False)       # advance indexing - a copy

In [384]: x.view(('i',4))[:,:2].__array_interface__['data']
Out[384]: (160925352, False)       # same buffer
但是选择['f1','f3']相当于:
x.view(('i',4))[:,[0,2]]
,另一个副本

或者看看跨步。前两个领域

In [404]: y2=x.getfield(np.dtype({name: x.dtype.fields[name] for name in ['f1','f2']}))
In [405]: y2.dtype
Out[405]: dtype([('f1', '<i4'), ('f2', '<i4')])
In [406]: y2.strides
Out[406]: (16,)
In [407]: y2.view(('i',2)).strides
Out[407]: (16, 4)
但是将
getfield
与这两个字段一起使用会产生与['f1','f3'相同的错误:

In [388]: y2=x.getfield(np.dtype({name: x.dtype.fields[name] for name in ['f2','f3']})).view(('i',2))
...
ValueError: new type not compatible with array.
视图
无法实现切片可以实现的数据缓冲偏移

========

再次查看中间的两个字段:

In [412]: y2=x.getfield(np.dtype({name: x.dtype.fields[name] for name in ['f2','f3']}))
     ...:  
In [413]: y2
Out[413]: 
array([(2, -1000000000), (2, 400), (2, 804846), (2, 800), (5, 900),
       (5, 1000), (5, 8900), (5, 11400), (3, 14500), (3, 40550),
       (3, 40990), (3, 44400)], 
      dtype={'names':['f2','f3'], 'formats':['<i4','<i4'], 'offsets':[4,8], 'itemsize':12})
In [414]: y2.__array_interface__['data']
Out[414]: (160925352, False)
[412]中的
:y2=x.getfield(np.dtype({name:x.dtype.fields[name]表示['f2','f3']}中的名称))
...:  
In[413]:y2
Out[413]:
数组([(2,-100000000),(2400),(2804846),(2800),(5900),
(5, 1000), (5, 8900), (5, 11400), (3, 14500), (3, 40550),
(3, 40990), (3, 44400)], 

数据类型={'names':['f2','f3'],'formats':['Yes,直接使用
ndarray
构造函数:

x = np.array([(22, 2, -1000000000, 2000), 
              (22, 2,         400, 2000),
              (22, 2,      804846, 2000), 
              (44, 2,         800, 4000), 
              (55, 5,         900, 5000), 
              (55, 5,        1000, 5000)], 
             dtype=[('f1','i'),('f2','i'),('f3','i'),('f4','i')])

fields = ['f4', 'f1']
shape = x.shape + (len(fields),)
offsets = [x.dtype.fields[name][1] for name in fields]
assert not any(np.diff(offsets, n=2))
strides = x.strides + (offsets[1] - offsets[0],)
y = np.ndarray(shape=shape, dtype='i', buffer=x,
               offset=offsets[0], strides=strides)
print repr(y)
给出:

array([[2000,   22],
       [2000,   22],
       [2000,   22],
       [4000,   44],
       [5000,   55],
       [5000,   55]])

顺便说一句,当原始数组中的所有字段都具有相同的数据类型时,首先在该数组上创建一个视图,然后再执行切片操作要容易得多。为了获得与上述相同的结果:

y = x.view('i').reshape(x.shape + (-1,))[:,-1::-3]

是,直接使用
ndarray
构造函数:

x = np.array([(22, 2, -1000000000, 2000), 
              (22, 2,         400, 2000),
              (22, 2,      804846, 2000), 
              (44, 2,         800, 4000), 
              (55, 5,         900, 5000), 
              (55, 5,        1000, 5000)], 
             dtype=[('f1','i'),('f2','i'),('f3','i'),('f4','i')])

fields = ['f4', 'f1']
shape = x.shape + (len(fields),)
offsets = [x.dtype.fields[name][1] for name in fields]
assert not any(np.diff(offsets, n=2))
strides = x.strides + (offsets[1] - offsets[0],)
y = np.ndarray(shape=shape, dtype='i', buffer=x,
               offset=offsets[0], strides=strides)
print repr(y)
给出:

array([[2000,   22],
       [2000,   22],
       [2000,   22],
       [4000,   44],
       [5000,   55],
       [5000,   55]])

顺便说一句,当原始数组中的所有字段都具有相同的数据类型时,首先在该数组上创建一个视图,然后再执行切片操作要容易得多。为了获得与上述相同的结果:

y = x.view('i').reshape(x.shape + (-1,))[:,-1::-3]

感谢您提供了非常详细的答案,我本来希望该过程能够处理连续字段,但我也注意到它只适用于包含第一个字段的切片。我知道,如果字段不连续,则会触发高级索引,并返回一个副本。我不明白为什么它不能处理某些连续字段因为这看起来本质上是一个切片。我在f2/f3“切片”上添加了一些信息。两种方法都需要某种偏移量,但它们使用不同的方法。一种方法偏移数据缓冲指针,并从那里使用常规跨步。另一种方法使用
dtype
偏移量和原始数据缓冲指针。非常感谢详细回答,我希望该过程能够处理连续字段,但我也注意到它只适用于包含第一个字段的切片。我知道,如果字段不连续,则会触发高级索引,从而返回副本。我不明白为什么它不能处理某些连续字段,因为这是一个错误ems本质上是一个切片。我在f2/f3“切片”上添加了一些信息。这两种方法都需要某种偏移量,但它们使用不同的方法。一种方法偏移databuffer指针,并从此处使用常规跨距。另一种方法使用
dtype
偏移量和原始databuffer指针。字段是否必须连续r这是否有效?['f4','f1']在我的理解中仍然是连续的。这就是为什么会有断言。请尝试
字段=['f4','f3','f1']
。偏移量是
[12,8,0]
-有一个-4和-8的间隙。
跨步
不能同时处理这两个。@snowleopard完全正确。它总是能处理2个字段,但如果偏移量不是线性间隔的,它会失败3个或更多。感谢您的确认,为了扩展您所说的内容,我注意到它在访问字段触发基本索引时起作用(也就是说,具有一个或多个固定增量的切片),并且在触发高级索引时失败。字段是否必须连续才能工作?['f4','f1']在我的理解中仍然是连续的。这就是断言存在的原因。请尝试
字段=['f4','f3','f1']
。偏移量是
[12,8,0]
-有一个-4和-8的间隙。
跨步
不能同时处理这两个。@snowleopard完全正确。它总是使用2个字段,但如果偏移量不是线性间隔的,则使用3个或更多字段时会失败。感谢您的确认,为了扩展您所说的内容,我注意到当ac