有多種方法可以為結(jié)構(gòu)化數(shù)組賦值:使用 python 元組、使用標(biāo)量值或使用其他結(jié)構(gòu)化數(shù)組。
為結(jié)構(gòu)化數(shù)組賦值的最簡單方法是使用 python 元組。每個分配的值應(yīng)該是一個長度等于數(shù)組中字段數(shù)的元組,而不是列表或數(shù)組,因為它們會觸發(fā) numpy 的廣播規(guī)則。元組的元素被分配給數(shù)組的連續(xù)字段,從左到右:
>>> x = np.array([(1, 2, 3), (4, 5, 6)], dtype='i8, f4, f8')
>>> x[1] = (7, 8, 9)
>>> x
array([(1, 2., 3.), (7, 8., 9.)],
dtype=[('f0', '<i8'), ('f1', '<f4'), ('f2', '<f8')])
分配給結(jié)構(gòu)化元素的標(biāo)量將分配給所有字段。當(dāng)將標(biāo)量分配給結(jié)構(gòu)化數(shù)組或?qū)⒎墙Y(jié)構(gòu)化數(shù)組分配給結(jié)構(gòu)化數(shù)組時,就會發(fā)生這種情況:
>>> x = np.zeros(2, dtype='i8, f4, ?, S1')
>>> x[:] = 3
>>> x
array([(3, 3., True, b'3'), (3, 3., True, b'3')],
dtype=[('f0', '<i8'), ('f1', '<f4'), ('f2', '?'), ('f3', 'S1')])
>>> x[:] = np.arange(2)
>>> x
array([(0, 0., False, b'0'), (1, 1., True, b'1')],
dtype=[('f0', '<i8'), ('f1', '<f4'), ('f2', '?'), ('f3', 'S1')])
結(jié)構(gòu)化數(shù)組也可以分配給非結(jié)構(gòu)化數(shù)組,但前提是結(jié)構(gòu)化數(shù)據(jù)類型只有一個字段:
>>> twofield = np.zeros(2, dtype=[('A', 'i4'), ('B', 'i4')])
>>> onefield = np.zeros(2, dtype=[('A', 'i4')])
>>> nostruct = np.zeros(2, dtype='i4')
>>> nostruct[:] = twofield
Traceback (most recent call last):
...
TypeError: Cannot cast array data from dtype([('A', '<i4'), ('B', '<i4')]) to dtype('int32') according to the rule 'unsafe'
兩個結(jié)構(gòu)化數(shù)組之間的賦值就像將源元素轉(zhuǎn)換為元組然后賦值給目標(biāo)元素一樣。也就是說,源數(shù)組的第一個字段被分配給目標(biāo)數(shù)組的第一個字段,第二個字段同樣如此,以此類推,無論字段名稱如何。具有不同字段數(shù)的結(jié)構(gòu)化數(shù)組不能相互分配。未包含在任何字段中的目標(biāo)結(jié)構(gòu)字節(jié)不受影響。
>>> a = np.zeros(3, dtype=[('a', 'i8'), ('b', 'f4'), ('c', 'S3')])
>>> b = np.ones(3, dtype=[('x', 'f4'), ('y', 'S3'), ('z', 'O')])
>>> b[:] = a
>>> b
array([(0., b'0.0', b''), (0., b'0.0', b''), (0., b'0.0', b'')],
dtype=[('x', '<f4'), ('y', 'S3'), ('z', 'O')])
當(dāng)分配給作為子數(shù)組的字段時,分配的值將首先廣播到子數(shù)組的形狀。
可以通過使用字段名稱索引數(shù)組來訪問和修改結(jié)構(gòu)化數(shù)組的各個字段。
>>> x = np.array([(1, 2), (3, 4)], dtype=[('foo', 'i8'), ('bar', 'f4')])
>>> x['foo']
array([1, 3])
>>> x['foo'] = 10
>>> x
array([(10, 2.), (10, 4.)],
dtype=[('foo', '<i8'), ('bar', '<f4')])
結(jié)果數(shù)組是原始數(shù)組的視圖。它共享相同的內(nèi)存位置,寫入視圖將修改原始數(shù)組。
>>> y = x['bar']
>>> y[:] = 11
>>> x
array([(10, 11.), (10, 11.)],
dtype=[('foo', '<i8'), ('bar', '<f4')])
此視圖與索引字段具有相同的 dtype 和 itemsize,因此它通常是非結(jié)構(gòu)化數(shù)組,嵌套結(jié)構(gòu)除外。
>>> y.dtype, y.shape, y.strides
(dtype('float32'), (2,), (12,))
如果訪問的字段是子數(shù)組,則子數(shù)組的維度將附加到結(jié)果的形狀中:
>>> x = np.zeros((2, 2), dtype=[('a', np.int32), ('b', np.float64, (3, 3))])
>>> x['a'].shape
(2, 2)
>>> x['b'].shape
(2, 2, 3, 3)
可以索引并分配給具有多字段索引的結(jié)構(gòu)化數(shù)組,其中索引是字段名稱列表。
警告
多字段索引的行為從 Numpy 1.15 更改為 Numpy 1.16。
使用多字段索引進行索引的結(jié)果是原始數(shù)組的視圖,如下所示:
>>> a = np.zeros(3, dtype=[('a', 'i4'), ('b', 'i4'), ('c', 'f4')])
>>> a[['a', 'c']]
array([(0, 0.), (0, 0.), (0, 0.)],
dtype={'names':['a','c'], 'formats':['<i4','<f4'], 'offsets':[0,8], 'itemsize':12})
分配給視圖會修改原始數(shù)組。視圖的字段將按照它們被索引的順序排列。請注意,與單字段索引不同,視圖的 dtype 與原始數(shù)組具有相同的項大小,并且具有與原始數(shù)組中相同偏移量的字段,并且僅缺少未索引的字段。
警告
在 Numpy 1.15 中,使用多字段索引對數(shù)組進行索引會返回上述結(jié)果的副本,但字段在內(nèi)存中打包在一起,就像通過numpy.lib.recfunctions.repack_fields
.
與 1.15 相比,Numpy 1.16 的新行為導(dǎo)致在未索引字段的位置有額外的“填充”字節(jié)。您將需要更新任何依賴于具有“打包”布局的數(shù)據(jù)的代碼。例如代碼,例如:
&& a[['a', 'c']].view('i8') # Fails in Numpy 1.16
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: When changing to a smaller dtype, its size must be a divisor of the size of original dtype
將需要改變。此代碼FutureWarning
自 Numpy 1.12起引發(fā)了 a?,FutureWarning
自 1.7起也引發(fā)了類似的代碼。
在 1.16 中,numpy.lib.recfunctions
模塊中引入了許多功能?以幫助用戶解決此更改。這些是?numpy.lib.recfunctions.repack_fields
。?numpy.lib.recfunctions.structured_to_unstructured
,?numpy.lib.recfunctions.unstructured_to_structured
,?numpy.lib.recfunctions.apply_along_fields
,?numpy.lib.recfunctions.assign_fields_by_name
和?numpy.lib.recfunctions.require_fields
。
該函數(shù)numpy.lib.recfunctions.repack_fields
始終可用于重現(xiàn)舊行為,因為它將返回結(jié)構(gòu)化數(shù)組的打包副本。例如,上面的代碼可以替換為:
&& from numpy.lib.recfunctions import repack_fields
&& repack_fields(a[['a', 'c']]).view('i8') # supported in 1.16
array([0, 0, 0])
此外,numpy 現(xiàn)在提供了一個新函數(shù)?numpy.lib.recfunctions.structured_to_unstructured
,對于希望將結(jié)構(gòu)化數(shù)組轉(zhuǎn)換為非結(jié)構(gòu)化數(shù)組的用戶來說,這是一種更安全、更有效的替代方法,正如上面的視圖通常所做的那樣。與視圖不同,此函數(shù)允許在考慮填充的情況下安全轉(zhuǎn)換為非結(jié)構(gòu)化類型,通常避免復(fù)制,并根據(jù)需要轉(zhuǎn)換數(shù)據(jù)類型。代碼如:
&& b = np.zeros(3, dtype=[('x', 'f4'), ('y', 'f4'), ('z', 'f4')])
&& b[['x', 'z']].view('f4')
array([0., 0., 0., 0., 0., 0., 0., 0., 0.], dtype=float32)
可以通過替換為更安全:
&& from numpy.lib.recfunctions import structured_to_unstructured
&& structured_to_unstructured(b[['x', 'z']])
array([0, 0, 0])
分配給具有多字段索引的數(shù)組會修改原始數(shù)組:
>>> a[['a', 'c']] = (2, 3)
>>> a
array([(2, 0, 3.), (2, 0, 3.), (2, 0, 3.)],
dtype=[('a', '<i4'), ('b', '<i4'), ('c', '<f4')])
這遵守上述結(jié)構(gòu)化數(shù)組分配規(guī)則。例如,這意味著可以使用適當(dāng)?shù)亩嘧侄嗡饕粨Q兩個字段的值:
>>> a[['a', 'c']] = a[['c', 'a']]
索引結(jié)構(gòu)化數(shù)組的單個元素(使用整數(shù)索引)返回結(jié)構(gòu)化標(biāo)量:
>>> x = np.array([(1, 2., 3.)], dtype='i, f, f')
>>> scalar = x[0]
>>> scalar
(1, 2., 3.)
>>> type(scalar)
<class 'numpy.void'>
與其他 numpy 標(biāo)量不同,結(jié)構(gòu)化標(biāo)量是可變的,就像原始數(shù)組的視圖一樣,因此修改標(biāo)量將修改原始數(shù)組。結(jié)構(gòu)化標(biāo)量還支持按字段名稱訪問和分配:
>>> x = np.array([(1, 2), (3, 4)], dtype=[('foo', 'i8'), ('bar', 'f4')])
>>> s = x[0]
>>> s['bar'] = 100
>>> x
array([(1, 100.), (3, 4.)],
dtype=[('foo', '<i8'), ('bar', '<f4')])
與元組類似,結(jié)構(gòu)化標(biāo)量也可以用整數(shù)索引:
>>> scalar = np.array([(1, 2., 3.)], dtype='i, f, f')[0]
>>> scalar[0]
1
>>> scalar[1] = 4
因此,元組可能被認(rèn)為是原生 Python 等價于 numpy 的結(jié)構(gòu)化類型,就像原生 Python 整數(shù)等同于 numpy 的整數(shù)類型一樣。可以通過調(diào)用將結(jié)構(gòu)化標(biāo)量轉(zhuǎn)換為元組numpy.ndarray.item
:
>>> scalar.item(), type(scalar.item())
((1, 4.0, 3.0), <class 'tuple'>)
為了防止破壞object
類型字段中的對象指針?,numpy 目前不允許包含對象的結(jié)構(gòu)化數(shù)組的視圖。
如果兩個 void 結(jié)構(gòu)化數(shù)組的 dtypes 相等,則測試數(shù)組的相等性將產(chǎn)生一個具有原始數(shù)組維度的布爾數(shù)組,元素設(shè)置為True
相應(yīng)結(jié)構(gòu)的所有字段相等的位置。如果字段名稱、dtypes 和標(biāo)題相同,忽略字節(jié)順序,并且字段的順序相同,則結(jié)構(gòu)化 dtypes 是相等的:
>>> a = np.zeros(2, dtype=[('a', 'i4'), ('b', 'i4')])
>>> b = np.ones(2, dtype=[('a', 'i4'), ('b', 'i4')])
>>> a == b
array([False, False])
目前,如果兩個 void 結(jié)構(gòu)化數(shù)組的 dtypes 不相等,則比較失敗,返回標(biāo)量值False
。此行為自 numpy 1.10 起已棄用,將來會引發(fā)錯誤或執(zhí)行元素比較。
在<
與>
運營商總是返回False
比較空洞結(jié)構(gòu)陣列時,與算術(shù)和位操作不被支持。
更多建議: