Skip to content

Commit c19a15c

Browse files
chore: merge feature branch
2 parents a947457 + 0786958 commit c19a15c

6 files changed

Lines changed: 1566 additions & 1666 deletions

File tree

.github/workflows/ci-plottest.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ jobs:
6666
echo "📦 Installing: uv sync $EXTRAS"
6767
uv sync $EXTRAS
6868
69-
- name: Setup Chrome for Highcharts
70-
if: steps.detect_libs.outputs.has_plots == 'true' && contains(steps.detect_libs.outputs.libraries, 'highcharts')
69+
- name: Setup Chrome for Selenium-based libraries
70+
if: steps.detect_libs.outputs.has_plots == 'true' && (contains(steps.detect_libs.outputs.libraries, 'highcharts') || contains(steps.detect_libs.outputs.libraries, 'bokeh'))
7171
uses: browser-actions/setup-chrome@v1
7272
with:
7373
chrome-version: stable
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()

plots/highcharts/line/line-basic/default.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@
9292
chrome_options.add_argument("--no-sandbox")
9393
chrome_options.add_argument("--disable-dev-shm-usage")
9494
chrome_options.add_argument("--disable-gpu")
95-
chrome_options.add_argument("--window-size=4900,2800")
95+
chrome_options.add_argument("--window-size=4800,2700")
9696

9797
driver = webdriver.Chrome(options=chrome_options)
9898
driver.get(f"file://{temp_path}")

plots/matplotlib/plot/line-basic/default.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
# Create plot
1414
fig, ax = plt.subplots(figsize=(16, 9))
15-
ax.plot(data["time"], data["value"], linewidth=2, color="#306998", marker="o", markersize=4)
15+
ax.plot(data["time"], data["value"], linewidth=2, color="#306998", marker="o", markersize=8)
1616

1717
# Labels and styling
1818
ax.set_xlabel("Time", fontsize=20)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ plotting = [
7575
lib-matplotlib = ["matplotlib>=3.9.0", "numpy>=1.26.0", "pandas>=2.2.0"]
7676
lib-seaborn = ["seaborn>=0.13.0", "matplotlib>=3.9.0", "numpy>=1.26.0", "pandas>=2.2.0"]
7777
lib-plotly = ["plotly>=5.18.0", "kaleido>=0.2.1", "numpy>=1.26.0", "pandas>=2.2.0"]
78-
lib-bokeh = ["bokeh>=3.4.0", "numpy>=1.26.0", "pandas>=2.2.0"]
78+
lib-bokeh = ["bokeh>=3.4.0", "numpy>=1.26.0", "pandas>=2.2.0", "selenium>=4.15.0", "webdriver-manager>=4.0.0"]
7979
lib-altair = ["altair>=5.2.0", "vl-convert-python>=1.3.0", "numpy>=1.26.0", "pandas>=2.2.0"]
8080
lib-plotnine = ["plotnine>=0.13.0", "numpy>=1.26.0", "pandas>=2.2.0"]
8181
lib-pygal = ["pygal>=3.0.0", "cairosvg>=2.7.0", "pandas>=2.2.0"]

0 commit comments

Comments
 (0)