如何基于含出现频率的字典/DataFrame绘制boxplot?
解决方案
结论:完全不需要展开成列表
大数据集下展开会直接导致内存溢出,完全没必要。我们可以通过计算加权分位数的方式,直接基于压缩的频率数据生成符合要求的箱线图。
步骤1:处理DataFrame格式的频率数据
假设你的DataFrame包含len(长度)、num_occurrence(出现次数)、dt(日期)字段,具体实现如下:
1.1 定义加权分位数计算函数
这个函数会根据出现次数作为权重,计算箱线图所需的核心统计量,支持异常值过滤(符合1.5*IQR规则):
import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns def weighted_percentile(data, weights, percents): data = np.asarray(data) weights = np.asarray(weights) # 按长度排序 sorted_idx = np.argsort(data) sorted_data = data[sorted_idx] sorted_weights = weights[sorted_idx] # 计算累计权重 cum_weights = np.cumsum(sorted_weights) total_weight = cum_weights[-1] # 定位分位数对应的权重位置并插值得到结果 percentile_points = [p * total_weight for p in percents] return np.interp(percentile_points, cum_weights, sorted_data) def weighted_boxplot_stats(data, weights): # 计算四分位数 q25, q50, q75 = weighted_percentile(data, weights, [0.25, 0.5, 0.75]) iqr = q75 - q25 lower_bound = q25 - 1.5 * iqr upper_bound = q75 + 1.5 * iqr # 过滤异常值 mask = (data >= lower_bound) & (data <= upper_bound) filtered_data = data[mask] filtered_weights = weights[mask] # 处理过滤后无数据的极端情况 if len(filtered_data) == 0: min_val = data.min() max_val = data.max() else: min_val = weighted_percentile(filtered_data, filtered_weights, [0.0])[0] max_val = weighted_percentile(filtered_data, filtered_weights, [1.0])[0] return pd.Series([min_val, q25, q50, q75, max_val], index=['min', '25%', '50%', '75%', 'max'])
1.2 按日期分组计算加权统计量
# 假设你的DataFrame名为df grouped_stats = df.groupby('dt').apply( lambda x: weighted_boxplot_stats(x['len'], x['num_occurrence']) ).reset_index()
1.3 绘制加权箱线图
基于计算好的统计量绘图,可套用seaborn样式美化:
plt.figure(figsize=(12, 6)) # 整理箱线图所需数据:每个日期对应一组[min, 25%, 50%, 75%, max] box_data = grouped_stats[['min', '25%', '50%', '75%', 'max']].values.T # 绘制箱线图 box = plt.boxplot(box_data, labels=grouped_stats['dt'], patch_artist=True) # 套用seaborn样式 sns.set_style("whitegrid") for patch in box['boxes']: patch.set_facecolor('#88c999') plt.title('加权长度分布箱线图(按日期分组)') plt.xlabel('日期') plt.ylabel('长度') plt.xticks(rotation=45) plt.tight_layout() plt.show()
步骤2:处理defaultdict格式的频率数据
先将字典转换为带日期字段的DataFrame(若字典对应多个日期,需补充日期匹配逻辑),再按上述DataFlow流程处理:
from collections import defaultdict # 假设你的defaultdict为len_counts,键是长度,值是出现次数 len_counts = defaultdict(int) # 数据填充示例 len_counts[5] = 100 len_counts[6] = 200 len_counts[7] = 150 # 转换为DataFrame(需补充对应日期) df_from_dict = pd.DataFrame([ {'len': length, 'num_occurrence': count, 'dt': '2023-01-01'} for length, count in len_counts.items() ]) # 后续步骤同DataFrame处理流程
内容的提问来源于stack exchange,提问作者rgowtham
相关产品推荐
相关产品推荐

