Especificando la encoding usando NumPy loadtxt / savetxt

El uso de las loadtxt NumPy loadtxt y savetxt falla cuando se trata de caracteres que no son ASCII. Estas funciones son principalmente para datos numéricos, pero también se admiten encabezados / pies de página alfanuméricos.

Tanto loadtxt como savetxt parecen estar aplicando la encoding latin-1, que me parece muy ortogonal al rest de Python 3, que es completamente consciente de Unicode y siempre parece estar usando utf-8 como la encoding predeterminada.

Dado que NumPy no se ha movido a utf-8 como la encoding predeterminada, puedo al menos cambiar la encoding de latin-1, ya sea a través de alguna función / atributo implementado o un hack conocido, ya sea solo para loadtxt / savetxt o para NumPy ¿en su totalidad?

Que esto no sea posible con Python 2 es perdonable, pero realmente no debería ser un problema cuando se usa Python 3. He encontrado el problema al usar cualquier combinación de Python 3.xy las últimas versiones de NumPy.

Código de ejemplo

Considere el archivo data.txt con el contenido.

 # This is π 3.14159265359 

Tratando de cargar esto con

 import numpy as np pi = np.loadtxt('data.txt') print(pi) 

falla con una excepción UnicodeEncodeError , que indica que el códec latin-1 no puede codificar el carácter ‘ \u03c0 ‘ (el carácter π ).

Esto es frustrante porque π solo está presente en una línea de comentario / encabezado, así que no hay razón para que loadtxt intente codificar este carácter.

Puedo leer exitosamente el archivo omitiendo explícitamente la primera fila, usando pi = np.loadtxt('data.txt', skiprows=1) , pero es un inconveniente tener que saber el número exacto de líneas de encabezado.

Se produce la misma excepción si trato de escribir un carácter Unicode usando savetxt :

 np.savetxt('data.txt', [3.14159265359], header='# This is π') 

Para llevar a cabo esta tarea con éxito, primero tengo que escribir el encabezado por algún otro medio y luego guardar los datos en un objeto de archivo abierto con el modo 'a+b' , por ejemplo

 with open('data.txt', 'w') as f: f.write('# This is π\n') with open('data.txt', 'a+b') as f: np.savetxt(f, [3.14159265359]) 

Lo que no hace falta decir es a la vez feo e inconveniente.

Solución

Me decidí por la solución por hpaulj, que pensé que sería bueno deletrear completamente. Cerca de la cima de mi progtwig ahora lo hago

 import numpy as np asbytes = lambda s: s if isinstance(s, bytes) else str(s).encode('utf-8') asstr = lambda s: s.decode('utf-8') if isinstance(s, bytes) else str(s) np.compat.py3k.asbytes = asbytes np.compat.py3k.asstr = asstr np.compat.py3k.asunicode = asstr np.lib.npyio.asbytes = asbytes np.lib.npyio.asstr = asstr np.lib.npyio.asunicode = asstr 

después de lo cual np.loadtxt y np.savetxt manejan Unicode correctamente.

Tenga en cuenta que para las versiones más nuevas de NumPy (puedo confirmar 1.14.3, pero también versiones anteriores) este truco no es necesario, ya que parece que Unicode ahora se maneja correctamente de manera predeterminada.

Al menos para savetxt las codificaciones se manejan en

 Signature: np.lib.npyio.asbytes(s) Source: def asbytes(s): if isinstance(s, bytes): return s return str(s).encode('latin1') File: /usr/local/lib/python3.5/dist-packages/numpy/compat/py3k.py Type: function Signature: np.lib.npyio.asstr(s) Source: def asstr(s): if isinstance(s, bytes): return s.decode('latin1') return str(s) File: /usr/local/lib/python3.5/dist-packages/numpy/compat/py3k.py Type: function 

El encabezado se escribe en el archivo wb con

  header = header.replace('\n', '\n' + comments) fh.write(asbytes(comments + header + newline)) 

Escribir una matriz numpy Unicode en un archivo de texto tiene algunas de mis exploraciones anteriores. Allí me estaba centrando en los caracteres de los datos, no en el encabezado.

Un par de hacks:

  • Abra el archivo en modo binario y pase el objeto de archivo abierto a loadtxt :

     In [12]: cat data.txt # This is π 3.14159265359 In [13]: with open('data.txt', 'rb') as f: ...: result = np.loadtxt(f) ...: In [14]: result Out[14]: array(3.14159265359) 
  • Abra el archivo utilizando la encoding latin1 y pase el objeto de archivo abierto a loadtxt :

     In [15]: with open('data.txt', encoding='latin1') as f: ...: result = np.loadtxt(f) ...: In [16]: result Out[16]: array(3.14159265359)