Skip to content

Commit fe02b5f

Browse files
feat(bokeh): implement line-basic
Add basic line plot implementation for bokeh library with: - Line rendering with customizable color, width, and alpha - Optional markers (scatter points) on data points - Support for datetime x-axis - Automatic data sorting for proper line connection - Subtle grid styling (alpha=0.3) - 16:9 aspect ratio (1600x900 pixels) - Full input validation and type hints - Google-style docstrings
1 parent 58567b5 commit fe02b5f

2 files changed

Lines changed: 217 additions & 0 deletions

File tree

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
"""
2+
line-basic: Basic Line Plot
3+
Implementation for: bokeh
4+
Variant: default
5+
Python: 3.10+
6+
"""
7+
8+
from typing import TYPE_CHECKING, Optional
9+
10+
import numpy as np
11+
import pandas as pd
12+
from bokeh.models import ColumnDataSource
13+
from bokeh.plotting import figure
14+
15+
16+
if TYPE_CHECKING:
17+
from bokeh.plotting import Figure
18+
19+
20+
def create_plot(
21+
data: pd.DataFrame,
22+
x: str,
23+
y: str,
24+
title: Optional[str] = None,
25+
xlabel: Optional[str] = None,
26+
ylabel: Optional[str] = None,
27+
color: str = "steelblue",
28+
line_width: float = 2.0,
29+
marker: Optional[str] = None,
30+
marker_size: float = 8,
31+
alpha: float = 1.0,
32+
width: int = 1600,
33+
height: int = 900,
34+
**kwargs,
35+
) -> "Figure":
36+
"""
37+
Create a basic line plot visualizing trends over continuous or sequential data.
38+
39+
Args:
40+
data: Input DataFrame with required columns
41+
x: Column name for x-axis values (numeric or datetime)
42+
y: Column name for y-axis values (numeric)
43+
title: Plot title (optional)
44+
xlabel: Custom x-axis label (optional, defaults to column name)
45+
ylabel: Custom y-axis label (optional, defaults to column name)
46+
color: Line color (default: "steelblue")
47+
line_width: Width of the line (default: 2.0)
48+
marker: Marker style for data points (optional, e.g., "circle", "square")
49+
marker_size: Size of markers if enabled (default: 8)
50+
alpha: Line transparency (default: 1.0)
51+
width: Figure width in pixels (default: 1600)
52+
height: Figure height in pixels (default: 900)
53+
**kwargs: Additional parameters
54+
55+
Returns:
56+
Bokeh Figure object
57+
58+
Raises:
59+
ValueError: If data is empty
60+
KeyError: If required columns not found
61+
TypeError: If y column contains non-numeric data
62+
63+
Example:
64+
>>> data = pd.DataFrame({'x': [1, 2, 3, 4, 5], 'y': [2, 4, 3, 5, 6]})
65+
>>> fig = create_plot(data, 'x', 'y')
66+
"""
67+
# Input validation
68+
if data.empty:
69+
raise ValueError("Data cannot be empty")
70+
71+
# Check required columns
72+
for col in [x, y]:
73+
if col not in data.columns:
74+
available = ", ".join(data.columns)
75+
raise KeyError(f"Column '{col}' not found. Available columns: {available}")
76+
77+
# Check if y column is numeric
78+
if not pd.api.types.is_numeric_dtype(data[y]):
79+
raise TypeError(f"Column '{y}' must contain numeric data")
80+
81+
# Sort data by x to ensure proper line connection
82+
plot_data = data[[x, y]].dropna().sort_values(by=x)
83+
84+
# Determine x-axis type
85+
x_axis_type = "datetime" if pd.api.types.is_datetime64_any_dtype(plot_data[x]) else "auto"
86+
87+
# Create ColumnDataSource
88+
source = ColumnDataSource(data={"x": plot_data[x], "y": plot_data[y]})
89+
90+
# Create figure
91+
p = figure(
92+
width=width,
93+
height=height,
94+
title=title or "Line Plot",
95+
x_axis_type=x_axis_type,
96+
toolbar_location="above",
97+
tools="pan,wheel_zoom,box_zoom,reset,save",
98+
)
99+
100+
# Plot line
101+
p.line(x="x", y="y", source=source, line_color=color, line_width=line_width, line_alpha=alpha)
102+
103+
# Add markers if specified
104+
if marker:
105+
p.scatter(x="x", y="y", source=source, size=marker_size, color=color, alpha=alpha, marker=marker)
106+
107+
# Labels
108+
p.xaxis.axis_label = xlabel or x
109+
p.yaxis.axis_label = ylabel or y
110+
111+
# Styling
112+
p.title.text_font_size = "14pt"
113+
p.title.align = "center"
114+
p.xaxis.axis_label_text_font_size = "12pt"
115+
p.yaxis.axis_label_text_font_size = "12pt"
116+
p.xgrid.grid_line_alpha = 0.3
117+
p.ygrid.grid_line_alpha = 0.3
118+
p.xgrid.grid_line_dash = [6, 4]
119+
p.ygrid.grid_line_dash = [6, 4]
120+
121+
return p
122+
123+
124+
if __name__ == "__main__":
125+
# Sample data for testing - simulating time series data
126+
np.random.seed(42)
127+
n_points = 50
128+
129+
# Create sequential x values
130+
x_values = np.arange(n_points)
131+
# Create y values with a trend and some noise
132+
y_values = 10 + 0.5 * x_values + np.random.randn(n_points) * 2
133+
134+
data = pd.DataFrame({"Time": x_values, "Value": y_values})
135+
136+
# Create plot
137+
fig = create_plot(
138+
data,
139+
x="Time",
140+
y="Value",
141+
title="Basic Line Plot Example",
142+
xlabel="Time (units)",
143+
ylabel="Measurement Value",
144+
color="steelblue",
145+
line_width=2.5,
146+
marker="circle",
147+
marker_size=6,
148+
)
149+
150+
# Save as PNG using webdriver-manager for automatic chromedriver
151+
from bokeh.io import export_png
152+
from selenium import webdriver
153+
from selenium.webdriver.chrome.options import Options
154+
from selenium.webdriver.chrome.service import Service
155+
from webdriver_manager.chrome import ChromeDriverManager
156+
157+
chrome_options = Options()
158+
chrome_options.add_argument("--headless")
159+
chrome_options.add_argument("--no-sandbox")
160+
chrome_options.add_argument("--disable-dev-shm-usage")
161+
162+
service = Service(ChromeDriverManager().install())
163+
driver = webdriver.Chrome(service=service, options=chrome_options)
164+
165+
export_png(fig, filename="plot.png", webdriver=driver)
166+
driver.quit()
167+
print("Plot saved to plot.png")

