将32位整数转为四个8位整数数组在Python中

6 投票
4 回答
15978 浏览
提问于 2025-04-18 17:14

如何高效地将一个32位整数转换成四个8位整数的数组,使用Python来实现呢?

现在我有以下这段代码,但它运行得非常慢:

def convert(int32_val):
    bin = np.binary_repr(int32_val, width = 32) 
    int8_arr = [int(bin[0:8],2), int(bin[8:16],2), 
                int(bin[16:24],2), int(bin[24:32],2)]
    return int8_arr  

例如:

print convert(1)
>>> [0, 0, 0, 1]   

print convert(-1)
>>> [255, 255, 255, 255]

print convert(-1306918380)  
>>> [178, 26, 2, 20]

我需要对无符号的32位整数实现相同的功能。

另外,能否对一个包含大量32位整数的numpy数组进行向量化处理呢?

4 个回答

2

在我的测试中,单纯使用Python自带的除法和取余运算,速度提高了6倍。

def convert(i):
    i = i % 4294967296
    n4 = i % 256
    i = i / 256
    n3 = i % 256
    i = i / 256
    n2 = i % 256
    n1 = i / 256
    return (n1,n2,n3,n4)
3

Python 3.2及更高版本中,新增了一个int的方法to_bytes,这个方法也可以使用:

>>> convert = lambda n : [int(i) for i in n.to_bytes(4, byteorder='big', signed=True)]
>>>
>>> convert(1)
[0, 0, 0, 1]
>>>
>>> convert(-1)
[255, 255, 255, 255]
>>>
>>> convert(-1306918380)
[178, 26, 2, 20]
>>>
3

你可以使用位运算来处理数据:

def int32_to_int8(n):
    mask = (1 << 8) - 1
    return [(n >> k) & mask for k in range(0, 32, 8)]

>>> int32_to_int8(32768)
[0, 128, 0, 0]

或者,你也可以在Python中使用struct模块

>>> import struct
>>> int32 = struct.pack("I", 32768)
>>> struct.unpack("B" * 4, int32)

(0, 128, 0, 0)

使用struct模块有一个很不错的地方,就是你可以非常高效地将int32转换为int8

import numpy.random

# Generate some random int32 numbers
x = numpy.random.randint(0, (1 << 31) - 1, 1000)

# Then you can convert all of them to int8 with just one command
x_int8 = struct.unpack('B' * (4*len(x)), buffer(x))

# To verify that the results are valid:
x[0]
Out[29]: 1219620060

int32_to_int8(x[0])
Out[30]: [220, 236, 177, 72]

x_int8[:4]
Out[31]: (220, 236, 177, 72)

# And it's FAST!

%timeit struct.unpack('B' * (4*len(x)), buffer(x))
10000 loops, best of 3: 32 µs per loop

%timeit [int32_to_int8(i) for i in x]
100 loops, best of 3: 6.01 ms per loop

更新:比较一下struct.unpackndarray.view

import numpy as np

# this is fast because it only creates the view, without involving any creation
# of objects in Python
%timeit x.view(np.int8)
1000000 loops, best of 3: 570 ns per loop

如果你要进行一些实际的计算:

uint8_type = "B" * len(x) * 4
%timeit sum(struct.unpack(uint8_type, buffer(x)))
10000 loops, best of 3: 52.6 µs per loop

# slow because in order to call sum(), implicitly the view object is converted to
# list.
%timeit sum(x.view(np.int8))
1000 loops, best of 3: 768 µs per loop

# use the numpy.sum() function - without creating Python objects
%timeit np.sum(x.view(np.int8))
100000 loops, best of 3: 8.55 µs per loop # <- FAST!

记住:使用ndarray.view()

7

使用 dtype 的方法可以参考这里的文档: http://docs.scipy.org/doc/numpy/reference/generated/numpy.dtype.html

Subdivide int16 into 2 int8‘s, called x and y. 0 and 1 are the offsets in bytes:

np.dtype((np.int16, {'x':(np.int8,0), 'y':(np.int8,1)}))
dtype(('<i2', [('x', '|i1'), ('y', '|i1')]))

或者根据你的情况进行调整:

In [30]: x=np.arange(12,dtype=np.int32)*1000
In [39]: dt=np.dtype((np.int32, {'f0':(np.uint8,0),'f1':(np.uint8,1),'f2':(np.uint8,2), 'f3':(np.uint8,3)}))

In [40]: x1=x.view(dtype=dt)

In [41]: x1['f0']
Out[41]: array([  0, 232, 208, 184, 160, 136, 112,  88,  64,  40,  16, 248], dtype=uint8)

In [42]: x1['f1']
Out[42]: array([ 0,  3,  7, 11, 15, 19, 23, 27, 31, 35, 39, 42], dtype=uint8)

进行比较

In [38]: x%256
Out[38]: array([  0, 232, 208, 184, 160, 136, 112,  88,  64,  40,  16, 248])

更多的文档可以查看 http://docs.scipy.org/doc/numpy/user/basics.rec.html

2) 元组参数:在记录结构中,唯一相关的元组情况是当一个结构映射到一个已有的数据类型时。这是通过将已有的数据类型和一个匹配的 dtype 定义放在一个元组中来完成的(使用这里描述的任何变体)。举个例子(使用列表定义,所以请查看 3) 以获取更多细节):

x = np.zeros(3, dtype=('i4',[('r','u1'), ('g','u1'), ('b','u1'), ('a','u1')]))

array([0, 0, 0])

x['r'] # array([0, 0, 0], dtype=uint8)

在这个例子中,生成了一个看起来和行为都像简单的 int32 数组的数组,但它也有定义字段,这些字段只使用了 int32 的一个字节(有点像 Fortran 的等价性)。

获取 4 个字节的 2D 数组的一种方法是:

In [46]: np.array([x1['f0'],x1['f1'],x1['f2'],x1['f3']])
Out[46]: 
array([[  0, 232, 208, 184, 160, 136, 112,  88,  64,  40,  16, 248],
       [  0,   3,   7,  11,  15,  19,  23,  27,  31,  35,  39,  42],
       [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0]], dtype=uint8)

同样的思路,但更紧凑:

In [50]: dt1=np.dtype(('i4', [('bytes','u1',4)]))

In [53]: x2=x.view(dtype=dt1)

In [54]: x2.dtype
Out[54]: dtype([('bytes', 'u1', (4,))])

In [55]: x2['bytes']
Out[55]: 
array([[  0,   0,   0,   0],
       [232,   3,   0,   0],
       [208,   7,   0,   0],
       [184,  11,   0,   0],
       [160,  15,   0,   0],
       [136,  19,   0,   0],
       [112,  23,   0,   0],
       [ 88,  27,   0,   0],
       [ 64,  31,   0,   0],
       [ 40,  35,   0,   0],
       [ 16,  39,   0,   0],
       [248,  42,   0,   0]], dtype=uint8)

In [56]: x2
Out[56]: 
array([    0,  1000,  2000,  3000,  4000,  5000,  6000,  7000,  8000,
        9000, 10000, 11000])

撰写回答