HvPlot+Bokeh对数尺度下小提琴图显示异常的技术求助
解决HvPlot+Bokeh小提琴图对数尺度下箱型异常的问题
问题根源
当设置logy=True时,HvPlot默认是先基于原始数据计算箱型统计量(分位数、极值等),再将这些数值映射到对数坐标轴,而非在对数空间内计算统计量。这就导致分位数在对数轴上的位置与实际分布脱节,出现Q2(中位数)超出小提琴分布范围的异常。
解决方案
方法1:自定义箱型统计量(无需修改原始数据)
手动在对数空间计算分位数、极值等统计量,转换回原始尺度后,通过box_plot_kwargs传入小提琴图,让箱型与对数空间的分布匹配。
代码示例:
import hvplot.pandas import pandas as pd import numpy as np hvplot.extension('bokeh') temp = [3.021e+00, 1.726e+00, 2.092e+00, 1.721e+00, np.nan, 3.182e+00, 8.335e+00, 9.851e+00, 9.743e+00, 3.153e+01, np.nan, 2.458e+01, 1.138e+01, 9.886e+01, 1.638e+01, 1.084e+00, 1.573e+00, 2.486e-01, 4.257e-01, 2.447e-01, 3.540e-01, 3.644e-01, 3.296e-01, 3.450e-01, 2.739e-01, 3.404e-01, 4.427e-01, 8.187e-01, 4.360e-01, 3.556e-01, 3.107e+00, 9.722e-01, 1.572e+00, 7.006e-01, 2.344e-01, 9.597e-01, 1.372e-01, 2.530e-01, 1.100e+00, 5.791e+00, 1.093e+01, 4.249e+00, 4.841e+00, 5.234e+00, 1.355e+01, 1.423e+01, 2.478e+01, np.nan, 4.721e+01, np.nan, 9.234e+01, np.nan, 1.045e+02, np.nan, 8.989e+01, np.nan, 9.330e+01, np.nan, 1.151e+02, np.nan, 2.193e+02, np.nan, 1.705e+02, np.nan, 3.835e+02, np.nan, 1.689e+03, np.nan, 1.879e+03, np.nan, 1.898e+03, np.nan, 2.374e+03, np.nan, 3.079e+03, 3.611e+03, 3.926e+03] test = pd.Series(temp) # 1. 过滤NaN,计算对数空间的统计量 valid_data = test.dropna() log_data = np.log10(valid_data) # 计算对数空间的分位数和极值 q1_log = np.percentile(log_data, 25) q2_log = np.percentile(log_data, 50) q3_log = np.percentile(log_data, 75) iqr_log = q3_log - q1_log lower_whisker_log = max(log_data.min(), q1_log - 1.5*iqr_log) upper_whisker_log = min(log_data.max(), q3_log + 1.5*iqr_log) # 转换回原始尺度 custom_stats = { 'q1': 10**q1_log, 'median': 10**q2_log, 'q3': 10**q3_log, 'lower': 10**lower_whisker_log, 'upper': 10**upper_whisker_log } # 2. 绘制小提琴图,传入自定义统计量 plt = test.hvplot.violin( logy=True, box_plot_kwargs={'stats': custom_stats} ) hvplot.show(plt)
方法2:直接使用Holoviews底层控制
HvPlot是Holoviews的封装,直接使用Holoviews的Violin元素可以更灵活地控制分布计算逻辑,在对数空间生成小提琴分布,再设置y轴为对数尺度。
代码示例:
import hvplot.pandas import pandas as pd import numpy as np import holoviews as hv hv.extension('bokeh') temp = [3.021e+00, 1.726e+00, 2.092e+00, 1.721e+00, np.nan, 3.182e+00, 8.335e+00, 9.851e+00, 9.743e+00, 3.153e+01, np.nan, 2.458e+01, 1.138e+01, 9.886e+01, 1.638e+01, 1.084e+00, 1.573e+00, 2.486e-01, 4.257e-01, 2.447e-01, 3.540e-01, 3.644e-01, 3.296e-01, 3.450e-01, 2.739e-01, 3.404e-01, 4.427e-01, 8.187e-01, 4.360e-01, 3.556e-01, 3.107e+00, 9.722e-01, 1.572e+00, 7.006e-01, 2.344e-01, 9.597e-01, 1.372e-01, 2.530e-01, 1.100e+00, 5.791e+00, 1.093e+01, 4.249e+00, 4.841e+00, 5.234e+00, 1.355e+01, 1.423e+01, 2.478e+01, np.nan, 4.721e+01, np.nan, 9.234e+01, np.nan, 1.045e+02, np.nan, 8.989e+01, np.nan, 9.330e+01, np.nan, 1.151e+02, np.nan, 2.193e+02, np.nan, 1.705e+02, np.nan, 3.835e+02, np.nan, 1.689e+03, np.nan, 1.879e+03, np.nan, 1.898e+03, np.nan, 2.374e+03, np.nan, 3.079e+03, 3.611e+03, 3.926e+03] test = pd.Series(temp).dropna() log_data = np.log10(test) # 在对数空间生成小提琴图,再将y轴转换为原始尺度的对数轴 violin = hv.Violin(log_data, vdims='value').opts( yformatter='%e', # 用科学计数法显示原始尺度数值 yaxis_type='log', yticks=[10**i for i in range(-1, 4)] # 自定义刻度对应原始尺度 ) hvplot.show(violin)
说明
两种方法都无需修改原始数据集:
- 方法1保留了HvPlot的便捷性,仅通过自定义统计量修正箱型位置;
- 方法2利用Holoviews的底层能力,直接在对数空间生成分布,更贴合对数尺度的展示逻辑。
内容的提问来源于stack exchange,提问作者Murnawful
相关产品推荐
相关产品推荐

