如何通过从Redis获取的bytearray从ndarray中选取行?
从Redis位串提取索引,选取NumPy数组对应行
我来帮你搞定这个用Redis存储的位集合从NumPy数组里挑对应行的需求!先理清楚你的场景:你通过Redis的set和setbit操作生成了一个位串,拿到的原始字节是b'\xc0\x08',转换成二进制后是0b1100000000001000——也就是第0、1、12位被置为1,目标是从NumPy数组中提取这些索引对应的元素,最终得到array([0, 1, 12])。
下面给你两种可行的解决方案,其中@paul-panzer的方案在性能上更优:
方案1:Python字符串+列表推导手动解析
这种方法比较直观,先把字节转换成二进制字符串,再遍历找出所有为1的位索引:
import numpy as np a_raw = b'\xc0\x08' # 将字节转换为整数,再转为二进制字符串,补前导零保证位长度准确 bit_str = bin(int.from_bytes(a_raw, byteorder='big'))[2:].zfill(len(a_raw)*8) # 筛选出所有值为'1'的位索引 indices = np.array([i for i, bit in enumerate(bit_str) if bit == '1']) # 测试数组(注意要包含索引12,所以数组长度设为13) arr = np.arange(13) result = arr[indices] print(result) # 输出:array([ 0, 1, 12])
方案2:@paul-panzer的NumPy高效方案
利用NumPy的内置函数直接处理字节数据,避免Python层面的循环操作,速度会快很多:
import numpy as np a_raw = b'\xc0\x08' # 将字节转为uint8数组,再解包为比特序列(big字节序) bits = np.unpackbits(np.frombuffer(a_raw, dtype=np.uint8), bitorder='big') # 找出所有值为1的位索引 indices = np.where(bits)[0] arr = np.arange(13) result = arr[indices] print(result) # 输出:array([ 0, 1, 12])
性能对比测试
为了直观展示两种方案的速度差异,我们可以用timeit做基准测试:
import timeit setup = ''' import numpy as np a_raw = b'\\xc0\\x08' ''' # 测试方案1的耗时 t1 = timeit.timeit(''' bit_str = bin(int.from_bytes(a_raw, byteorder='big'))[2:].zfill(len(a_raw)*8) indices = np.array([i for i, bit in enumerate(bit_str) if bit == '1']) ''', setup=setup, number=100000) # 测试方案2的耗时 t2 = timeit.timeit(''' bits = np.unpackbits(np.frombuffer(a_raw, dtype=np.uint8), bitorder='big') indices = np.where(bits)[0] ''', setup=setup, number=100000) print(f"方案1耗时: {t1:.4f}秒") print(f"方案2耗时: {t2:.4f}秒")
测试结果通常会显示方案2的耗时远低于方案1,这是因为NumPy的unpackbits和where都是基于C实现的底层操作,比Python层面的字符串遍历和列表推导高效得多,尤其在处理大量位数据时优势会更明显。
内容的提问来源于stack exchange,提问作者Jacob Eggers




