NumPy 結(jié)構(gòu)化數(shù)據(jù)類型

2021-09-01 10:33 更新

結(jié)構(gòu)化數(shù)據(jù)類型可以被認為是一定長度(結(jié)構(gòu)的itemsize)的字節(jié)序列,它被解釋為字段的集合。每個字段在結(jié)構(gòu)中都有一個名稱、一個數(shù)據(jù)類型和一個字節(jié)偏移量。字段的數(shù)據(jù)類型可以是任何 numpy 數(shù)據(jù)類型,包括其他結(jié)構(gòu)化數(shù)據(jù)類型,也可以是子數(shù)組數(shù)據(jù)類型,其行為類似于指定形狀的 ndarray。字段的偏移量是任意的,字段甚至可能重疊。這些偏移量通常由 numpy 自動確定,但也可以指定。

1、結(jié)構(gòu)化數(shù)據(jù)類型創(chuàng)建

可以使用函數(shù)創(chuàng)建結(jié)構(gòu)化數(shù)據(jù)類型numpy.dtype。有 4 種替代形式的規(guī)范,它們的靈活性和簡潔性各不相同。這些在數(shù)據(jù)類型對象參考頁面中有進一步的記錄?,總而言之,它們是:

  1. 元組列表,每個字段一個元組 每個元組都具有形狀是可選的形式。是一個字符串(或元組,如果使用標題,請參閱?下面的字段標題),可以是任何可轉(zhuǎn)換為數(shù)據(jù)類型的對象,并且是指定子數(shù)組形狀的整數(shù)元組。(fieldname,?datatype,?shape)``fieldname``datatype``shape
    >>> np.dtype([('x', 'f4'), ('y', np.float32), ('z', 'f4', (2, 2))])
    dtype([('x', '<f4'), ('y', '<f4'), ('z', '<f4', (2, 2))])

    如果fieldname是空字符串'',則該字段將被賦予表單的默認名稱f#,其中#是該字段的整數(shù)索引,從左側(cè)開始計數(shù):

    >>> np.dtype([('x', 'f4'), ('', 'i4'), ('z', 'i8')])
    dtype([('x', '<f4'), ('f1', '<i4'), ('z', '<i8')])

    結(jié)構(gòu)內(nèi)字段的字節(jié)偏移量和總結(jié)構(gòu)項大小是自動確定的。

  2. 一串逗號分隔的數(shù)據(jù)類型規(guī)范 在這個速記符號中,任何字符串 dtype 規(guī)范都可以在字符串中使用并用逗號分隔。字段的itemsize和字節(jié)偏移量自動確定和字段名被給予默認名稱f0,?f1等等。
    >>> np.dtype('i8, f4, S3')
    dtype([('f0', '<i8'), ('f1', '<f4'), ('f2', 'S3')])
    >>> np.dtype('3int8, float32, (2, 3)float64')
    dtype([('f0', 'i1', (3,)), ('f1', '<f4'), ('f2', '<f8', (2, 3))])
  3. 字段參數(shù)數(shù)組字典 這是最靈活的規(guī)范形式,因為它允許控制字段的字節(jié)偏移量和結(jié)構(gòu)的項大小。 字典有兩個必需的鍵,“名稱”和“格式”,以及四個可選鍵,“偏移”、“項目大小”、“對齊”和“標題”。'names' 和 'formats' 的值應該分別是相同長度的字段名稱列表和 dtype 規(guī)范列表??蛇x的 'offsets' 值應該是一個整數(shù)字節(jié)偏移列表,結(jié)構(gòu)中的每個字段一個。如果未給出“偏移量”,則會自動確定偏移量??蛇x的“itemsize”值應該是一個整數(shù),描述 dtype 的總大?。ㄒ宰止?jié)為單位),它必須足夠大以包含所有字段。
    >>> np.dtype({'names': ['col1', 'col2'], 'formats': ['i4', 'f4']})
    dtype([('col1', '<i4'), ('col2', '<f4')])
    >>> np.dtype({'names': ['col1', 'col2'],
    ...           'formats': ['i4', 'f4'],
    ...           'offsets': [0, 4],
    ...           'itemsize': 12})
    dtype({'names':['col1','col2'], 'formats':['<i4','<f4'], 'offsets':[0,4], 'itemsize':12})

    可以選擇偏移以使字段重疊,盡管這意味著分配給一個字段可能會破壞任何重疊字段的數(shù)據(jù)。作為一個例外,numpy.object_類型字段不能與其他字段重疊,因為存在破壞內(nèi)部對象指針然后取消引用它的風險。 可以將可選的 'aligned' 值設(shè)置為True使自動偏移計算使用對齊的偏移(請參閱自動字節(jié)偏移和對齊),就好像 的 'align' 關(guān)鍵字參數(shù)numpy.dtype已設(shè)置為 True。 可選的“titles”值應該是與“names”長度相同的標題列表,請參閱下面的字段標題。

  4. 字段名稱字典 不鼓勵使用這種形式的規(guī)范,但在此處記錄,因為較舊的 numpy 代碼可能會使用它。字典的鍵是字段名稱,值是指定類型和偏移量的元組:
    >>> np.dtype({'col1': ('i1', 0), 'col2': ('f4', 1)})
    dtype([('col1', 'i1'), ('col2', '<f4')])

    不鼓勵這種形式,因為 Python 字典在 Python 3.6 之前的 Python 版本中不保留順序,并且結(jié)構(gòu)化 dtype 中字段的順序是有意義的??梢允褂?3 元組指定字段標題,請參見下文。

