7

NumPy 似乎缺乏对 3 字节和 6 字节类型(又名uint24uint48. 我有一个使用这些类型的大型数据集,并希望将其提供给 numpy。我目前在做什么(对于 uint24):

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

虽然它适用于 100MB 的输入,但它看起来效率低下(想想 100s GB 的数据)。有没有更有效的方法?例如,创建一种特殊类型的只读视图来屏蔽部分数据会很有用(“具有两个 MSB 始终为零的 uint64”类型)。我只需要对数据进行只读访问。

4

3 回答 3

8

我不相信有一种方法可以满足您的要求(它需要未对齐的访问,这在某些架构上效率非常低)。我从文件中读取和存储任意字节长度整数的解决方案可能更有效地将数据传输到进程内数组:

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]

strides您可以使用构造函数参数获得非对齐访问:

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

但是,每个元素都将与下一个元素重叠,因此要实际使用它,您必须在访问时屏蔽高字节。

于 2012-08-15T10:11:27.533 回答
2

对此有一个答案: 如何创建包含 24 位整数的 Numpy dtype?

它有点难看,但正是你想要的:允许你索引你的 ndarray,就像它有一个 dtype 一样,<u3你可以memmap()从磁盘获取大数据。
您仍然需要手动应用位掩码来清除第四个重叠字节,但这可以在访问后应用于切片(多维)数组。

诀窍是滥用 ndarray 的“步幅”部分,以便索引工作。为了让它在不抱怨限制的情况下工作,有一个特殊的技巧

于 2015-12-07T07:34:18.450 回答
0

使用下面的代码,您可以读取编码为大端或小端的任何大小的整数:

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
于 2012-08-15T12:20:43.883 回答