Spec-Zone.ru › NumPy 2.0

Работа с массивами строк и байтов

Хотя NumPy в первую очередь является библиотекой для работы с числами, часто удобно работать с массивами NumPy строк или байтов. Две наиболее распространённые ситуации:

  • Работа с данными, загруженными или сопоставленными с памятью из файла данных, где одно или несколько полей данных являются строкой или байтовой строкой, и максимальная длина поля известна заранее. Это часто используется для поля имени или метки.
  • Использование индексации и векторизации NumPy с массивами Python-строк неизвестной длины, которые могут или не могут иметь данные для каждого значения.

Для первого случая NumPy предоставляет фиксированной ширины numpy.void, numpy.str_ и numpy.bytes_ типы данных. Для второго случая numpy предоставляет numpy.dtypes.StringDType. Ниже мы опишем, как работать с массивами строк фиксированной и переменной ширины, как преобразовывать между двумя представлениями и предоставим некоторые рекомендации для наиболее эффективной работы со строковыми данными в NumPy.

Типы данных фиксированной ширины

До NumPy 2.0 типы данных фиксированной ширины numpy.str_, numpy.bytes_ и numpy.void были единственными доступными типами для работы со строками и байтовыми строками в NumPy. По этой причине они используются в качестве типа данных по умолчанию для строк и байтовых строк соответственно:

>>> np.array(["hello", "world"])
array(['hello', 'world'], dtype='<U5')

Здесь обнаруженный тип данных — '<U5', или строка Unicode с порядком следования байтов little-endian, с максимальной длиной 5 кодовых точек Unicode.

Аналогично для байтовых строк:

>>> np.array([b"hello", b"world"])
array([b'hello', b'world'], dtype='|S5')

Поскольку это кодировка с одним байтом, порядок байтов — ‘|’ (не применимо), и обнаруженный тип данных — байтовая строка максимальной длины 5 символов.

Вы также можете использовать numpy.void для представления байтовых строк:

>>> np.array([b"hello", b"world"]).astype(np.void)
array([b'\x68\x65\x6C\x6C\x6F', b'\x77\x6F\x72\x6C\x64'], dtype='|V5')

Это наиболее полезно при работе с потоками байтов, которые не хорошо представлены как байтовые строки, и вместо этого лучше рассматриваются как коллекции 8-битных целых чисел.

Строки переменной ширины

Новое в версии 2.0.

Примечание

numpy.dtypes.StringDType — это новое дополнение к NumPy, реализованное с помощью новой поддержки гибких пользовательских типов данных в NumPy и не так хорошо протестировано в рабочих потоках, как старые типы данных NumPy.

Часто данные реальных строк не имеют предсказуемой длины. В этих случаях использование строк фиксированной ширины неудобно, поскольку хранение всех данных без усечения требует знания длины самой длинной строки, которую вы хотите хранить в массиве, до создания массива.

Для поддержки таких ситуаций NumPy предоставляет numpy.dtypes.StringDType, который хранит данные строк переменной ширины в кодировке UTF-8 в массиве NumPy:

>>> from numpy.dtypes import StringDType
>>> data = ["this is a longer string", "short string"]
>>> arr = np.array(data, dtype=StringDType())
>>> arr
array(['this is a longer string', 'short string'], dtype=StringDType())

Обратите внимание, что в отличие от строк фиксированной ширины, StringDType не параметризуется максимальной длиной элемента массива; произвольно длинные или короткие строки могут храниться в одном массиве без необходимости резервирования места для заполняющих байтов в коротких строках.

Также обратите внимание, что в отличие от строк фиксированной ширины и большинства других типов данных NumPy, StringDType не хранит строковые данные в основном буфере ndarray данных. Вместо этого буфер массива используется для хранения метаданных о том, где хранятся строковые данные в памяти. Это различие означает, что код, ожидающий, что буфер массива будет содержать строковые данные, не будет работать правильно и потребует обновления для поддержки StringDType.

Поддержка пропущенных данных

Часто наборы данных строк неполны, и нужна специальная метка для указания отсутствия значения. По умолчанию StringDType не имеет специальной поддержки пропущенных значений, кроме того, что пустые строки используются для заполнения пустых массивов:

>>> np.empty(3, dtype=StringDType())
array(['', '', ''], dtype=StringDType())

Вы можете создать экземпляр StringDType с поддержкой пропущенных значений, передав na_object в качестве ключевого аргумента инициализатора:

>>> dt = StringDType(na_object=None)
>>> arr = np.array(["this array has", None, "as an entry"], dtype=dt)
>>> arr
array(['this array has', None, 'as an entry'],
      dtype=StringDType(na_object=None))
>>> arr[1] is None
True

