如何高效统计二维NumPy数组中二进制编码向量的出现频率?
高效统计大规模二进制向量出现频率的方法
问题描述
我有一个存储大量二进制编码向量的二维NumPy数组(示例如下,实际单向量元素数超8000,总向量数超58000):
import numpy as np labels = np.array([ [0, 1, 0, 0, 1, 0], [0, 0, 1, 0, 0, 1], [0, 1, 0, 0, 1, 0] ])
我需要统计每个完整向量的出现频率(示例的频率表应为[2,1]),类似Scipy的itemfreq或NumPy的histogram但针对整个向量。我自己写了一段双重循环的代码,虽然能运行,但数据量大时效率极低:
def get_label_set_freq_table(labels): uniques = np.empty_like(labels) freq_table = np.zeros(shape=labels.shape[0]) equal = False for idx,row in enumerate(labels): for lbl_idx,label_set in enumerate(uniques): if np.array_equal(row,label_set): equal = True freq_table[lbl_idx] += 1 break if not equal: uniques[idx] = row freq_table[idx] += 1 equal = False return freq_table
求更高效的实现方式?
解决方案
你的代码用双重循环逐行比对,时间复杂度是O(n²),数据量到5万+的时候肯定会卡顿。核心优化思路是把每个向量转换成可快速哈希、比较的唯一标识,用哈希表或NumPy向量化操作来统计,把时间复杂度降到**O(n)**级别。以下是几种实用的高效方法:
方法1:Python原生collections.Counter(直观易实现)
把每个NumPy行向量转成不可变的元组(元组可作为字典键),然后用Counter直接计数:
import numpy as np from collections import Counter def fast_freq_counter(labels): # 生成元组序列(内存友好的生成器) tuple_rows = (tuple(row) for row in labels) # 统计频率 count_dict = Counter(tuple_rows) # 转换成你需要的格式:唯一向量数组 + 频率数组 unique_vectors = np.array(list(count_dict.keys())) freq_table = np.array(list(count_dict.values())) return unique_vectors, freq_table
这个方法比双重循环快几个数量级,适合中等规模的数据,代码也容易理解。
方法2:NumPyview二进制转整数(最优解,适合二进制向量)
因为你的向量是二进制的,我们可以把每个向量当成二进制数转成整数(或大整数块),用NumPy的unique直接统计:
import numpy as np def binary_vector_freq(labels): # 把二进制向量转成连续的整数块(这里用uint64,可根据向量长度调整类型) # view操作是零拷贝,效率极高 as_integers = labels.view(np.uint64).reshape(labels.shape[0], -1) # 用np.unique统计唯一值和频率 unique_integers, freq_table = np.unique(as_integers, axis=0, return_counts=True) # 可选:还原回原始二进制向量 unique_vectors = unique_integers.view(labels.dtype).reshape(unique_integers.shape[0], labels.shape[1]) return unique_vectors, freq_table
这是最快的方法,完全用NumPy向量化操作,没有Python循环。如果向量长度超过单个整数类型的位数(比如uint64是64位),可以把向量分成多个块转成整数元组,再用np.unique处理。
方法3:Pandasvalue_counts(适合已有Pandas栈的场景)
如果你的项目已经在使用Pandas,直接把数组转成DataFrame用value_counts统计:
import numpy as np import pandas as pd def pandas_freq_counter(labels): df = pd.DataFrame(labels) freq_series = df.value_counts() # 转换成需要的格式 unique_vectors = freq_series.index.to_numpy() freq_table = freq_series.values return unique_vectors, freq_table
Pandas的value_counts底层做了优化,效率和Counter相近,代码简洁。
性能总结
| 方法 | 时间复杂度 | 适用场景 |
|---|---|---|
| 原始双重循环 | O(n²) | 极小数据量(不推荐) |
| Counter元组法 | O(n) | 中等规模数据,易实现 |
| NumPy view整数法 | O(n) | 大规模二进制向量,性能最优 |
| Pandas value_counts | O(n) | 已有Pandas栈的项目 |
内容的提问来源于stack exchange,提问作者Alber8295
相关产品推荐
相关产品推荐