2、操作和顯示結(jié)構(gòu)化數(shù)據(jù)類型

結(jié)構(gòu)化數(shù)據(jù)類型的字段名稱列表可以names?在 dtype 對象的屬性中找到:

>>> d = np.dtype([('x', 'i8'), ('y', 'f4')])
>>> d.names
('x', 'y')

可以通過names使用相同長度的字符串序列分配給屬性來修改字段名稱。

dtype 對象還有一個類似字典的屬性,fields其鍵是字段名稱(和字段標題,見下文),其值是包含每個字段的 dtype 和字節(jié)偏移量的元組。

>>> d.fields
mappingproxy({'x': (dtype('int64'), 0), 'y': (dtype('float32'), 8)})

對于非結(jié)構(gòu)化數(shù)組,thenamesfieldsattributes 都相等None。測試dtype是否結(jié)構(gòu)化的推薦方法是使用if dt.names 不是 None而不是if dt.names,以考慮具有 0 個字段的 dtype。

如果可能,結(jié)構(gòu)化數(shù)據(jù)類型的字符串表示以“元組列表”形式顯示,否則 numpy 將退回使用更通用的字典形式。

3、自動字節(jié)偏移和對齊

Numpy 使用兩種方法之一來自動確定結(jié)構(gòu)化數(shù)據(jù)類型的字段字節(jié)偏移量和整體項大小,具體取決于是否?align=True指定為 的關(guān)鍵字參數(shù)numpy.dtype。

默認情況下 (?align=False),numpy 會將字段打包在一起,以便每個字段從前一個字段結(jié)束的字節(jié)偏移量開始,并且這些字段在內(nèi)存中是連續(xù)的。

>>> def print_offsets(d):
...     print("offsets:", [d.fields[name][1] for name in d.names])
...     print("itemsize:", d.itemsize)
>>> print_offsets(np.dtype('u1, u1, i4, u1, i8, u2'))
offsets: [0, 1, 2, 6, 7, 15]
itemsize: 17

