Skip to content

Commit d065258

Browse files
MarkusNeusingerclaude[bot]github-actions[bot]
authored
feat: add area-basic implementation (9 libraries) (#508)
## Summary Adds `area-basic` plot implementation. ### Libraries - **Merged:** 9 (all libraries) - **Not Feasible:** 0 ### Links - **Spec:** `specs/area-basic.md` - **Parent Issue:** #201 --- :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 4954fa5 commit d065258

File tree

12 files changed

+2045
-2350
lines changed

12 files changed

+2045
-2350
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""
2+
area-basic: Basic Area Chart
3+
Library: altair
4+
"""
5+
6+
import altair as alt
7+
import pandas as pd
8+
9+
10+
# Data
11+
data = pd.DataFrame(
12+
{
13+
"month": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
14+
"sales": [100, 150, 130, 180, 200, 220, 195, 240, 260, 230, 280, 310],
15+
}
16+
)
17+
18+
# Create chart with area and line
19+
chart = (
20+
alt.Chart(data)
21+
.mark_area(opacity=0.5, color="#306998", line={"color": "#306998", "strokeWidth": 2})
22+
.encode(
23+
x=alt.X("month:Q", title="Month", axis=alt.Axis(labelFontSize=16, titleFontSize=20)),
24+
y=alt.Y("sales:Q", title="Sales", axis=alt.Axis(labelFontSize=16, titleFontSize=20)),
25+
)
26+
.properties(width=1600, height=900, title=alt.TitleParams(text="Basic Area Chart", fontSize=20))
27+
.configure_view(strokeWidth=0)
28+
.configure_axis(grid=True, gridOpacity=0.3)
29+
)
30+
31+
# Save (1600 × 900 × 3 = 4800 × 2700)
32+
chart.save("plot.png", scale_factor=3.0)
Lines changed: 46 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -1,164 +1,55 @@
11
"""
22
area-basic: Basic Area Chart
3-
Implementation for: bokeh
4-
Variant: default
5-
Python: 3.10+
3+
Library: bokeh
64
"""
75

8-
from typing import TYPE_CHECKING, Optional
9-
106
import pandas as pd
7+
from bokeh.io import export_png
118
from bokeh.models import ColumnDataSource
129
from bokeh.plotting import figure
1310

1411

15-
if TYPE_CHECKING:
16-
from bokeh.plotting import Figure
17-
18-
19-
def create_plot(
20-
data: pd.DataFrame,
21-
x: str,
22-
y: str,
23-
fill_alpha: float = 0.5,
24-
line_color: Optional[str] = None,
25-
title: Optional[str] = None,
26-
x_label: Optional[str] = None,
27-
y_label: Optional[str] = None,
28-
width: int = 1600,
29-
height: int = 900,
30-
**kwargs,
31-
) -> "Figure":
32-
"""
33-
Create a basic filled area chart using bokeh.
34-
35-
A simple filled area chart showing a single data series over time or
36-
sequential x-values. The area between the data line and the baseline
37-
(zero) is filled with a semi-transparent color.
38-
39-
Args:
40-
data: Input DataFrame with x and y columns
41-
x: Column name for x-axis values
42-
y: Column name for y-axis values
43-
fill_alpha: Transparency of the filled area (default: 0.5)
44-
line_color: Color of the line and fill (default: bokeh blue)
45-
title: Chart title (optional)
46-
x_label: Label for x-axis (optional, defaults to column name)
47-
y_label: Label for y-axis (optional, defaults to column name)
48-
width: Figure width in pixels (default: 1600)
49-
height: Figure height in pixels (default: 900)
50-
**kwargs: Additional parameters passed to figure
51-
52-
Returns:
53-
Bokeh Figure object
54-
55-
Raises:
56-
ValueError: If data is empty or fill_alpha is out of range
57-
KeyError: If required columns not found
58-
59-
Example:
60-
>>> data = pd.DataFrame({
61-
... 'Month': [1, 2, 3, 4, 5, 6],
62-
... 'Sales': [100, 120, 90, 140, 160, 130]
63-
... })
64-
>>> fig = create_plot(data, x='Month', y='Sales', title='Monthly Sales')
65-
"""
66-
# Input validation
67-
if data.empty:
68-
raise ValueError("Data cannot be empty")
69-
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-
if not 0 <= fill_alpha <= 1:
76-
raise ValueError(f"fill_alpha must be between 0 and 1, got {fill_alpha}")
77-
78-
# Set default color (bokeh blue)
79-
color = line_color or "#1f77b4"
80-
81-
# Sort data by x to ensure proper area rendering
82-
plot_data = data[[x, y]].dropna().sort_values(by=x).reset_index(drop=True)
83-
84-
# Create ColumnDataSource
85-
source = ColumnDataSource(data={"x": plot_data[x], "y": plot_data[y], "y0": [0] * len(plot_data)})
86-
87-
# Create figure
88-
p = figure(
89-
width=width,
90-
height=height,
91-
title=title or "Area Chart",
92-
x_axis_label=x_label or x,
93-
y_axis_label=y_label or y,
94-
toolbar_location="above",
95-
tools="pan,wheel_zoom,box_zoom,reset,save",
96-
**kwargs,
97-
)
98-
99-
# Draw the filled area from baseline (0) to y values
100-
p.varea(x="x", y1="y0", y2="y", source=source, fill_color=color, fill_alpha=fill_alpha)
101-
102-
# Draw line on top for better visibility
103-
p.line(x="x", y="y", source=source, line_color=color, line_width=2)
104-
105-
# Styling
106-
p.title.text_font_size = "14pt"
107-
p.title.align = "center"
108-
109-
# Grid styling - subtle
110-
p.xgrid.grid_line_alpha = 0.3
111-
p.ygrid.grid_line_alpha = 0.3
112-
p.xgrid.grid_line_dash = [6, 4]
113-
p.ygrid.grid_line_dash = [6, 4]
114-
115-
# Axis styling
116-
p.xaxis.axis_label_text_font_size = "12pt"
117-
p.yaxis.axis_label_text_font_size = "12pt"
118-
p.xaxis.major_label_text_font_size = "10pt"
119-
p.yaxis.major_label_text_font_size = "10pt"
120-
121-
return p
122-
123-
124-
if __name__ == "__main__":
125-
import numpy as np
126-
127-
# Sample data: Monthly website traffic over a year
128-
np.random.seed(42)
129-
months = list(range(1, 13))
130-
base_traffic = [1000, 1100, 1050, 1200, 1400, 1600, 1500, 1550, 1700, 1650, 1800, 2000]
131-
noise = np.random.normal(0, 50, 12)
132-
traffic = [max(0, int(b + n)) for b, n in zip(base_traffic, noise, strict=False)]
133-
134-
data = pd.DataFrame({"Month": months, "Visitors": traffic})
135-
136-
# Create plot
137-
fig = create_plot(
138-
data,
139-
x="Month",
140-
y="Visitors",
141-
title="Monthly Website Traffic",
142-
x_label="Month",
143-
y_label="Visitors (thousands)",
144-
fill_alpha=0.5,
145-
)
146-
147-
# Save as PNG using webdriver-manager for automatic chromedriver
148-
from bokeh.io import export_png
149-
from selenium import webdriver
150-
from selenium.webdriver.chrome.options import Options
151-
from selenium.webdriver.chrome.service import Service
152-
from webdriver_manager.chrome import ChromeDriverManager
153-
154-
chrome_options = Options()
155-
chrome_options.add_argument("--headless")
156-
chrome_options.add_argument("--no-sandbox")
157-
chrome_options.add_argument("--disable-dev-shm-usage")
158-
159-
service = Service(ChromeDriverManager().install())
160-
driver = webdriver.Chrome(service=service, options=chrome_options)
161-
162-
export_png(fig, filename="plot.png", webdriver=driver)
163-
driver.quit()
164-
print("Plot saved to plot.png")
12+
# Data
13+
data = pd.DataFrame(
14+
{
15+
"month": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
16+
"sales": [120, 135, 148, 162, 175, 195, 210, 198, 185, 170, 158, 190],
17+
}
18+
)
19+
20+
# Create x positions for categorical months
21+
x = list(range(len(data["month"])))
22+
y = data["sales"].tolist()
23+
24+
source = ColumnDataSource(data={"x": x, "y": y, "month": data["month"]})
25+
26+
# Create figure
27+
p = figure(
28+
width=4800,
29+
height=2700,
30+
title="Monthly Sales Volume",
31+
x_axis_label="Month",
32+
y_axis_label="Sales ($)",
33+
x_range=(-0.5, len(x) - 0.5),
34+
)
35+
36+
# Plot area (varea fills from y1 to y2)
37+
p.varea(x="x", y1=0, y2="y", source=source, fill_alpha=0.7, fill_color="#306998")
38+
39+
# Add line on top for clearer boundary
40+
p.line(x="x", y="y", source=source, line_width=2, line_color="#306998")
41+
42+
# Configure x-axis ticks to show month labels
43+
p.xaxis.ticker = x
44+
p.xaxis.major_label_overrides = dict(enumerate(data["month"]))
45+
46+
# Styling
47+
p.title.text_font_size = "20pt"
48+
p.xaxis.axis_label_text_font_size = "20pt"
49+
p.yaxis.axis_label_text_font_size = "20pt"
50+
p.xaxis.major_label_text_font_size = "16pt"
51+
p.yaxis.major_label_text_font_size = "16pt"
52+
p.grid.grid_line_alpha = 0.3
53+
54+
# Save
55+
export_png(p, filename="plot.png")
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
"""
2+
area-basic: Basic Area Chart
3+
Library: highcharts
4+
"""
5+
6+
import tempfile
7+
import time
8+
import urllib.request
9+
from pathlib import Path
10+
11+
from highcharts_core.chart import Chart
12+
from highcharts_core.options import HighchartsOptions
13+
from highcharts_core.options.series.area import AreaSeries
14+
from selenium import webdriver
15+
from selenium.webdriver.chrome.options import Options
16+
17+
18+
# Data - Monthly sales example
19+
sales = [100, 150, 130, 180, 200, 220, 195, 240, 280, 310, 290, 350]
20+
21+
# Create chart
22+
chart = Chart(container="container")
23+
chart.options = HighchartsOptions()
24+
25+
# Chart configuration
26+
chart.options.chart = {
27+
"type": "area",
28+
"width": 4800,
29+
"height": 2700,
30+
"backgroundColor": "#ffffff",
31+
"style": {"fontFamily": "Arial, sans-serif"},
32+
}
33+
34+
# Title
35+
chart.options.title = {"text": "Monthly Sales Trend", "style": {"fontSize": "48px"}}
36+
37+
# Axes
38+
chart.options.x_axis = {
39+
"title": {"text": "Month", "style": {"fontSize": "40px"}},
40+
"categories": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
41+
"labels": {"style": {"fontSize": "32px"}, "enabled": True},
42+
"gridLineWidth": 1,
43+
"gridLineColor": "rgba(0, 0, 0, 0.1)",
44+
"tickmarkPlacement": "on",
45+
}
46+
47+
chart.options.y_axis = {
48+
"title": {"text": "Sales ($)", "style": {"fontSize": "40px"}},
49+
"labels": {"style": {"fontSize": "32px"}},
50+
"gridLineWidth": 1,
51+
"gridLineColor": "rgba(0, 0, 0, 0.1)",
52+
}
53+
54+
# Legend
55+
chart.options.legend = {"enabled": False}
56+
57+
# Create area series
58+
series = AreaSeries()
59+
series.data = sales
60+
series.name = "Sales"
61+
series.color = "#306998"
62+
series.fill_opacity = 0.5
63+
series.line_width = 4
64+
series.marker = {"enabled": True, "radius": 6, "fillColor": "#306998", "lineWidth": 2, "lineColor": "#ffffff"}
65+
66+
chart.add_series(series)
67+
68+
# Download Highcharts JS
69+
highcharts_url = "https://code.highcharts.com/highcharts.js"
70+
with urllib.request.urlopen(highcharts_url, timeout=30) as response:
71+
highcharts_js = response.read().decode("utf-8")
72+
73+
# Generate HTML with inline scripts
74+
html_str = chart.to_js_literal()
75+
html_content = f"""<!DOCTYPE html>
76+
<html>
77+
<head>
78+
<meta charset="utf-8">
79+
<script>{highcharts_js}</script>
80+
</head>
81+
<body style="margin:0;">
82+
<div id="container" style="width: 4800px; height: 2700px;"></div>
83+
<script>{html_str}</script>
84+
</body>
85+
</html>"""
86+
87+
# Write temp HTML and take screenshot
88+
with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f:
89+
f.write(html_content)
90+
temp_path = f.name
91+
92+
chrome_options = Options()
93+
chrome_options.add_argument("--headless")
94+
chrome_options.add_argument("--no-sandbox")
95+
chrome_options.add_argument("--disable-dev-shm-usage")
96+
chrome_options.add_argument("--disable-gpu")
97+
chrome_options.add_argument("--window-size=4800,2800")
98+
99+
driver = webdriver.Chrome(options=chrome_options)
100+
driver.set_window_size(4800, 2800)
101+
driver.get(f"file://{temp_path}")
102+
time.sleep(5)
103+
104+
# Get the container element and screenshot it directly
105+
container = driver.find_element("id", "container")
106+
container.screenshot("plot.png")
107+
driver.quit()
108+
109+
Path(temp_path).unlink()
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""
2+
area-basic: Basic Area Chart
3+
Library: letsplot
4+
"""
5+
6+
import pandas as pd
7+
from lets_plot import (
8+
LetsPlot,
9+
aes,
10+
element_text,
11+
geom_area,
12+
geom_line,
13+
ggplot,
14+
ggsave,
15+
ggsize,
16+
labs,
17+
scale_x_continuous,
18+
theme,
19+
theme_minimal,
20+
)
21+
22+
23+
LetsPlot.setup_html()
24+
25+
# Data
26+
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
27+
data = pd.DataFrame(
28+
{"month_num": range(len(months)), "sales": [120, 135, 148, 162, 175, 195, 210, 198, 185, 170, 158, 190]}
29+
)
30+
31+
# Plot
32+
plot = (
33+
ggplot(data, aes(x="month_num", y="sales"))
34+
+ geom_area(fill="#306998", alpha=0.5)
35+
+ geom_line(color="#306998", size=2)
36+
+ scale_x_continuous(breaks=list(range(len(months))), labels=months)
37+
+ labs(x="Month", y="Sales", title="Basic Area Chart")
38+
+ theme_minimal()
39+
+ theme(axis_title=element_text(size=20), axis_text=element_text(size=16), plot_title=element_text(size=20))
40+
+ ggsize(1600, 900)
41+
)
42+
43+
# Save - scale 3x to get 4800 × 2700 px
44+
ggsave(plot, "plot.png", path=".", scale=3)

0 commit comments

Comments
 (0)