Работа с массивами строк и байтов
Хотя 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