Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 33 additions & 137 deletions plots/bokeh/line/line-basic/default.py
Original file line number Diff line number Diff line change
@@ -1,154 +1,50 @@
"""
line-basic: Basic Line Plot
Implementation for: bokeh
Variant: default
Python: 3.10+
Library: bokeh
"""

from typing import TYPE_CHECKING, Optional

import numpy as np
import pandas as pd
from bokeh.io import export_png
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service


if TYPE_CHECKING:
from bokeh.plotting import Figure


def create_plot(
data: pd.DataFrame,
x: str,
y: str,
title: Optional[str] = None,
xlabel: Optional[str] = None,
ylabel: Optional[str] = None,
color: str = "steelblue",
line_width: float = 2.0,
marker: Optional[str] = None,
marker_size: float = 8,
alpha: float = 1.0,
width: int = 1600,
height: int = 900,
**kwargs,
) -> "Figure":
"""
Create a basic line plot visualizing trends over continuous or sequential data.

Args:
data: Input DataFrame with required columns
x: Column name for x-axis values (numeric or datetime)
y: Column name for y-axis values (numeric)
title: Plot title (optional)
xlabel: Custom x-axis label (optional, defaults to column name)
ylabel: Custom y-axis label (optional, defaults to column name)
color: Line color (default: "steelblue")
line_width: Width of the line (default: 2.0)
marker: Marker style for data points (optional, e.g., "circle", "square")
marker_size: Size of markers if enabled (default: 8)
alpha: Line transparency (default: 1.0)
width: Figure width in pixels (default: 1600)
height: Figure height in pixels (default: 900)
**kwargs: Additional parameters

Returns:
Bokeh Figure object

Raises:
ValueError: If data is empty
KeyError: If required columns not found
TypeError: If y column contains non-numeric data

Example:
>>> data = pd.DataFrame({'x': [1, 2, 3, 4, 5], 'y': [2, 4, 3, 5, 6]})
>>> fig = create_plot(data, 'x', 'y')
"""
# Input validation
if data.empty:
raise ValueError("Data cannot be empty")

# Check required columns
for col in [x, y]:
if col not in data.columns:
available = ", ".join(data.columns)
raise KeyError(f"Column '{col}' not found. Available columns: {available}")

# Check if y column is numeric
if not pd.api.types.is_numeric_dtype(data[y]):
raise TypeError(f"Column '{y}' must contain numeric data")

# Sort data by x to ensure proper line connection
plot_data = data[[x, y]].dropna().sort_values(by=x)

# Determine x-axis type
x_axis_type = "datetime" if pd.api.types.is_datetime64_any_dtype(plot_data[x]) else "auto"

# Create ColumnDataSource
source = ColumnDataSource(data={"x": plot_data[x], "y": plot_data[y]})

# Create figure
p = figure(
width=width,
height=height,
title=title or "Line Plot",
x_axis_type=x_axis_type,
toolbar_location="above",
tools="pan,wheel_zoom,box_zoom,reset,save",
)

# Plot line
p.line(x="x", y="y", source=source, line_color=color, line_width=line_width, line_alpha=alpha)

# Add markers if specified
if marker:
p.scatter(x="x", y="y", source=source, size=marker_size, color=color, alpha=alpha, marker=marker)

# Labels
p.xaxis.axis_label = xlabel or x
p.yaxis.axis_label = ylabel or y

# Styling
p.title.text_font_size = "14pt"
p.title.align = "center"
p.xaxis.axis_label_text_font_size = "12pt"
p.yaxis.axis_label_text_font_size = "12pt"
p.xgrid.grid_line_alpha = 0.3
p.ygrid.grid_line_alpha = 0.3
p.xgrid.grid_line_dash = [6, 4]
p.ygrid.grid_line_dash = [6, 4]
# Data
data = pd.DataFrame({"time": [1, 2, 3, 4, 5, 6, 7], "value": [10, 15, 13, 18, 22, 19, 25]})

return p
source = ColumnDataSource(data={"x": data["time"], "y": data["value"]})

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

if __name__ == "__main__":
from bokeh.io import export_png
# Plot line
p.line(x="x", y="y", source=source, line_width=2, line_color="#306998")

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

# Create sequential x values
x_values = np.arange(n_points)
# Create y values with a trend and some noise
y_values = 10 + 0.5 * x_values + np.random.randn(n_points) * 2
# Styling
p.title.text_font_size = "20pt"
p.xaxis.axis_label_text_font_size = "20pt"
p.yaxis.axis_label_text_font_size = "20pt"
p.xaxis.major_label_text_font_size = "16pt"
p.yaxis.major_label_text_font_size = "16pt"
p.grid.grid_line_alpha = 0.3

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

# Create plot
fig = create_plot(
data,
x="Time",
y="Value",
title="Basic Line Plot Example",
xlabel="Time (units)",
ylabel="Measurement Value",
color="steelblue",
line_width=2.5,
marker="circle",
marker_size=6,
)
# Use system chromedriver (pre-installed on GitHub Actions runners)
service = Service()
driver = webdriver.Chrome(service=service, options=chrome_options)

# Save as PNG
export_png(fig, filename="plot.png")
print("Plot saved to plot.png")
# Save
export_png(p, filename="plot.png", webdriver=driver)
driver.quit()
Loading
Loading