specs/line-basic.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# line-basic: Basic Line Plot
2+
3+
**Spec Version:** 1.0.0
4+
5+
## Description
6+
7+
A fundamental line plot that visualizes trends over continuous or sequential data. Ideal for showing how values change over time or across ordered categories, connecting data points with straight line segments.
8+
9+
## Data Requirements
10+
11+
- **x**: Numeric or datetime column for x-axis values (typically time or sequence)
12+
- **y**: Numeric column for y-axis values
13+
14+
## Optional Parameters
15+
16+
- `title`: Plot title (type: str, default: None)
17+
- `xlabel`: X-axis label (type: str, default: column name)
18+
- `ylabel`: Y-axis label (type: str, default: column name)
19+
- `color`: Line color (type: str, default: "steelblue")
20+
- `line_width`: Width of the line (type: float, default: 2.0)
21+
- `marker`: Marker style for data points (type: str, default: None)
22+
- `marker_size`: Size of markers if enabled (type: float, default: 8)
23+
- `alpha`: Line transparency (type: float, default: 1.0)
24+
25+
## Quality Criteria
26+
27+
- [ ] X and Y axes are labeled with column names or custom labels
28+
- [ ] Grid is visible but subtle (alpha <= 0.3)
29+
- [ ] Line is clearly visible with appropriate width
30+
- [ ] No overlapping axis labels or tick marks
31+
- [ ] Data points are connected in correct order
32+
- [ ] Figure uses 16:9 aspect ratio
33+
- [ ] Type hints and input validation present
34+
35+
## Expected Output
36+
37+
A line plot with data points connected by line segments. The x-axis represents the independent variable (often time or sequence), and the y-axis represents the dependent variable being measured. The line should be clearly visible against the grid background, with proper axis labels and title (if provided). The plot should handle various data sizes gracefully and maintain readability.
38+
39+
## Tags
40+
41+
line, trend, time-series, basic, 2d
42+
43+
## Use Cases
44+
45+
- Tracking stock prices over time
46+
- Monitoring temperature changes throughout the day
47+
- Displaying website traffic over months
48+
- Showing sales growth across quarters
49+
- Visualizing sensor readings over time
50+
- Analyzing experiment results over sequential trials

0 commit comments

Comments
 (0)