конвертеры numpy genfromtxt, не работающие с юникодом (Python 3) - PullRequest
0 голосов
/ 07 февраля 2019

Я перемещаю некоторый код из Python2 в Python3.

Мне известно об изменениях "unicode" по сравнению с "bytes", которые влияют на обработку строк (Unicode по умолчанию в Python 3).

Однако я обнаружил некоторые несоответствия в том, как numpy обрабатывает их в обратном вызове converters для функции genfromtxt.

Рассмотрим файл данных (data.txt):

1,2,hello!
3,4,world!

Я хочу прочитать в строках и убрать "!"с ковнером.В Python 2 я сделал это:

np.genfromtxt('data.txt', delimiter=',', dtype='i4,i4,S10',
        converters={2:lambda s: s.strip('!')})

, который отлично работает.Для Python 3 я просто изменил S10 на U10 в dtypes, чтобы гарантировать, что данные считываются как Unicode, а не как байтовый массив:

np.genfromtxt('data.txt', delimiter=',', dtype='i4,i4,U10',
        converters={2:lambda s: s.strip('!')})

Однако преобразователь завершается с ошибкой: TypeError: a bytes-like object is required, not 'str'

Похоже, что функция преобразователя все еще получает байтовый массив, а не строку Unicode, так как единственный способ заставить его работать - это изменить s.strip('!') на s.strip(b'!')

Этокажется противоречивым для меня.Тем более, что он отлично работает, если я выполняю команду полосы ПОСЛЕ Я читаю массив, а не как преобразователь, т.е. это прекрасно работает:

dat=np.genfromtxt('data.txt', delimiter=',', dtype='i4,i4,U10') 
print(dat.dtype)
print(dat['f2'][0].strip('!') # notice, no 'b' needed

печатает:

[('f0', '<i4'), ('f1', '<i4'), ('f2', '<U10')]
hello

Это кажется мне противоречивым и затрудняет определение того, когда использовать спецификатор 'b', чтобы избежать вышеупомянутой ошибки!

Я нахожу, что проблема Unicode и байтов очень разочаровывает в Python 3!

1 Ответ

0 голосов
/ 07 февраля 2019

Зависит от режима открытия файла.Для совместимости с Py2, genfromtxt используется, чтобы всегда работать в байтовом режиме.Теперь Py3 может открыть его в текстовом режиме

Добавление параметра encoding (здесь достаточно None, файл сохраняется как UTF8):

In [373]: np.genfromtxt('stack54570492.txt', delimiter=',', dtype='i4,i4,S10',
     ...:         converters={2:lambda s: s.strip('!')}, encoding=None)     
Out[373]: 
array([(1, 2, b'hello'), (3, 4, b'world')],
      dtype=[('f0', '<i4'), ('f1', '<i4'), ('f2', 'S10')])

(см.Документы для более подробной информации об этом параметре.)

Преобразователь применяется к необработанной строке, прежде чем будет предпринята попытка присвоить dtype.Поэтому не имеет значения, указали ли вы U10 или S10.Часто конвертер используется для очистки строки, чтобы ее можно было преобразовать в число.

В вашем случае постобработки все еще требуется b - в зависимости от типа d:

In [376]: dat=np.genfromtxt('stack54570492.txt', delimiter=',', dtype='i4,i4,S10')
In [377]: dat
Out[377]: 
array([(1, 2, b'hello!'), (3, 4, b'world!')],
      dtype=[('f0', '<i4'), ('f1', '<i4'), ('f2', 'S10')])
In [378]: dat['f2'][0].strip('!')
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-378-697f391eaffc> in <module>()
----> 1 dat['f2'][0].strip('!')

TypeError: a bytes-like object is required, not 'str'
In [379]: dat['f2'][0].strip(b'!')
Out[379]: b'hello'

Полная справка для encoding:

кодировка: str, необязательно

Кодировка, используемая для декодирования входного файла.Не применяется, когда fname является файловым объектом.Специальное значение 'bytes' включает обходные пути обратной совместимости, которые гарантируют, что вы получите байтовые массивы, когда это возможно, и передадут строки в кодировке latin1 преобразователям.Переопределите это значение, чтобы получать массивы Unicode и передавать строки в качестве входных данных для преобразователей.Если установлено значение Нет, используется системное значение по умолчанию.Значение по умолчанию - «байты».

.. versionadded :: 1.14.0

При кодировании «байты» в преобразователе требуется b'!' (независимо отуказанный тип d):

In [382]: np.genfromtxt('stack54570492.txt', delimiter=',', dtype=None,
     ...:         converters={2:lambda s: s.strip(b'!')}, encoding='bytes')      
Out[382]: 
array([(1, 2, b'hello'), (3, 4, b'world')],
      dtype=[('f0', '<i8'), ('f1', '<i8'), ('f2', 'S5')])

In [383]: np.genfromtxt('stack54570492.txt', delimiter=',', dtype=None,
     ...:         converters={2:lambda s: s.strip('!')}, encoding=None)
Out[383]: 
array([(1, 2, 'hello'), (3, 4, 'world')],
      dtype=[('f0', '<i8'), ('f1', '<i8'), ('f2', '<U5')])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...