NumPy:3字节、6字节类型(也称为uint24、uint48)

yhqotfr8  于 2023-10-19  发布在  其他
关注(0)|答案(3)|浏览(124)

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的输入,但它看起来效率很低(想想100 GB的数据)。有没有更有效的方法?例如,创建一种特殊的只读视图,它屏蔽了部分数据,这将是非常有用的(一种“uint64,两个MSB始终为零”类型)。我只需要对数据的只读访问权限。

0tdrvxhp

0tdrvxhp1#

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

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,))

然而,这样每个元素都会与下一个元素重叠,所以要真正使用它,你必须在访问时屏蔽掉高字节。

wfypjpf4

wfypjpf42#

答案在:如何创建包含24位整数的Numpy dtype?
这是一个有点丑,但正是你想要的:允许你索引你的ndarray,就像它有一个<u3的dtype一样,这样你就可以从磁盘上memmap()大数据。
您仍然需要手动应用位掩码来清除第四个重叠字节,但可以在访问后应用于切片(多维)数组。
诀窍是滥用ndarray的'stride'部分,以便索引工作。为了让它工作而不抱怨限制,有一个特殊的技巧。

wdebmtf2

wdebmtf23#

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

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

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

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

相关问题