如何创建一个二维二进制计数的Numpy数组?

3 投票
4 回答
96 浏览
提问于 2025-04-14 16:05

创建一个像这样的数组:

[[0,0,0,0]
[0,0,0,1]
[0,0,1,0]
[0,0,1,1]
[0,1,0,0]
.
.
.
[1,1,1,1]]

我试过用binary_repr()来生成二进制数字的字符串,然后把它变成数组,但它不能把整个数组都转换成二进制表示。

4 个回答

0

如果我理解得没错,你可以使用 itertools.product 这个工具:

from itertools import product

arr = np.fromiter(product([0, 1], repeat=4), dtype=(int, 4))
print(arr)

输出结果是:

[[0 0 0 0]
 [0 0 0 1]
 [0 0 1 0]
 [0 0 1 1]
 [0 1 0 0]
 [0 1 0 1]
 [0 1 1 0]
 [0 1 1 1]
 [1 0 0 0]
 [1 0 0 1]
 [1 0 1 0]
 [1 0 1 1]
 [1 1 0 0]
 [1 1 0 1]
 [1 1 1 0]
 [1 1 1 1]]
1

这里有两种快速的方法,可以从已有的i位数组构建出i+1位的数组。

这是对20位的基准测试(其他解决方案稍作调整,以确保我们都在做同样的事情):

  40 ms  binary_counting_1
  16 ms  binary_counting_2
1202 ms  Andrej_Kesely
4668 ms  Arne
  79 ms  mozway

代码:

def binary_counting_1(n, dtype):
    a = np.array([[]], dtype=dtype)
    for i in range(n):
        a = np.vstack(
            [np.c_[np.zeros(2**i, dtype), a],
             np.c_[np.ones(2**i, dtype), a]]
        )
    return a


def binary_counting_2(n, dtype):
    a = np.zeros((2**n, n), dtype)
    for i in range(n):
        m = 2**i
        a[m:2*m, -i:] = a[:m, -i:]
        a[m:2*m, ~i] = 1
    return a


def Andrej_Kesely(n, dtype):
    return np.fromiter(product([0, 1], repeat=n), dtype=(dtype, n))


def Arne(N_DIGITS, dtype):
    return np.array([list(format(i, 'b').zfill(N_DIGITS)) 
                for i in range(2 ** N_DIGITS)], dtype=dtype)


def mozway(n, dtype):
    n = 2**n
    a = np.arange(n)[:,None]
    M = (n-1).bit_length()
    N = -(M // -8)
    return np.hstack([np.unpackbits((a>>(8*x)).astype(dtype), axis=1)
                      for x in range(N-1, -1, -1)])[:, -M:]


funcs = binary_counting_1, binary_counting_2, Andrej_Kesely, Arne, mozway

import numpy as np
from time import time
from itertools import product

n, dtype = 20, 'uint8'
expect = funcs[0](n, dtype)
for _ in range(3):
    for f in funcs:
        t0 = time()
        result = f(n, dtype)
        print(f'{(time()-t0)*1e3 :4.0f} ms ', f.__name__)
        assert (result == expect).all()
        del result

在线尝试这个!

2

对于不超过255的数字,可以使用 numpy.unpackbits

np.unpackbits(np.arange(16, dtype=np.uint8)[:,None], axis=1)[:, -4:]

[:, -4:] 是用来保留最后4位(总共8位中的一部分)。

输出结果:

array([[0, 0, 0, 0],
       [0, 0, 0, 1],
       [0, 0, 1, 0],
       [0, 0, 1, 1],
       [0, 1, 0, 0],
       [0, 1, 0, 1],
       [0, 1, 1, 0],
       [0, 1, 1, 1],
       [1, 0, 0, 0],
       [1, 0, 0, 1],
       [1, 0, 1, 0],
       [1, 0, 1, 1],
       [1, 1, 0, 0],
       [1, 1, 0, 1],
       [1, 1, 1, 0],
       [1, 1, 1, 1]], dtype=uint8)

对于不超过65535的数字,你可以用相同的方法,把数字分成两部分(前8位和后8位):

这里有一些选定的数字示例(不是完整的范围):

a = np.array([0,1,2,254,255,256,257,512,1024])[:,None]

out = np.c_[np.unpackbits((a>>8).astype(np.uint8), axis=1),
            np.unpackbits((a&255).astype(np.uint8), axis=1)]

输出结果:

array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 0
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],  # 1
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],  # 2
       [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0],  # 254
       [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],  # 255
       [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],  # 256
       [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1],  # 257
       [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 512
       [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],  # 1024 
       ], dtype=uint8)

然后你可以继续这个过程,处理更大的数字:

a = np.array([0,1,2,254,255,256,257,512,1024,65535,65536,65537])[:,None]

np.c_[np.unpackbits((a>>16).astype(np.uint8), axis=1),
      np.unpackbits((a>>8).astype(np.uint8), axis=1),
      np.unpackbits(a.astype(np.uint8), axis=1),
      ]

array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
       [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]], dtype=uint8)

这是一个通用的方法,可以生成从1到n-1的所有数字:

def numpy_packbits(n):
    a = np.arange(n)[:,None]
    #N = int(np.ceil(np.log2(n)/8))
    #M = int(np.ceil(np.log2(n)))
    M = (n-1).bit_length()
    N = -(M // -8)
    return np.hstack([np.unpackbits((a>>(8*x)).astype(np.uint8), axis=1)
                      for x in range(N-1, -1, -1)])[:, -M:]

时间记录

在此输入图片描述

撰写回答