Skip to content
Closed
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
64 changes: 54 additions & 10 deletions plots/scatter-basic/implementations/altair.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,73 @@
""" pyplots.ai
scatter-basic: Basic Scatter Plot
Library: altair 6.0.0 | Python 3.13.11
Quality: 91/100 | Created: 2025-12-22
Library: altair 6.0.0 | Python 3.14.2
Quality: /100 | Updated: 2026-02-10
"""

import altair as alt
import numpy as np
import pandas as pd


# Data
# Data - Daily temperature vs ice cream sales for a beach town
np.random.seed(42)
x = np.random.randn(100) * 2 + 10
y = x * 0.8 + np.random.randn(100) * 2
n = 120
temperature = np.random.normal(24, 7, n).clip(5, 42)
sales = temperature * 12 + np.random.normal(0, 35, n) + 50
sales = sales.clip(20, None)

df = pd.DataFrame({"x": x, "y": y})
df = pd.DataFrame(
{"Temperature (Celsius)": np.round(temperature, 1), "Ice Cream Sales (USD)": np.round(sales, 0).astype(int)}
)

# Plot
chart = (
alt.Chart(df)
.mark_point(filled=True, size=200, opacity=0.7, color="#306998")
.encode(x=alt.X("x:Q", title="X Value"), y=alt.Y("y:Q", title="Y Value"), tooltip=["x:Q", "y:Q"])
.properties(width=1600, height=900, title=alt.Title("scatter-basic · altair · pyplots.ai", fontSize=28))
.configure_axis(labelFontSize=18, titleFontSize=22, grid=True, gridOpacity=0.3)
.mark_circle(size=220, opacity=0.72, stroke="#1a3a5c", strokeWidth=0.8)
.encode(
x=alt.X(
"Temperature (Celsius):Q",
scale=alt.Scale(domain=[0, 45], nice=True),
axis=alt.Axis(labelFontSize=18, titleFontSize=22, tickCount=10, gridOpacity=0.25, gridDash=[4, 4]),
),
y=alt.Y(
"Ice Cream Sales (USD):Q",
scale=alt.Scale(domain=[0, 600], nice=True),
axis=alt.Axis(labelFontSize=18, titleFontSize=22, gridOpacity=0.25, gridDash=[4, 4]),
),
color=alt.Color(
"Temperature (Celsius):Q",
scale=alt.Scale(scheme="turbo", domain=[5, 42]),
legend=alt.Legend(
title="Temp (C)",
titleFontSize=16,
labelFontSize=14,
gradientLength=260,
gradientThickness=18,
orient="right",
),
),
tooltip=[
alt.Tooltip("Temperature (Celsius):Q", format=".1f"),
alt.Tooltip("Ice Cream Sales (USD):Q", format=",.0f"),
],
)
.properties(
width=1600,
height=900,
title=alt.Title(
"scatter-basic \u00b7 altair \u00b7 pyplots.ai",
fontSize=28,
fontWeight="bold",
anchor="start",
offset=16,
subtitle="Daily temperature vs ice cream sales in a coastal town",
subtitleFontSize=18,
subtitleColor="#555555",
),
)
.configure_view(strokeWidth=0)
.configure_axis(domainColor="#888888", tickColor="#888888", labelColor="#333333", titleColor="#222222")
)

# Save
Expand Down
82 changes: 59 additions & 23 deletions plots/scatter-basic/implementations/bokeh.py
Original file line number Diff line number Diff line change
@@ -1,52 +1,88 @@
""" pyplots.ai
scatter-basic: Basic Scatter Plot
Library: bokeh 3.8.1 | Python 3.13.11
Quality: 85/100 | Created: 2025-12-22
Library: bokeh 3.8.2 | Python 3.14.2
Quality: /100 | Updated: 2026-02-10
"""

import numpy as np
from bokeh.io import export_png
from bokeh.models import ColumnDataSource, HoverTool
from bokeh.plotting import figure, output_file, save
from bokeh.transform import factor_cmap


# Data - Study hours vs exam scores (realistic scenario)
# Data - Coffee shop daily metrics: cups sold vs revenue across seasons
np.random.seed(42)
study_hours = np.random.uniform(1, 10, 100)
exam_scores = study_hours * 8 + np.random.randn(100) * 5 + 20
exam_scores = np.clip(exam_scores, 0, 100)
n = 120
seasons = np.random.choice(["Winter", "Spring", "Summer", "Autumn"], size=n, p=[0.28, 0.24, 0.24, 0.24])
base_cups = {"Winter": 180, "Spring": 140, "Summer": 110, "Autumn": 150}
cups_sold = np.array([base_cups[s] + np.random.normal(0, 30) for s in seasons]).clip(40, 300)
revenue = cups_sold * np.random.uniform(3.2, 4.8, n) + np.random.normal(0, 40, n)
revenue = revenue.clip(100, 1600)

# Create ColumnDataSource
source = ColumnDataSource(data={"study_hours": study_hours, "exam_scores": exam_scores})
source = ColumnDataSource(data={"cups": cups_sold, "revenue": revenue, "season": seasons})

