如何用Altair绘制并排分组直方图并将Y轴设为百分比
用Altair创建并排分组直方图(Y轴显示百分比)
问题描述
需要创建并排分组直方图(两个类别在同一分箱区间内的柱子并排显示),Y轴显示每个类别的百分比而非计数,但当前代码生成的是分面柱图,无法合并为单张图,同时希望优化百分比的计算方式。
当前尝试的代码:
import pandas as pd import numpy as np import altair as alt # Generate some example data true_scores = np.random.normal(1, 2, 1000) false_scores = np.random.normal(-2, 3, 1000) scores = np.concatenate((true_scores, false_scores)) labels = [True] * 1000 + [False] * 1000 df = pd.DataFrame({"score": scores, "label": labels}) # make plot alt.Chart(df).transform_joinaggregate( total='count(*)', groupby=['label'] ).transform_calculate( pct='1 / datum.total' ).mark_bar().encode( x=alt.X("label:N", axis=alt.Axis(title=None, labels=False)), y=alt.Y("sum(pct):Q", axis=alt.Axis(title="Count")), color=alt.Color('label:N'), column=alt.Column('score:Q', bin=alt.Bin(maxbins=100), header=alt.Header(labelOrient="bottom")), )
解决方案
要实现并排分组直方图并显示百分比,需要调整分箱方式、百分比计算逻辑,同时去掉分面(column)改用分组编码:
完整代码
import pandas as pd import numpy as np import altair as alt # 生成示例数据 true_scores = np.random.normal(1, 2, 1000) false_scores = np.random.normal(-2, 3, 1000) scores = np.concatenate((true_scores, false_scores)) labels = [True] * 1000 + [False] * 1000 df = pd.DataFrame({"score": scores, "label": labels}) alt.Chart(df).transform_bin( # 对score统一分箱,确保两个类别使用相同的区间 'binned_score', field='score', bin=alt.Bin(maxbins=20) ).transform_joinaggregate( # 计算每个(label, 分箱区间)的样本数 bin_count='count()', groupby=['label', 'binned_score'] ).transform_joinaggregate( # 计算每个label的总样本数 total='count()', groupby=['label'] ).transform_calculate( # 计算当前分箱占该label的百分比 pct='datum.bin_count / datum.total' ).mark_bar( # 设置柱子宽度,优化并排显示的间距 width=15 ).encode( x=alt.X('binned_score:Q', title='Score'), y=alt.Y('pct:Q', title='Percentage', axis=alt.Axis(format='%')), color=alt.Color('label:N', title='Label'), # 用xOffset实现同一分箱下不同label的柱子并排 xOffset='label:N' ).properties( width=600, height=400 )
关键说明
- 统一分箱:使用
transform_bin对score进行分箱,确保两个类别(True/False)使用完全相同的区间,避免分面导致的轴不一致问题。 - 百分比计算:
- 先用
transform_joinaggregate计算每个(label, 分箱区间)内的样本数 - 再计算每个label的总样本数
- 最后通过
transform_calculate得到当前分箱占该label的百分比
- 先用
- 并排显示:通过
xOffset='label:N'让同一分箱下的不同label柱子并排,配合mark_bar(width)调整柱子间距,替代原有的分面逻辑。 - Y轴格式化:用
axis=alt.Axis(format='%')将Y轴数值直接显示为百分比格式,无需手动转换。
内容的提问来源于stack exchange,提问作者Kshitij Sachan
相关产品推荐
相关产品推荐