如果align=True設(shè)置,numpy 將以與許多 C 編譯器填充 C 結(jié)構(gòu)相同的方式填充結(jié)構(gòu)。在某些情況下,對齊結(jié)構(gòu)可以提高性能,但代價是增加了數(shù)據(jù)類型大小。在字段之間插入填充字節(jié),這樣每個字段的字節(jié)偏移量將是該字段對齊的倍數(shù),對于簡單數(shù)據(jù)類型,這通常等于字段的大?。ㄒ宰止?jié)為單位),請參閱PyArray_Descr.alignment。該結(jié)構(gòu)還將添加尾隨填充,以便其項目大小是最大字段對齊的倍數(shù)。

>>> print_offsets(np.dtype('u1, u1, i4, u1, i8, u2', align=True))
offsets: [0, 1, 4, 8, 16, 24]
itemsize: 32

請注意,盡管默認情況下幾乎所有現(xiàn)代 C 編譯器都以這種方式填充,但 C 結(jié)構(gòu)中的填充取決于 C 實現(xiàn),因此不能保證此內(nèi)存布局與 C 程序中相應結(jié)構(gòu)的內(nèi)存布局完全匹配??赡苄枰?numpy 端或 C 端做一些工作,以獲得精確的對應關(guān)系。

如果使用offsets基于字典的 dtype 規(guī)范中的可選鍵指定偏移量,則設(shè)置align=True將檢查每個字段的偏移量是否是其大小的倍數(shù),并且 itemsize 是最大字段大小的倍數(shù),如果不是,則引發(fā)異常。

如果結(jié)構(gòu)化數(shù)組的字段和itemsize的偏移量滿足對齊條件,則該數(shù)組將具有ALIGNED?flag集合。

便利函數(shù)numpy.lib.recfunctions.repack_fields將對齊的 dtype 或數(shù)組轉(zhuǎn)換為壓縮類型,反之亦然。它采用 dtype 或結(jié)構(gòu)化 ndarray 作為參數(shù),并返回帶有重新打包的字段的副本,帶有或不帶有填充字節(jié)。

4、字段標題

除了字段名稱之外,字段還可能有一個相關(guān)聯(lián)的title,一個備用名稱,有時用作字段的附加描述或別名。標題可用于索引數(shù)組,就像字段名稱一樣。

要在使用 dtype 規(guī)范的元組列表形式時添加標題,可以將字段名稱指定為兩個字符串的元組,而不是單個字符串,這將分別是字段的標題和字段名稱。例如:

>>> np.dtype([(('my title', 'name'), 'f4')])
dtype([(('my title', 'name'), '<f4')])

當使用第一種形式的基于字典的規(guī)范時,標題可以作為額外的'titles'鍵提供,如上所述。當使用第二個(不鼓勵的)基于字典的規(guī)范時,可以通過提供一個 3 元素元組而不是通常的 2 元素元組來提供標題:(datatype,?offset,?title)

>>> np.dtype({'name': ('i4', 0, 'my title')})
dtype([(('my title', 'name'), '<i4')])

dtype.fields字典將包含標題作為鍵,如果使用任何頭銜。這實際上意味著具有標題的字段將在字段字典中出現(xiàn)兩次。這些字段的元組值還將具有第三個元素,即字段標題。正因為如此,并且因為names屬性保留字段順序而fields?屬性可能不保留,建議使用 dtype 的names屬性遍歷 dtype 的字段,該屬性不會列出標題,如下所示:

>>> for name in d.names:
...     print(d.fields[name][:2])
(dtype('int64'), 0)
(dtype('float32'), 8)

4、聯(lián)合類型

numpy.void默認情況下,結(jié)構(gòu)化數(shù)據(jù)類型在 numpy 中實現(xiàn)為具有基本類型?,但可以使用數(shù)據(jù)類型對象中描述的 dtype 規(guī)范形式?將其他 numpy 類型解釋為結(jié)構(gòu)化類型。這里,是所需的底層 dtype,字段和標志將從?.?此 dtype 類似于 C 中的“聯(lián)合”。(base_dtype,?dtype)``base_dtype``dtype

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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號