# Create figure (4800 x 2700 px for 16:9 aspect ratio)
p = figure(width=4800, height=2700, title="scatter-basic · bokeh · pyplots.ai")
season_list = ["Winter", "Spring", "Summer", "Autumn"]
palette = ["#306998", "#2CA02C", "#FFD43B", "#E25822"]

# Set axis labels explicitly (more reliable than figure parameters)
p.xaxis.axis_label = "Study Hours (hrs)"
p.yaxis.axis_label = "Exam Score (%)"
# Create figure
p = figure(width=4800, height=2700, title="scatter-basic \u00b7 bokeh \u00b7 pyplots.ai")
p.xaxis.axis_label = "Cups Sold per Day"
p.yaxis.axis_label = "Daily Revenue ($)"

# Plot scatter points (size increased for visibility on large canvas)
p.scatter(x="study_hours", y="exam_scores", source=source, size=50, color="#306998", alpha=0.7)
# Plot scatter with season-based coloring
p.scatter(
x="cups",
y="revenue",
source=source,
size=30,
alpha=0.75,
color=factor_cmap("season", palette, season_list),
legend_group="season",
)

# Add HoverTool for interactivity (key Bokeh distinctive feature)
hover = HoverTool(tooltips=[("Study Hours", "@study_hours{0.1} hrs"), ("Exam Score", "@exam_scores{0.1}%")])
# HoverTool for interactivity
hover = HoverTool(tooltips=[("Season", "@season"), ("Cups Sold", "@cups{0}"), ("Revenue", "$@revenue{0.00}")])
p.add_tools(hover)

# Styling (scaled for 4800x2700 px canvas - larger sizes for readability)
# Title styling
p.title.text_font_size = "72pt"
p.title.text_color = "#2C3E50"

# Axis styling
p.xaxis.axis_label_text_font_size = "48pt"
p.yaxis.axis_label_text_font_size = "48pt"
p.xaxis.major_label_text_font_size = "36pt"
p.yaxis.major_label_text_font_size = "36pt"
p.xaxis.axis_label_text_color = "#34495E"
p.yaxis.axis_label_text_color = "#34495E"
p.xaxis.axis_line_width = 3
p.yaxis.axis_line_width = 3
p.xaxis.major_tick_line_width = 3
p.yaxis.major_tick_line_width = 3

# Grid styling (subtle, per quality criteria VQ-07: alpha 0.2-0.4)
p.grid.grid_line_alpha = 0.35
# Grid
p.grid.grid_line_alpha = 0.3
p.grid.grid_line_width = 2
p.grid.grid_line_dash = [6, 4]

# Save as PNG
export_png(p, filename="plot.png")
# Legend
p.legend.label_text_font_size = "36pt"
p.legend.glyph_width = 50
p.legend.glyph_height = 50
p.legend.spacing = 14
p.legend.padding = 20
p.legend.background_fill_alpha = 0.85
p.legend.border_line_alpha = 0.3
p.legend.location = "top_left"

# Save as HTML (interactive)
# Background
p.background_fill_color = "#FAFBFC"
p.border_fill_color = "white"
p.outline_line_color = "#E0E0E0"
p.outline_line_width = 2

# Save
export_png(p, filename="plot.png")
output_file("plot.html")
save(p)
129 changes: 103 additions & 26 deletions plots/scatter-basic/implementations/highcharts.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
""" pyplots.ai
scatter-basic: Basic Scatter Plot
Library: highcharts unknown | Python 3.13.11
Quality: 92/100 | Created: 2025-12-22
Library: highcharts 1.10.3 | Python 3.14.2
Quality: /100 | Updated: 2026-02-10
"""

import tempfile
Expand All @@ -17,10 +17,11 @@
from selenium.webdriver.chrome.options import Options


# Data
# Data - Daily temperature vs iced coffee sales at a cafe
np.random.seed(42)
x = np.random.randn(100) * 2 + 10
y = x * 0.8 + np.random.randn(100) * 2
temperature = np.random.normal(22, 8, 120).clip(2, 40) # Daily temp in Celsius
iced_sales = temperature * 2.8 + np.random.normal(0, 8, 120) # Iced drinks sold
iced_sales = iced_sales.clip(5, None)

# Create chart
chart = Chart(container="container")
Expand All @@ -31,44 +32,120 @@
"type": "scatter",
"width": 4800,
"height": 2700,
"backgroundColor": "#ffffff",
"marginBottom": 150,
"backgroundColor": "#FAFBFC",
"marginBottom": 220,
"marginLeft": 200,
"marginTop": 200,
"style": {"fontFamily": "'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif"},
}

# Title (required format: spec-id · library · pyplots.ai)
# Title
chart.options.title = {
"text": "scatter-basic · highcharts · pyplots.ai",
"style": {"fontSize": "72px", "fontWeight": "bold"},
"text": "scatter-basic \u00b7 highcharts \u00b7 pyplots.ai",
"style": {"fontSize": "64px", "fontWeight": "600", "color": "#2D3748", "letterSpacing": "1px"},
"margin": 60,
}

