Skip to content

Commit ae66ffc

Browse files
feat(bokeh): implement bar-basic (#131)
## Summary Implements `bar-basic` for **bokeh** library. **Parent Issue:** #117 **Sub-Issue:** #121 **Base Branch:** `plot/bar-basic` **Attempt:** 1/3 ## Implementation - `plots/bokeh/vbar/bar-basic/default.py` ## Features - Uses `vbar` glyph for vertical bar chart - Supports categorical x-axis with `x_range` - Uses `ColumnDataSource` for data management - Customizable colors, alpha, and edge colors - Subtle grid on y-axis only (alpha=0.3) - Y-axis starts at zero for accurate comparison - Support for x-axis label rotation - Proper input validation with helpful error messages - Google-style docstrings with type hints Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
1 parent 3cafedf commit ae66ffc

1 file changed

Lines changed: 152 additions & 0 deletions

File tree

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
"""
2+
bar-basic: Basic Bar Chart
3+
Library: bokeh
4+
"""
5+
6+
from typing import TYPE_CHECKING
7+
8+
import pandas as pd
9+
from bokeh.io import export_png
10+
from bokeh.models import ColumnDataSource
11+
from bokeh.plotting import figure
12+
13+
14+
if TYPE_CHECKING:
15+
from bokeh.plotting import figure as Figure
16+
17+
18+
def create_plot(
19+
data: pd.DataFrame,
20+
category: str,
21+
value: str,
22+
figsize: tuple[int, int] = (1600, 900),
23+
color: str = "steelblue",
24+
edgecolor: str = "black",
25+
alpha: float = 0.8,
26+
title: str | None = None,
27+
xlabel: str | None = None,
28+
ylabel: str | None = None,
29+
rotation: int = 0,
30+
**kwargs,
31+
) -> "Figure":
32+
"""
33+
Create a basic vertical bar chart.
34+
35+
A fundamental bar chart that visualizes categorical data with numeric values,
36+
ideal for comparing quantities across discrete categories.
37+
38+
Args:
39+
data: Input DataFrame with categorical and numeric columns
40+
category: Column name for category labels (x-axis)
41+
value: Column name for numeric values (bar heights)
42+
figsize: Figure size as (width, height) in pixels. Defaults to (1600, 900).
43+
color: Bar fill color. Defaults to "steelblue".
44+
edgecolor: Bar edge color. Defaults to "black".
45+
alpha: Transparency level for bars (0-1). Defaults to 0.8.
46+
title: Plot title. Defaults to None.
47+
xlabel: X-axis label. Defaults to category column name.
48+
ylabel: Y-axis label. Defaults to value column name.
49+
rotation: Rotation angle for x-axis labels in degrees. Defaults to 0.
50+
**kwargs: Additional parameters passed to figure.
51+
52+
Returns:
53+
Bokeh figure object with the bar chart.
54+
55+
Raises:
56+
ValueError: If data is empty.
57+
KeyError: If required columns are not found in data.
58+
59+
Example:
60+
>>> data = pd.DataFrame({
61+
... 'category': ['A', 'B', 'C', 'D'],
62+
... 'value': [10, 25, 15, 30]
63+
... })
64+
>>> fig = create_plot(data, 'category', 'value', title='Sample Bar Chart')
65+
"""
66+
# Input validation
67+
if data.empty:
68+
raise ValueError("Data cannot be empty")
69+
70+
for col in [category, value]:
71+
if col not in data.columns:
72+
available = ", ".join(data.columns.tolist())
73+
raise KeyError(f"Column '{col}' not found. Available: {available}")
74+
75+
# Prepare data - drop NaN values
76+
plot_data = data[[category, value]].dropna()
77+
78+
# Get categories as list for x_range
79+
categories = plot_data[category].astype(str).tolist()
80+
81+
# Create ColumnDataSource
82+
source = ColumnDataSource(data={"categories": categories, "values": plot_data[value].tolist()})
83+
84+
# Set labels
85+
x_label = xlabel if xlabel is not None else category
86+
y_label = ylabel if ylabel is not None else value
87+
88+
# Create figure with categorical x-axis
89+
p = figure(
90+
width=figsize[0],
91+
height=figsize[1],
92+
x_range=categories,
93+
title=title,
94+
x_axis_label=x_label,
95+
y_axis_label=y_label,
96+
**kwargs,
97+
)
98+
99+
# Calculate bar width (0.8 of available space)
100+
bar_width = 0.8
101+
102+
# Add bars
103+
p.vbar(
104+
x="categories",
105+
top="values",
106+
width=bar_width,
107+
source=source,
108+
fill_color=color,
109+
fill_alpha=alpha,
110+
line_color=edgecolor,
111+
line_width=1,
112+
)
113+
114+
# Ensure y-axis starts at zero
115+
p.y_range.start = 0
116+
117+
# Style grid - subtle y-grid only
118+
p.xgrid.grid_line_color = None
119+
p.ygrid.grid_line_alpha = 0.3
120+
121+
# Apply x-axis label rotation if specified
122+
if rotation != 0:
123+
from math import pi
124+
125+
p.xaxis.major_label_orientation = rotation * pi / 180
126+
127+
# Style axis labels
128+
p.xaxis.axis_label_text_font_size = "12pt"
129+
p.yaxis.axis_label_text_font_size = "12pt"
130+
p.xaxis.major_label_text_font_size = "10pt"
131+
p.yaxis.major_label_text_font_size = "10pt"
132+
133+
# Style title if present
134+
if title:
135+
p.title.text_font_size = "14pt"
136+
p.title.align = "center"
137+
138+
return p
139+
140+
141+
if __name__ == "__main__":
142+
# Sample data for testing
143+
sample_data = pd.DataFrame(
144+
{"category": ["Product A", "Product B", "Product C", "Product D", "Product E"], "value": [45, 78, 52, 91, 63]}
145+
)
146+
147+
# Create plot
148+
fig = create_plot(sample_data, "category", "value", title="Sales by Product", xlabel="Product", ylabel="Sales ($)")
149+
150+
# Save
151+
export_png(fig, filename="plot.png")
152+
print("Plot saved to plot.png")

0 commit comments

Comments
 (0)