Значение na_object может быть любым произвольным объектом Python. Общие варианты — numpy.nan, float('nan'), None, объект, специально предназначенный для представления пропущенных данных, например, pandas.NA, или (желательно) уникальная строка, например, "__placeholder__".

NumPy имеет специальную обработку для NaN-подобных маркеров и строковых маркеров.

NaN-подобные маркеры отсутствующих данных

NaN-подобный маркер возвращает себя в результате арифметических операций. Сюда входят python nan float и маркер пропущенных данных Pandas pd.NA. NaN-подобные маркеры наследуют эти свойства в строковых операциях. Это означает, что, например, результат сложения с любой другой строкой — это маркер:

>>> dt = StringDType(na_object=np.nan)
>>> arr = np.array(["hello", np.nan, "world"], dtype=dt)
>>> arr + arr
array(['hellohello', nan, 'worldworld'], dtype=StringDType(na_object=nan))

Следуя поведению nan в массивах с плавающей точкой, NaN-подобные маркеры сортируются в конец массива:

>>> np.sort(arr)
array(['hello', 'world', nan], dtype=StringDType(na_object=nan))

Строковые маркеры отсутствующих данных

Строковое значение отсутствующих данных — это экземпляр str или подтип str. Если такой массив передаётся в строковую операцию или преобразование, записи «отсутствуют» обрабатываются так, как будто у них есть значение, заданное строковым маркером. Операции сравнения аналогично используют значение маркера непосредственно для отсутствующих записей.

Другие маркеры

Другие объекты, такие как None также поддерживаются в качестве маркеров отсутствующих данных. Если в массиве присутствуют отсутствующие данные с использованием такого маркера, строковые операции приведут к ошибке:

>>> dt = StringDType(na_object=None)
>>> arr = np.array(["this array has", None, "as an entry"])
>>> np.sort(arr)
Traceback (most recent call last):
...
TypeError: '<' not supported between instances of 'NoneType' and 'str'

Приведение нестроковых данных

По умолчанию данные, не являющиеся строками, преобразуются в строки:

>>> np.array([1, object(), 3.4], dtype=StringDType())
array(['1', '<object object at 0x7faa2497dde0>', '3.4'], dtype=StringDType())

Если этого поведения не требуется, экземпляр типа данных можно создать, отключив приведение к строкам, установив coerce=False в инициализаторе:

>>> np.array([1, object(), 3.4], dtype=StringDType(coerce=False))
Traceback (most recent call last):
...
ValueError: StringDType only allows string data when string coercion is disabled.

Это позволяет строго проверять данные в том же проходе по данным, который NumPy использует для создания массива. Установка coerce=True восстанавливает поведение по умолчанию, позволяя приведение к строкам.

Преобразование в и из строк фиксированной ширины

StringDType поддерживает двустороннее преобразование между numpy.str_, numpy.bytes_ и numpy.void. Преобразование в строку фиксированной ширины наиболее полезно, когда строки необходимо сопоставить с памятью в массиве ndarray или когда строка фиксированной ширины требуется для чтения и записи в столбцовый формат данных с известной максимальной длиной строки.

Во всех случаях преобразование в строку фиксированной ширины требует указания максимальной разрешённой длины строки:

>>> arr = np.array(["hello", "world"], dtype=StringDType())
>>> arr.astype(np.str_)  
Traceback (most recent call last):
...
TypeError: Casting from StringDType to a fixed-width dtype with an
unspecified size is not currently supported, specify an explicit
size for the output dtype instead.

The above exception was the direct cause of the following
exception:

TypeError: cannot cast dtype StringDType() to <class 'numpy.dtypes.StrDType'>.
>>> arr.astype("U5")
array(['hello', 'world'], dtype='<U5')

Преобразование numpy.bytes_ наиболее полезно для строковых данных, которые, как известно, содержат только символы ASCII, поскольку символы за пределами этого диапазона не могут быть представлены одним байтом в кодировке UTF-8 и отклоняются.

Любая допустимая строка Unicode может быть преобразована в numpy.str_, хотя поскольку numpy.str_ использует кодировку UCS4 в 32 бита для всех символов, это часто приводит к пустому месту в памяти для реальных текстовых данных, которые могут быть хорошо представлены более экономичной кодировкой.

Кроме того, любая допустимая строка Unicode может быть преобразована в numpy.void, сохраняя байты UTF-8 напрямую в выходном массиве:

>>> arr = np.array(["hello", "world"], dtype=StringDType())
>>> arr.astype("V5")
array([b'\x68\x65\x6C\x6C\x6F', b'\x77\x6F\x72\x6C\x64'], dtype='|V5')

Необходимо следить за тем, чтобы выходной массив имел достаточно места для байтов UTF-8 в строке, поскольку размер потока байтов UTF-8 в байтах необязательно совпадает с количеством символов в строке.

© 2005–2024 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/2.0/user/basics.strings.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API