# Axes (scaled for 4800x2700 px)
# Subtitle for context
chart.options.subtitle = {
"text": "Daily Temperature vs Iced Coffee Sales \u2014 120 days observed",
"style": {"fontSize": "40px", "color": "#718096", "fontWeight": "400"},
}

# X-axis
chart.options.x_axis = {
"title": {"text": "X Value", "style": {"fontSize": "48px"}},
"labels": {"style": {"fontSize": "36px"}},
"title": {
"text": "Daily Temperature (\u00b0C)",
"style": {"fontSize": "44px", "color": "#4A5568", "fontWeight": "500"},
"margin": 30,
},
"labels": {"style": {"fontSize": "34px", "color": "#718096"}},
"gridLineWidth": 1,
"gridLineColor": "rgba(0, 0, 0, 0.15)",
"gridLineDashStyle": "Dash",
"gridLineColor": "rgba(203, 213, 224, 0.5)",
"gridLineDashStyle": "Dot",
"lineColor": "#CBD5E0",
"lineWidth": 2,
"tickColor": "#CBD5E0",
"tickWidth": 2,
"tickLength": 10,
}

# Y-axis
chart.options.y_axis = {
"title": {"text": "Y Value", "style": {"fontSize": "48px"}},
"labels": {"style": {"fontSize": "36px"}},
"title": {
"text": "Iced Drinks Sold",
"style": {"fontSize": "44px", "color": "#4A5568", "fontWeight": "500"},
"margin": 30,
},
"labels": {"style": {"fontSize": "34px", "color": "#718096"}},
"gridLineWidth": 1,
"gridLineColor": "rgba(0, 0, 0, 0.15)",
"gridLineDashStyle": "Dash",
"gridLineColor": "rgba(203, 213, 224, 0.5)",
"gridLineDashStyle": "Dot",
"lineColor": "#CBD5E0",
"lineWidth": 2,
"tickColor": "#CBD5E0",
"tickWidth": 2,
"tickLength": 10,
}

# Legend and credits
chart.options.legend = {"enabled": False}
chart.options.credits = {"enabled": False}

# Create scatter series with Python Blue color and transparency
series = ScatterSeries()
series.data = [[float(xi), float(yi)] for xi, yi in zip(x, y, strict=True)]
series.name = "Data"
series.color = "rgba(48, 105, 152, 0.7)" # Python Blue with alpha
series.marker = {"radius": 18, "symbol": "circle"} # Larger markers for 4800x2700
# Tooltip styling for interactive HTML version
chart.options.tooltip = {
"headerFormat": "",
"pointFormat": "<b>{point.x:.1f}\u00b0C</b> \u2192 <b>{point.y:.0f}</b> drinks",
"style": {"fontSize": "28px"},
"backgroundColor": "rgba(255, 255, 255, 0.95)",
"borderColor": "#306998",
"borderRadius": 8,
"shadow": {"color": "rgba(0,0,0,0.1)", "offsetX": 2, "offsetY": 2, "width": 4},
}

chart.add_series(series)
# Split data into warm and cool groups for visual interest
cool_mask = temperature < 18
warm_mask = ~cool_mask

# Cool days series (blue tones)
series_cool = ScatterSeries()
series_cool.data = [
[float(xi), float(yi)] for xi, yi in zip(temperature[cool_mask], iced_sales[cool_mask], strict=True)
]
series_cool.name = "Cool Days (< 18\u00b0C)"
series_cool.color = "rgba(48, 105, 152, 0.75)"
series_cool.marker = {"radius": 16, "symbol": "circle", "lineWidth": 2, "lineColor": "rgba(48, 105, 152, 0.9)"}

# Warm days series (amber/gold tones)
series_warm = ScatterSeries()
series_warm.data = [
[float(xi), float(yi)] for xi, yi in zip(temperature[warm_mask], iced_sales[warm_mask], strict=True)
]
series_warm.name = "Warm Days (\u2265 18\u00b0C)"
series_warm.color = "rgba(255, 179, 25, 0.75)"
series_warm.marker = {"radius": 16, "symbol": "circle", "lineWidth": 2, "lineColor": "rgba(214, 138, 0, 0.9)"}

chart.add_series(series_cool)
chart.add_series(series_warm)

# Enable legend for two series - positioned inside top-left
chart.options.legend = {
"enabled": True,
"layout": "vertical",
"align": "left",
"verticalAlign": "top",
"x": 180,
"y": 120,
"floating": True,
"backgroundColor": "rgba(255, 255, 255, 0.85)",
"borderColor": "#E2E8F0",
"borderWidth": 2,
"borderRadius": 8,
"padding": 20,
"itemStyle": {"fontSize": "34px", "fontWeight": "500", "color": "#4A5568"},
"symbolRadius": 8,
"itemMarginBottom": 10,
}

# Download Highcharts JS (required for headless Chrome)
highcharts_url = "https://code.highcharts.com/highcharts.js"
Expand Down
Loading
Loading