【问题标题】:Converting 32-bit integer into array of four 8-bit integers in Python在 Python 中将 32 位整数转换为四个 8 位整数的数组
【发布时间】:2014-10-07 13:07:07
【问题描述】:

如何在 Python 中高效地将一个 32 位整数转换为一个由四个 8 位整数组成的数组?

目前我有以下代码,超级慢:

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 数组?

【问题讨论】:

  • 正常的 div/mod 操作有什么问题?
  • dtype 允许您以两种不同的方式查看数组。 docs.scipy.org/doc/numpy/reference/generated/numpy.dtype.html 上有一个这样做的例子。 np.dtype((np.int16, {'x':(np.int8,0), 'y':(np.int8,1)}))
  • 你有一个 32 位整数的 numpy 数组吗?给出您要处理的实际输入的示例。
  • 我刚刚在对@hpaulj 的回答的评论中提出了这个建议。如果x是数组,可以使用y = x.view(np.uint8).reshape(x.shape + (4,))
  • @WarrenWeckesser 在几乎每个人都使用的 little endian 平台上,视图会给他相反的字节顺序 (1 -> [1, 0, 0, 0]),因此在重塑视图后可能需要 [:, ::-1]。但是,是的,单线绝对是解决这个问题的方法。

标签: python numpy vectorization


【解决方案1】:

使用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')]))

数组([0, 0, 0])

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

在这种情况下,生成的数组看起来和行为都像一个简单的 int32 数组,但也定义了只使用 int32 的一个字节的字段(有点像 Fortran 等效)。

获取 4 个字节的二维数组的一种方法是:

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

【讨论】:

  • 使用 dtype 创建视图绝对是正确的方法。如果x 是一个np.int32 的连续数组,它可以像y = x.view(np.uint8).reshape(x.shape + (4,)) 一样简单。
【解决方案2】:

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 package

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

    您可以利用 struct 包的一个好处是您可以非常有效地执行此 int32int8

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

    【讨论】:

    • 谢谢,这绝对比我最初的方法快得多。虽然比基于视图的方法慢得多。
    • 确实如此。因为基于视图的方法不会生成新数据;它只是使用不同类型的指针来枚举。另一方面,struct.unpack 需要创建一大堆 Python 对象,这会影响性能。感谢您的提问,我学到了一些新东西。
    【解决方案4】:

    仅使用 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)
    

    【讨论】:

    猜你喜欢
    • 2020-10-17
    • 1970-01-01
    • 1970-01-01
    • 2012-06-11
    • 1970-01-01
    • 2019-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多