Skip to content

Commit e1a2fe9

Browse files
MarkusNeusingerclaude[bot]github-actions[bot]
authored
feat: add line-basic implementation (9 libraries) (#511)
## Summary Adds `line-basic` plot implementation. ### Libraries - **Merged:** 9 (all libraries) - **Not Feasible:** 0 ### Links - **Spec:** `specs/line-basic.md` - **Parent Issue:** #205 --- :robot: *Auto-generated by pyplots CI* --------- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
1 parent 402d7dc commit e1a2fe9

File tree

10 files changed

+313
-1323
lines changed

10 files changed

+313
-1323
lines changed
Lines changed: 23 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -1,154 +1,35 @@
11
"""
2-
line-basic: Basic Line Chart
3-
Implementation for: altair
4-
Variant: default
5-
Python: 3.10+
2+
line-basic: Basic Line Plot
3+
Library: altair
64
"""
75

8-
from typing import TYPE_CHECKING, Optional
9-
106
import altair as alt
117
import pandas as pd
128

139

14-
if TYPE_CHECKING:
15-
from altair import Chart
16-
17-
18-
def create_plot(
19-
data: pd.DataFrame,
20-
x: str,
21-
y: str,
22-
title: Optional[str] = None,
23-
xlabel: Optional[str] = None,
24-
ylabel: Optional[str] = None,
25-
color: str = "steelblue",
26-
linewidth: float = 2,
27-
marker: Optional[str] = None,
28-
marker_size: int = 60,
29-
width: int = 800,
30-
height: int = 450,
31-
**kwargs,
32-
) -> "Chart":
33-
"""
34-
Create a basic line chart showing trends over a continuous axis using altair.
35-
36-
Args:
37-
data: Input DataFrame with required columns
38-
x: Column name for x-axis values (typically time or sequence)
39-
y: Column name for y-axis values
40-
title: Plot title (optional)
41-
xlabel: Custom x-axis label (optional, defaults to x column name)
42-
ylabel: Custom y-axis label (optional, defaults to y column name)
43-
color: Line color (default: 'steelblue')
44-
linewidth: Width of the line (default: 2)
45-
marker: Marker style - 'circle', 'square', 'diamond', etc. (optional)
46-
marker_size: Size of markers if enabled (default: 60)
47-
width: Figure width in pixels (default: 800)
48-
height: Figure height in pixels (default: 450)
49-
**kwargs: Additional parameters for altair chart configuration
50-
51-
Returns:
52-
Altair Chart object
53-
54-
Raises:
55-
ValueError: If data is empty
56-
KeyError: If required columns not found
57-
58-
Example:
59-
>>> data = pd.DataFrame({
60-
... 'month': [1, 2, 3, 4, 5, 6],
61-
... 'sales': [100, 150, 130, 180, 200, 190]
62-
... })
63-
>>> chart = create_plot(data, x='month', y='sales')
64-
"""
65-
# Input validation
66-
if data.empty:
67-
raise ValueError("Data cannot be empty")
68-
69-
# Check required columns
70-
for col in [x, y]:
71-
if col not in data.columns:
72-
available = ", ".join(data.columns)
73-
raise KeyError(f"Column '{col}' not found. Available columns: {available}")
74-
75-
# Sort data by x-axis to ensure proper line connections
76-
plot_data = data.sort_values(by=x).copy()
10+
# Data
11+
data = pd.DataFrame({"time": [1, 2, 3, 4, 5, 6, 7], "value": [10, 15, 13, 18, 22, 19, 25]})
7712

78-
# Determine x encoding type based on data
79-
x_dtype = plot_data[x].dtype
80-
if pd.api.types.is_datetime64_any_dtype(x_dtype):
81-
x_encoding = f"{x}:T"
82-
else:
83-
x_encoding = f"{x}:Q"
84-
85-
# Create the line chart
86-
line = (
87-
alt.Chart(plot_data)
88-
.mark_line(color=color, strokeWidth=linewidth)
89-
.encode(
90-
x=alt.X(x_encoding, title=xlabel or x, axis=alt.Axis(labelAngle=0, labelLimit=200)),
91-
y=alt.Y(f"{y}:Q", title=ylabel or y, scale=alt.Scale(zero=False)),
92-
tooltip=[
93-
alt.Tooltip(x_encoding, title=xlabel or x),
94-
alt.Tooltip(f"{y}:Q", title=ylabel or y, format=".2f"),
95-
],
96-
)
97-
)
98-
99-
# Add markers if specified
100-
if marker:
101-
points = (
102-
alt.Chart(plot_data)
103-
.mark_point(color=color, size=marker_size, filled=True, shape=marker)
104-
.encode(
105-
x=alt.X(x_encoding),
106-
y=alt.Y(f"{y}:Q"),
107-
tooltip=[
108-
alt.Tooltip(x_encoding, title=xlabel or x),
109-
alt.Tooltip(f"{y}:Q", title=ylabel or y, format=".2f"),
110-
],
111-
)
112-
)
113-
chart_base = line + points
114-
else:
115-
chart_base = line
116-
117-
# Apply properties and configuration
118-
chart = (
119-
chart_base.properties(
120-
width=width, height=height, title=alt.TitleParams(text=title or "Line Chart", fontSize=16, anchor="middle")
121-
)
122-
.configure_view(strokeWidth=0)
123-
.configure_axis(grid=True, gridOpacity=0.3, gridDash=[3, 3], domainWidth=1, tickWidth=1)
13+
# Create line chart
14+
line = (
15+
alt.Chart(data)
16+
.mark_line(strokeWidth=2, color="#306998")
17+
.encode(
18+
x=alt.X("time:Q", title="Time", axis=alt.Axis(labelFontSize=16, titleFontSize=20)),
19+
y=alt.Y("value:Q", title="Value", axis=alt.Axis(labelFontSize=16, titleFontSize=20)),
12420
)
21+
)
12522

126-
return chart
23+
# Add points on the line for clarity
24+
points = alt.Chart(data).mark_point(size=100, color="#306998", filled=True).encode(x="time:Q", y="value:Q")
12725

26+
# Combine and configure chart
27+
chart = (
28+
(line + points)
29+
.properties(width=1600, height=900, title=alt.Title("Basic Line Plot", fontSize=20))
30+
.configure_view(strokeWidth=0)
31+
.configure_axis(grid=True, gridOpacity=0.3)
32+
)
12833

129-
if __name__ == "__main__":
130-
# Sample data for testing - monthly sales trend
131-
sample_data = pd.DataFrame(
132-
{
133-
"Month": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
134-
"Sales": [120, 150, 170, 165, 180, 220, 250, 240, 210, 190, 180, 200],
135-
}
136-
)
137-
138-
# Create plot with markers
139-
chart = create_plot(
140-
sample_data,
141-
x="Month",
142-
y="Sales",
143-
title="Monthly Sales Trend (2024)",
144-
xlabel="Month",
145-
ylabel="Sales (thousands)",
146-
color="steelblue",
147-
linewidth=2.5,
148-
marker="circle",
149-
marker_size=80,
150-
)
151-
152-
# Save as PNG
153-
chart.save("plot.png", scale_factor=2.0)
154-
print("Plot saved to plot.png")
34+
# Save as PNG (1600 × 900 at scale 3 = 4800 × 2700 px)
35+
chart.save("plot.png", scale_factor=3.0)
Lines changed: 33 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -1,154 +1,50 @@
11
"""
22
line-basic: Basic Line Plot
3-
Implementation for: bokeh
4-
Variant: default
5-
Python: 3.10+
3+
Library: bokeh
64
"""
75

8-
from typing import TYPE_CHECKING, Optional
9-
10-
import numpy as np
116
import pandas as pd
7+
from bokeh.io import export_png
128
from bokeh.models import ColumnDataSource
139
from bokeh.plotting import figure
10+
from selenium import webdriver
11+
from selenium.webdriver.chrome.options import Options
12+
from selenium.webdriver.chrome.service import Service
1413

1514

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]
15+
# Data
16+
data = pd.DataFrame({"time": [1, 2, 3, 4, 5, 6, 7], "value": [10, 15, 13, 18, 22, 19, 25]})
12017

121-
return p
18+
source = ColumnDataSource(data={"x": data["time"], "y": data["value"]})
12219

20+
# Create figure
21+
p = figure(width=4800, height=2700, title="Basic Line Plot", x_axis_label="Time", y_axis_label="Value")
12322

124-
if __name__ == "__main__":
125-
from bokeh.io import export_png
23+
# Plot line
24+
p.line(x="x", y="y", source=source, line_width=2, line_color="#306998")
12625

127-
# Sample data for testing - simulating time series data
128-
np.random.seed(42)
129-
n_points = 50
26+
# Add markers at data points
27+
p.scatter(x="x", y="y", source=source, size=8, color="#306998")
13028

131-
# Create sequential x values
132-
x_values = np.arange(n_points)
133-
# Create y values with a trend and some noise
134-
y_values = 10 + 0.5 * x_values + np.random.randn(n_points) * 2
29+
# Styling
30+
p.title.text_font_size = "20pt"
31+
p.xaxis.axis_label_text_font_size = "20pt"
32+
p.yaxis.axis_label_text_font_size = "20pt"
33+
p.xaxis.major_label_text_font_size = "16pt"
34+
p.yaxis.major_label_text_font_size = "16pt"
35+
p.grid.grid_line_alpha = 0.3
13536

136-
data = pd.DataFrame({"Time": x_values, "Value": y_values})
37+
# Setup Chrome/Chromium webdriver for PNG export
38+
chrome_options = Options()
39+
chrome_options.add_argument("--headless=new")
40+
chrome_options.add_argument("--no-sandbox")
41+
chrome_options.add_argument("--disable-dev-shm-usage")
42+
chrome_options.add_argument("--disable-gpu")
13743

138-
# Create plot
139-
fig = create_plot(
140-
data,
141-
x="Time",
142-
y="Value",
143-
title="Basic Line Plot Example",
144-
xlabel="Time (units)",
145-
ylabel="Measurement Value",
146-
color="steelblue",
147-
line_width=2.5,
148-
marker="circle",
149-
marker_size=6,
150-
)
44+
# Use system chromedriver (pre-installed on GitHub Actions runners)
45+
service = Service()
46+
driver = webdriver.Chrome(service=service, options=chrome_options)
15147

152-
# Save as PNG
153-
export_png(fig, filename="plot.png")
154-
print("Plot saved to plot.png")
48+
# Save
49+
export_png(p, filename="plot.png", webdriver=driver)
50+
driver.quit()

0 commit comments

Comments
 (0)