NumPy 結(jié)構(gòu)化數(shù)組的索引和賦值

2021-09-01 10:43 更新

1、將數(shù)據(jù)分配給結(jié)構(gòu)化數(shù)組

有多種方法可以為結(jié)構(gòu)化數(shù)組賦值:使用 python 元組、使用標(biāo)量值或使用其他結(jié)構(gòu)化數(shù)組。

1.1 從 Python 本機類型(元組)賦值

為結(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')])

1.2 從標(biāo)量賦值

分配給結(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'

1.3 從其他結(jié)構(gòu)化數(shù)組賦值

兩個結(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')])

1.4 涉及子數(shù)組賦值

當(dāng)分配給作為子數(shù)組的字段時,分配的值將首先廣播到子數(shù)組的形狀。

2、索引結(jié)構(gòu)化數(shù)組

2.1訪問單個字段

可以通過使用字段名稱索引數(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)

2.2 訪問多個字段

可以索引并分配給具有多字段索引的結(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']]

2.3 用整數(shù)索引以獲得結(jié)構(gòu)化標(biāo)量

索引結(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'>)

3、查看包含對象的結(jié)構(gòu)化數(shù)組

為了防止破壞object類型字段中的對象指針?,numpy 目前不允許包含對象的結(jié)構(gòu)化數(shù)組的視圖。

4、結(jié)構(gòu)比較

如果兩個 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ù)和位操作不被支持。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號