求助:700万条数据集绘制Histogram的Python性能优化方案
700万条数据频率统计与直方图绘制的性能优化方案
瓶颈分析
你当前代码运行缓慢的核心原因:
- 纯Python循环逐行读取并构建列表,处理700万条数据的开销极大
- 对字符串类型数据执行
np.unique或手动字典统计,效率远低于数值型数组的向量化操作 - 若唯一值数量过多,
plt.bar绘制大量柱子会占用巨量计算资源
优化方案
1. 优先转为数值类型(关键提速点)
如果你的数据是整数/浮点数,绝对不要以字符串形式处理,直接读取为numpy数值数组,这能让速度提升几个数量级。
2. 高效读取文件
用numpy的loadtxt直接读取文件为数值数组,彻底避免纯Python循环:
import numpy as np import matplotlib.pyplot as plt file_path = "D:/results/planarity2.txt" # 整数用dtype=int,浮点数用dtype=np.float64 data = np.loadtxt(file_path, dtype=np.float64)
3. 快速统计频率
方案A:numpy向量化操作(最快)
- 整数类型数据用
np.bincount:
counts = np.bincount(data.astype(int)) unique_values = np.arange(len(counts)) # 过滤掉计数为0的项(可选) mask = counts > 0 unique_values = unique_values[mask] counts = counts[mask]
- 浮点数/非整数离散值用
np.unique直接处理数值数组:
unique_values, counts = np.unique(data, return_counts=True)
方案B:collections.Counter(字符串数据首选)
如果必须处理字符串类型数据,用Counter代替手动字典统计,内部实现比纯Python循环高效得多:
from collections import Counter with open(file_path, "r") as f: # 生成器直接传入,避免先构建大列表 value_frequency = Counter(line.strip() for line in f) unique_values = list(value_frequency.keys()) counts = list(value_frequency.values())
4. 优化绘图逻辑
如果唯一值数量极大(比如超过1000),plt.bar会因渲染过多柱子变得极慢,此时改用plt.hist自动分箱渲染:
plt.hist(data, bins='auto', edgecolor='black', alpha=0.7) plt.xlabel('数值') plt.ylabel('频率') plt.title('数值频率分布直方图') plt.show()
若必须绘制精确离散值:
- 只保留频率Top N的取值(比如前200个)
- 关闭
edgecolor减少渲染开销 - 对字符串标签添加旋转避免重叠:
plt.xticks(rotation=45, ha='right')
完整优化代码示例
数值型数据版本
import numpy as np import matplotlib.pyplot as plt file_path = "D:/results/planarity2.txt" # 快速读取数值数据 data = np.loadtxt(file_path, dtype=np.float64) # 快速统计频率 unique_values, counts = np.unique(data, return_counts=True) # 根据唯一值数量选择绘图方式 if len(unique_values) < 1000: plt.bar(unique_values, counts, edgecolor='black', alpha=0.7) else: plt.hist(data, bins='auto', edgecolor='black', alpha=0.7) plt.xlabel('数值') plt.ylabel('频率') plt.title('数值频率分布直方图') plt.show()
字符串型数据版本
from collections import Counter import matplotlib.pyplot as plt file_path = "D:/results/planarity2.txt" # 快速统计频率 with open(file_path, "r") as f: value_frequency = Counter(line.strip() for line in f) # 只保留前200个高频值(避免绘图卡顿) sorted_items = sorted(value_frequency.items(), key=lambda x: x[1], reverse=True)[:200] unique_values, counts = zip(*sorted_items) plt.bar(unique_values, counts, edgecolor='black', alpha=0.7) plt.xlabel('取值') plt.ylabel('频率') plt.title('取值频率分布直方图') plt.xticks(rotation=45, ha='right') plt.show()
内容的提问来源于stack exchange,提问作者youssef
相关产品推荐
相关产品推荐

