2012-08-15 17 views
5

NumPy parece carecer de compatibilidad integrada para los tipos de 3 y 6 bytes, también conocidos como uint24 y uint48. Tengo un gran conjunto de datos usando estos tipos y quiero alimentarlo a numpy. Lo que hago actualmente (por uint24):NumPy: tipos de 3 bytes y 6 bytes (también conocido como uint24, uint48)

import numpy as np 
dt = np.dtype([('head', '<u2'), ('data', '<u2', (3,))]) 
# I would like to be able to write 
# dt = np.dtype([('head', '<u2'), ('data', '<u3', (2,))]) 
# dt = np.dtype([('head', '<u2'), ('data', '<u6')]) 
a = np.memmap("filename", mode='r', dtype=dt) 
# convert 3 x 2byte data to 2 x 3byte 
# w1 is LSB, w3 is MSB 
w1, w2, w3 = a['data'].swapaxes(0,1) 
a2 = np.ndarray((2,a.size), dtype='u4') 
# 3 LSB 
a2[0] = w2 % 256 
a2[0] <<= 16 
a2[0] += w1 
# 3 MSB 
a2[1] = w3 
a2[1] <<=8 
a2[1] += w2 >> 8 
# now a2 contains "uint24" matrix 

Mientras que funciona para la entrada de 100 MB, parece ineficiente (pensar en 100s GB de datos). ¿Hay una manera más eficiente? Por ejemplo, sería útil crear un tipo especial de vista de solo lectura que enmascare parte de los datos (tipo de "uint64 con dos MSBs siempre cero"). Solo necesito acceso de solo lectura a los datos.

Respuesta

6

No creo que haya una manera de hacer lo que se está pidiendo (requeriría acceso no alineado, que es altamente ineficiente en algunas arquitecturas). Mi solución de Reading and storing arbitrary byte length integers from a file podría ser más eficientes en la transferencia de los datos a un array en proceso:

a = np.memmap("filename", mode='r', dtype=np.dtype('>u1')) 
e = np.zeros(a.size/6, np.dtype('>u8')) 
for i in range(3): 
    e.view(dtype='>u2')[i + 1::4] = a.view(dtype='>u2')[i::3] 

Usted puede obtener acceso no alineado con el parámetro strides constructor:

e = np.ndarray((a.size - 2) // 6, np.dtype('<u8'), buf, strides=(6,)) 

Sin embargo, con esto cada elemento pasará se superponen con el siguiente, por lo que para usarlo realmente tendrías que enmascarar los bytes altos en el acceso.

+1

+1 para problemas de alineación. Los problemas no valen la pena. – Joe

+0

Si bien su método se ve mejor, todavía incurre en leer toda la entrada varias veces y almacenar una copia modificada en la memoria. Tanto tu como mi solución original no funcionarán para la entrada de 100GB. –

0

con el siguiente código se puede leer números enteros de cualquier tamaño codificado tan grande o pequeño endian:

def readBigEndian(filename, bytesize): 
    with (open(filename,"rb")) as f: 
     str = f.read(bytesize) 
     while len(str)==bytesize: 
      int = 0; 
      for byte in map(ord,str): 
       print byte 
       int = (int << 8) | byte 
      yield(int) 
      str = f.read(bytesize) 

def readLittleEndian(filename, bytesize): 
    with (open(filename,"rb")) as f: 
     str = f.read(bytesize) 
     while len(str)==bytesize: 
      int = 0; 
      shift = 0 
      for byte in map(ord,str): 
       print byte 
       int |= byte << shift 
       shift += 8 
      yield(int) 
      str = f.read(bytesize) 

for i in readLittleEndian("readint.py",3): 
    print i 
1

Hay una respuesta para esto más en: How do I create a Numpy dtype that includes 24 bit integers?

Es un poco feo, pero hace exactamente lo que quiere: le permite indexar su ndarray como si tuviera un tipo de <u3 para que pueda memmap() big data desde el disco.
Aún necesita aplicar manualmente una máscara de bits para borrar el cuarto byte superpuesto, pero eso se puede aplicar a la matriz en rodajas (multidimensional) después del acceso.

El truco es abusar de la parte "zancada" de un ndarray, para que la indexación funcione. Para que funcione sin quejarse de los límites, está a special trick.

Cuestiones relacionadas