使用Python Bokeh绘制堆叠条形图遇vbar_stack属性错误求助
Hey there! Let's get that stacked bar chart working for you. The error you're seeing—AttributeError: 'Figure' object has no attribute 'vbar_stack'—happens because Bokeh doesn't have a vbar_stack method for vertical stacked bars. Instead, we need to adjust how we structure our data and use Bokeh's built-in tools to create the stacked effect.
Step 1: Correctly Structure Your Data
First, let's clean up the data aggregation to get a wide-format DataFrame where each class has separate columns for "yes" and "no" counts. This makes it easier to build stacked bars in Bokeh:
import pandas as pd from bokeh.plotting import figure, show, ColumnDataSource from bokeh.models import LabelSet from bokeh.palettes import Viridis2 # Your raw data raw_data = { 'Class': ['S', 'S', 'G', 'P', 'P', 'V', 'G', 'V', 'V', 'V', 'P'], 'Class_Change': ['yes', 'yes', 'yes', 'yes', 'yes', 'no', 'yes', 'no', 'no', 'yes', 'no'] } df_class = pd.DataFrame(raw_data) # Aggregate counts and pivot to wide format df_counts = df_class.groupby(['Class', 'Class_Change']).size().unstack(fill_value=0)
This gives us a structured DataFrame like this:
| Class | no | yes |
|---|---|---|
| G | 0 | 2 |
| P | 1 | 2 |
| S | 0 | 2 |
| V | 3 | 1 |
Step 2: Build the Stacked Bar Chart
Now we'll create a ColumnDataSource with our wide-format data, then add two vertical bar layers (one for "no", one for "yes")—using the bottom parameter for the second layer to stack it on top of the first:
# Create data source source = ColumnDataSource(data={ 'Classes': df_counts.index.tolist(), 'no_counts': df_counts['no'].tolist(), 'yes_counts': df_counts['yes'].tolist(), 'colors': Viridis2 # Colors for "no" and "yes" segments }) # Initialize the plot plot = figure( x_range=df_counts.index.tolist(), y_range=(0, df_counts.sum(axis=1).max() + 0.5), # Add buffer for labels plot_height=350, plot_width=800, title="Class Change Status Counts", toolbar_location=None, tools="" ) # Add "no" bars (bottom layer) no_bars = plot.vbar( x='Classes', top='no_counts', width=0.9, color='colors[0]', source=source, legend_label='No' ) # Add "yes" bars (stacked on top of "no") yes_bars = plot.vbar( x='Classes', top='yes_counts', bottom='no_counts', width=0.9, color='colors[1]', source=source, legend_label='Yes' ) # Add count labels to each bar segment # Labels for "no" bars no_labels = LabelSet( x='Classes', y='no_counts', text='no_counts', level='glyph', y_offset=-15, # Adjust position for readability source=source, render_mode='canvas' ) # Labels for "yes" bars (positioned above the "no" bars) yes_labels = LabelSet( x='Classes', y='no_counts', text='yes_counts', level='glyph', y_offset=15, source=source, render_mode='canvas' ) plot.add_layout(no_labels) plot.add_layout(yes_labels) # Clean up plot styling plot.legend.title = "Change Status" plot.xaxis.axis_label = "Class" plot.yaxis.axis_label = "Count" plot.legend.location = "top_left" # Show the chart show(plot)
Key Fixes Explained:
- Data Format: We converted the grouped data to a wide format so each status ("yes"/"no") has its own column—this is essential for stacking in Bokeh.
- Stacking Logic: Instead of a non-existent
vbar_stackmethod, we use twovbarcalls: the second usesbottom='no_counts'to sit on top of the first layer. - Label Placement: We added separate labels for each bar segment, adjusting their position to be readable without overlapping.
内容的提问来源于stack exchange,提问作者Shuvayan Das

