From 55be685f4ab0f9b8fc164694d99ad92dbbbbd0a0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 31 Dec 2025 13:53:33 +0000 Subject: [PATCH 1/7] feat(bokeh): implement scatter-animated-controls --- .../implementations/bokeh.py | 286 ++++++++++++++++++ 1 file changed, 286 insertions(+) create mode 100644 plots/scatter-animated-controls/implementations/bokeh.py diff --git a/plots/scatter-animated-controls/implementations/bokeh.py b/plots/scatter-animated-controls/implementations/bokeh.py new file mode 100644 index 0000000000..b6ccd5a720 --- /dev/null +++ b/plots/scatter-animated-controls/implementations/bokeh.py @@ -0,0 +1,286 @@ +"""pyplots.ai +scatter-animated-controls: Animated Scatter Plot with Play Controls +Library: bokeh | Python 3.13 +Quality: pending | Created: 2025-12-31 +""" + +import numpy as np +import pandas as pd +from bokeh.io import export_png, save +from bokeh.layouts import column, row +from bokeh.models import Button, ColumnDataSource, CustomJS, Div, HoverTool, Label, Slider +from bokeh.plotting import figure +from bokeh.resources import CDN + + +# Data: Simulated country metrics over 20 years (Gapminder-style) +np.random.seed(42) + +n_countries = 15 +years = np.arange(2004, 2024) +n_years = len(years) + +countries = [ + "Country A", + "Country B", + "Country C", + "Country D", + "Country E", + "Country F", + "Country G", + "Country H", + "Country I", + "Country J", + "Country K", + "Country L", + "Country M", + "Country N", + "Country O", +] + +regions = ["North", "South", "East", "West", "Central"] +country_regions = [regions[i % 5] for i in range(n_countries)] + +# Region colors (colorblind-safe) +region_colors = { + "North": "#306998", # Python Blue + "South": "#FFD43B", # Python Yellow + "East": "#E15759", # Red + "West": "#76B7B2", # Teal + "Central": "#59A14F", # Green +} + +# Generate time-series data for each country +data_frames = [] +for i, country in enumerate(countries): + base_gdp = np.random.uniform(5000, 40000) + base_life = np.random.uniform(55, 75) + base_pop = np.random.uniform(5, 200) # millions + + gdp_growth = np.random.uniform(0.02, 0.06) + life_improvement = np.random.uniform(0.2, 0.5) + pop_growth = np.random.uniform(0.005, 0.02) + + # Add some noise and variation + gdp_noise = np.cumsum(np.random.randn(n_years) * 500) + life_noise = np.cumsum(np.random.randn(n_years) * 0.3) + pop_noise = np.cumsum(np.random.randn(n_years) * 0.5) + + gdp = base_gdp * (1 + gdp_growth) ** np.arange(n_years) + gdp_noise + life_exp = base_life + life_improvement * np.arange(n_years) + life_noise + population = base_pop * (1 + pop_growth) ** np.arange(n_years) + pop_noise + + # Ensure positive values + gdp = np.maximum(gdp, 1000) + life_exp = np.clip(life_exp, 40, 90) + population = np.maximum(population, 1) + + for j, year in enumerate(years): + data_frames.append( + { + "country": country, + "region": country_regions[i], + "year": year, + "gdp_per_capita": gdp[j], + "life_expectancy": life_exp[j], + "population": population[j], + "color": region_colors[country_regions[i]], + } + ) + +df = pd.DataFrame(data_frames) + +# Initial data (first year) +initial_year = years[0] +initial_data = df[df["year"] == initial_year].copy() + +# Create ColumnDataSource +source = ColumnDataSource( + data={ + "x": initial_data["gdp_per_capita"].values, + "y": initial_data["life_expectancy"].values, + "size": (initial_data["population"].values ** 0.5) * 5, # Scale for visibility + "country": initial_data["country"].values, + "region": initial_data["region"].values, + "population": initial_data["population"].values, + "color": initial_data["color"].values, + } +) + +# Store all data for animation +all_data = {} +for year in years: + year_data = df[df["year"] == year] + all_data[str(year)] = { + "x": year_data["gdp_per_capita"].tolist(), + "y": year_data["life_expectancy"].tolist(), + "size": [(p**0.5) * 5 for p in year_data["population"].values], + "country": year_data["country"].tolist(), + "region": year_data["region"].tolist(), + "population": year_data["population"].tolist(), + "color": year_data["color"].tolist(), + } + +# Create figure +p = figure( + width=4800, + height=2700, + title="scatter-animated-controls · bokeh · pyplots.ai", + x_axis_label="GDP per Capita (USD)", + y_axis_label="Life Expectancy (Years)", + x_range=(0, 80000), + y_range=(40, 95), + tools="pan,wheel_zoom,box_zoom,reset,save", +) + +# Style the figure +p.title.text_font_size = "42pt" +p.xaxis.axis_label_text_font_size = "32pt" +p.yaxis.axis_label_text_font_size = "32pt" +p.xaxis.major_label_text_font_size = "24pt" +p.yaxis.major_label_text_font_size = "24pt" + +# Grid styling +p.grid.grid_line_alpha = 0.3 +p.grid.grid_line_dash = [6, 4] + +# Background +p.background_fill_color = "#fafafa" + +# Add scatter plot +scatter = p.scatter( + x="x", y="y", size="size", color="color", alpha=0.7, line_color="white", line_width=2, source=source +) + +# Add hover tool +hover = HoverTool( + tooltips=[ + ("Country", "@country"), + ("Region", "@region"), + ("GDP per Capita", "$@x{0,0}"), + ("Life Expectancy", "@y{0.1} years"), + ("Population", "@population{0.1} million"), + ], + renderers=[scatter], +) +p.add_tools(hover) + +# Add year label (large background text) +year_label = Label( + x=70000, + y=50, + text=str(initial_year), + text_font_size="120pt", + text_color="#cccccc", + text_alpha=0.5, + text_align="right", +) +p.add_layout(year_label) + +# Create slider +slider = Slider(start=int(years[0]), end=int(years[-1]), value=int(years[0]), step=1, title="Year", width=600) + +# Create play/pause button +button = Button(label="▶ Play", button_type="success", width=150) + +# Create legend info display +legend_html = """ +
+ Regions:
+ North    + South    + East    + West    + Central +
+""" +legend_div = Div(text=legend_html, width=800) + +# JavaScript callback for slider +slider_callback = CustomJS( + args={"source": source, "all_data": all_data, "year_label": year_label}, + code=""" + const year = cb_obj.value.toString(); + const data = all_data[year]; + + source.data['x'] = data['x']; + source.data['y'] = data['y']; + source.data['size'] = data['size']; + source.data['country'] = data['country']; + source.data['region'] = data['region']; + source.data['population'] = data['population']; + source.data['color'] = data['color']; + source.change.emit(); + + year_label.text = year; +""", +) +slider.js_on_change("value", slider_callback) + +# JavaScript callback for play/pause button +button_callback = CustomJS( + args={"button": button, "slider": slider, "years_start": int(years[0]), "years_end": int(years[-1])}, + code=""" + if (button.label.includes('Play')) { + button.label = '⏸ Pause'; + button.button_type = 'warning'; + + // Start animation + window.animation_interval = setInterval(function() { + if (slider.value >= slider.end) { + slider.value = slider.start; + } else { + slider.value = slider.value + 1; + } + }, 500); + } else { + button.label = '▶ Play'; + button.button_type = 'success'; + + // Stop animation + if (window.animation_interval) { + clearInterval(window.animation_interval); + } + } +""", +) +button.js_on_click(button_callback) + +# Create title div +title_div = Div( + text=""" +
+ Country Development Over Time (2004-2023) +
+
+ Bubble size represents population. Click Play to animate or drag the slider. +
+""", + width=1000, +) + +# Layout +controls = row(button, slider, legend_div) +layout = column(title_div, controls, p) + +# Save HTML (interactive version with controls) +save(layout, filename="plot.html", title="Animated Scatter Plot", resources=CDN) + +# For PNG export, show the middle year frame as a representative snapshot +middle_year = years[len(years) // 2] +middle_data = df[df["year"] == middle_year] + +# Update source for static export +source.data = { + "x": middle_data["gdp_per_capita"].values, + "y": middle_data["life_expectancy"].values, + "size": (middle_data["population"].values ** 0.5) * 5, + "country": middle_data["country"].values, + "region": middle_data["region"].values, + "population": middle_data["population"].values, + "color": middle_data["color"].values, +} +year_label.text = str(middle_year) + +# Export PNG (static snapshot) +export_png(p, filename="plot.png") From 175daf3d1b70ec40d613be4e5068c132e841cf97 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 31 Dec 2025 13:53:51 +0000 Subject: [PATCH 2/7] chore(bokeh): add metadata for scatter-animated-controls --- .../metadata/bokeh.yaml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 plots/scatter-animated-controls/metadata/bokeh.yaml diff --git a/plots/scatter-animated-controls/metadata/bokeh.yaml b/plots/scatter-animated-controls/metadata/bokeh.yaml new file mode 100644 index 0000000000..57dbf7968a --- /dev/null +++ b/plots/scatter-animated-controls/metadata/bokeh.yaml @@ -0,0 +1,19 @@ +# Per-library metadata for bokeh implementation of scatter-animated-controls +# Auto-generated by impl-generate.yml + +library: bokeh +specification_id: scatter-animated-controls +created: '2025-12-31T13:53:51Z' +updated: '2025-12-31T13:53:51Z' +generated_by: claude-opus-4-5-20251101 +workflow_run: 20620303226 +issue: 3067 +python_version: 3.13.11 +library_version: 3.8.1 +preview_url: https://storage.googleapis.com/pyplots-images/plots/scatter-animated-controls/bokeh/plot.png +preview_thumb: https://storage.googleapis.com/pyplots-images/plots/scatter-animated-controls/bokeh/plot_thumb.png +preview_html: https://storage.googleapis.com/pyplots-images/plots/scatter-animated-controls/bokeh/plot.html +quality_score: null +review: + strengths: [] + weaknesses: [] From e6458d629ffdaa1ae38510718aa6a3880e3bf77d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 31 Dec 2025 14:02:46 +0000 Subject: [PATCH 3/7] chore(bokeh): update quality score 88 and review feedback for scatter-animated-controls --- .../implementations/bokeh.py | 6 ++--- .../metadata/bokeh.yaml | 22 +++++++++++++------ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/plots/scatter-animated-controls/implementations/bokeh.py b/plots/scatter-animated-controls/implementations/bokeh.py index b6ccd5a720..576da932ef 100644 --- a/plots/scatter-animated-controls/implementations/bokeh.py +++ b/plots/scatter-animated-controls/implementations/bokeh.py @@ -1,7 +1,7 @@ -"""pyplots.ai +""" pyplots.ai scatter-animated-controls: Animated Scatter Plot with Play Controls -Library: bokeh | Python 3.13 -Quality: pending | Created: 2025-12-31 +Library: bokeh 3.8.1 | Python 3.13.11 +Quality: 88/100 | Created: 2025-12-31 """ import numpy as np diff --git a/plots/scatter-animated-controls/metadata/bokeh.yaml b/plots/scatter-animated-controls/metadata/bokeh.yaml index 57dbf7968a..1af345955a 100644 --- a/plots/scatter-animated-controls/metadata/bokeh.yaml +++ b/plots/scatter-animated-controls/metadata/bokeh.yaml @@ -1,10 +1,7 @@ -# Per-library metadata for bokeh implementation of scatter-animated-controls -# Auto-generated by impl-generate.yml - library: bokeh specification_id: scatter-animated-controls created: '2025-12-31T13:53:51Z' -updated: '2025-12-31T13:53:51Z' +updated: '2025-12-31T14:02:46Z' generated_by: claude-opus-4-5-20251101 workflow_run: 20620303226 issue: 3067 @@ -13,7 +10,18 @@ library_version: 3.8.1 preview_url: https://storage.googleapis.com/pyplots-images/plots/scatter-animated-controls/bokeh/plot.png preview_thumb: https://storage.googleapis.com/pyplots-images/plots/scatter-animated-controls/bokeh/plot_thumb.png preview_html: https://storage.googleapis.com/pyplots-images/plots/scatter-animated-controls/bokeh/plot.html -quality_score: null +quality_score: 88 review: - strengths: [] - weaknesses: [] + strengths: + - Excellent Gapminder-style visualization with realistic country development data + - Proper use of Bokeh interactive widgets (Button, Slider) with CustomJS callbacks + for animation + - Clean implementation of play/pause functionality with proper interval management + - Good colorblind-safe palette for regional differentiation + - Appropriate bubble sizing based on population with good alpha transparency + - Year label as large background text follows spec requirement + - Both HTML (interactive) and PNG (static snapshot) outputs generated + weaknesses: + - Legend is not visible in PNG export - only shown via HTML Div element which is + not rendered in export_png() + - Font sizes could be slightly larger for optimal readability at 4800x2700 canvas From 39da393e31938cd197286a8eadd4e011da975ef1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 31 Dec 2025 14:10:11 +0000 Subject: [PATCH 4/7] fix(bokeh): address review feedback for scatter-animated-controls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add native Bokeh legend using factor_cmap and legend_field for PNG export visibility - Increase font sizes for better readability at 4800x2700 canvas - Remove unused color field from data sources (using factor_cmap instead) - Configure legend styling with proper font sizes and positioning Attempt 1/3 - fixes based on AI review 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../implementations/bokeh.py | 68 +++++++++++-------- 1 file changed, 39 insertions(+), 29 deletions(-) diff --git a/plots/scatter-animated-controls/implementations/bokeh.py b/plots/scatter-animated-controls/implementations/bokeh.py index 576da932ef..8ba5b8722b 100644 --- a/plots/scatter-animated-controls/implementations/bokeh.py +++ b/plots/scatter-animated-controls/implementations/bokeh.py @@ -1,4 +1,4 @@ -""" pyplots.ai +"""pyplots.ai scatter-animated-controls: Animated Scatter Plot with Play Controls Library: bokeh 3.8.1 | Python 3.13.11 Quality: 88/100 | Created: 2025-12-31 @@ -11,6 +11,7 @@ from bokeh.models import Button, ColumnDataSource, CustomJS, Div, HoverTool, Label, Slider from bokeh.plotting import figure from bokeh.resources import CDN +from bokeh.transform import factor_cmap # Data: Simulated country metrics over 20 years (Gapminder-style) @@ -41,15 +42,6 @@ regions = ["North", "South", "East", "West", "Central"] country_regions = [regions[i % 5] for i in range(n_countries)] -# Region colors (colorblind-safe) -region_colors = { - "North": "#306998", # Python Blue - "South": "#FFD43B", # Python Yellow - "East": "#E15759", # Red - "West": "#76B7B2", # Teal - "Central": "#59A14F", # Green -} - # Generate time-series data for each country data_frames = [] for i, country in enumerate(countries): @@ -84,7 +76,6 @@ "gdp_per_capita": gdp[j], "life_expectancy": life_exp[j], "population": population[j], - "color": region_colors[country_regions[i]], } ) @@ -94,7 +85,7 @@ initial_year = years[0] initial_data = df[df["year"] == initial_year].copy() -# Create ColumnDataSource +# Create ColumnDataSource (color is handled by factor_cmap based on region) source = ColumnDataSource( data={ "x": initial_data["gdp_per_capita"].values, @@ -103,11 +94,10 @@ "country": initial_data["country"].values, "region": initial_data["region"].values, "population": initial_data["population"].values, - "color": initial_data["color"].values, } ) -# Store all data for animation +# Store all data for animation (color is handled by factor_cmap based on region) all_data = {} for year in years: year_data = df[df["year"] == year] @@ -118,9 +108,12 @@ "country": year_data["country"].tolist(), "region": year_data["region"].tolist(), "population": year_data["population"].tolist(), - "color": year_data["color"].tolist(), } +# Define regions list and color palette for factor_cmap +regions_list = ["North", "South", "East", "West", "Central"] +color_palette = ["#306998", "#FFD43B", "#E15759", "#76B7B2", "#59A14F"] + # Create figure p = figure( width=4800, @@ -133,12 +126,12 @@ tools="pan,wheel_zoom,box_zoom,reset,save", ) -# Style the figure -p.title.text_font_size = "42pt" -p.xaxis.axis_label_text_font_size = "32pt" -p.yaxis.axis_label_text_font_size = "32pt" -p.xaxis.major_label_text_font_size = "24pt" -p.yaxis.major_label_text_font_size = "24pt" +# Style the figure - increased font sizes for better readability at 4800x2700 +p.title.text_font_size = "48pt" +p.xaxis.axis_label_text_font_size = "36pt" +p.yaxis.axis_label_text_font_size = "36pt" +p.xaxis.major_label_text_font_size = "28pt" +p.yaxis.major_label_text_font_size = "28pt" # Grid styling p.grid.grid_line_alpha = 0.3 @@ -147,11 +140,30 @@ # Background p.background_fill_color = "#fafafa" -# Add scatter plot +# Add scatter plot with legend_field for native legend in PNG export scatter = p.scatter( - x="x", y="y", size="size", color="color", alpha=0.7, line_color="white", line_width=2, source=source + x="x", + y="y", + size="size", + color=factor_cmap("region", palette=color_palette, factors=regions_list), + alpha=0.7, + line_color="white", + line_width=2, + source=source, + legend_field="region", ) +# Configure legend for visibility in PNG export +p.legend.location = "top_left" +p.legend.title = "Region" +p.legend.title_text_font_size = "28pt" +p.legend.label_text_font_size = "24pt" +p.legend.glyph_height = 40 +p.legend.glyph_width = 40 +p.legend.spacing = 10 +p.legend.padding = 20 +p.legend.background_fill_alpha = 0.8 + # Add hover tool hover = HoverTool( tooltips=[ @@ -165,12 +177,12 @@ ) p.add_tools(hover) -# Add year label (large background text) +# Add year label (large background text) - increased size for better visibility year_label = Label( x=70000, y=50, text=str(initial_year), - text_font_size="120pt", + text_font_size="150pt", text_color="#cccccc", text_alpha=0.5, text_align="right", @@ -196,7 +208,7 @@ """ legend_div = Div(text=legend_html, width=800) -# JavaScript callback for slider +# JavaScript callback for slider (region drives color via factor_cmap) slider_callback = CustomJS( args={"source": source, "all_data": all_data, "year_label": year_label}, code=""" @@ -209,7 +221,6 @@ source.data['country'] = data['country']; source.data['region'] = data['region']; source.data['population'] = data['population']; - source.data['color'] = data['color']; source.change.emit(); year_label.text = year; @@ -270,7 +281,7 @@ middle_year = years[len(years) // 2] middle_data = df[df["year"] == middle_year] -# Update source for static export +# Update source for static export (color via factor_cmap based on region) source.data = { "x": middle_data["gdp_per_capita"].values, "y": middle_data["life_expectancy"].values, @@ -278,7 +289,6 @@ "country": middle_data["country"].values, "region": middle_data["region"].values, "population": middle_data["population"].values, - "color": middle_data["color"].values, } year_label.text = str(middle_year) From 0a9aa753130778bfc7f6b0a9466fe6af6003d993 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 31 Dec 2025 14:20:50 +0000 Subject: [PATCH 5/7] chore(bokeh): update quality score 85 and review feedback for scatter-animated-controls --- .../implementations/bokeh.py | 4 ++-- .../metadata/bokeh.yaml | 24 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/plots/scatter-animated-controls/implementations/bokeh.py b/plots/scatter-animated-controls/implementations/bokeh.py index 8ba5b8722b..37d9ea86b7 100644 --- a/plots/scatter-animated-controls/implementations/bokeh.py +++ b/plots/scatter-animated-controls/implementations/bokeh.py @@ -1,7 +1,7 @@ -"""pyplots.ai +""" pyplots.ai scatter-animated-controls: Animated Scatter Plot with Play Controls Library: bokeh 3.8.1 | Python 3.13.11 -Quality: 88/100 | Created: 2025-12-31 +Quality: 85/100 | Created: 2025-12-31 """ import numpy as np diff --git a/plots/scatter-animated-controls/metadata/bokeh.yaml b/plots/scatter-animated-controls/metadata/bokeh.yaml index 1af345955a..199e3da1da 100644 --- a/plots/scatter-animated-controls/metadata/bokeh.yaml +++ b/plots/scatter-animated-controls/metadata/bokeh.yaml @@ -1,7 +1,7 @@ library: bokeh specification_id: scatter-animated-controls created: '2025-12-31T13:53:51Z' -updated: '2025-12-31T14:02:46Z' +updated: '2025-12-31T14:20:50Z' generated_by: claude-opus-4-5-20251101 workflow_run: 20620303226 issue: 3067 @@ -10,18 +10,18 @@ library_version: 3.8.1 preview_url: https://storage.googleapis.com/pyplots-images/plots/scatter-animated-controls/bokeh/plot.png preview_thumb: https://storage.googleapis.com/pyplots-images/plots/scatter-animated-controls/bokeh/plot_thumb.png preview_html: https://storage.googleapis.com/pyplots-images/plots/scatter-animated-controls/bokeh/plot.html -quality_score: 88 +quality_score: 85 review: strengths: - Excellent Gapminder-style visualization with realistic country development data - - Proper use of Bokeh interactive widgets (Button, Slider) with CustomJS callbacks - for animation - - Clean implementation of play/pause functionality with proper interval management - - Good colorblind-safe palette for regional differentiation - - Appropriate bubble sizing based on population with good alpha transparency - - Year label as large background text follows spec requirement - - Both HTML (interactive) and PNG (static snapshot) outputs generated + - 'Good use of Bokeh interactive features: CustomJS animation, slider, play/pause + button, hover tooltips' + - Year label watermark clearly visible in the plot as specified + - Proper title format and descriptive axis labels with units + - Good color palette with 5 distinct region colors + - Appropriate bubble sizing based on population weaknesses: - - Legend is not visible in PNG export - only shown via HTML Div element which is - not rendered in export_png() - - Font sizes could be slightly larger for optimal readability at 4800x2700 canvas + - Legend in PNG export is extremely small and nearly illegible despite code setting + larger font sizes - appears cut off in top right corner + - Legend positioning and sizing need adjustment for static PNG export to match the + intended 4800x2700 canvas From 96d436b9bc85fbfc85afc5e46a70cb46937d9dc2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 31 Dec 2025 14:26:30 +0000 Subject: [PATCH 6/7] fix(bokeh): address review feedback for scatter-animated-controls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Attempt 2/3 - fixes based on AI review: - Increased legend font sizes (title: 36pt, labels: 32pt) for better visibility - Increased legend glyph sizes (60x60) for clearer region markers - Added legend border and increased background opacity (0.9) - Added min_border settings to prevent legend clipping in PNG export - Increased legend margin (40) and padding (30) for better spacing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../implementations/bokeh.py | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/plots/scatter-animated-controls/implementations/bokeh.py b/plots/scatter-animated-controls/implementations/bokeh.py index 37d9ea86b7..3f46b5d984 100644 --- a/plots/scatter-animated-controls/implementations/bokeh.py +++ b/plots/scatter-animated-controls/implementations/bokeh.py @@ -1,7 +1,7 @@ -""" pyplots.ai +"""pyplots.ai scatter-animated-controls: Animated Scatter Plot with Play Controls Library: bokeh 3.8.1 | Python 3.13.11 -Quality: 85/100 | Created: 2025-12-31 +Created: 2025-12-31 """ import numpy as np @@ -140,6 +140,12 @@ # Background p.background_fill_color = "#fafafa" +# Add margins to prevent legend clipping +p.min_border_left = 120 +p.min_border_right = 120 +p.min_border_top = 100 +p.min_border_bottom = 100 + # Add scatter plot with legend_field for native legend in PNG export scatter = p.scatter( x="x", @@ -153,16 +159,19 @@ legend_field="region", ) -# Configure legend for visibility in PNG export +# Configure legend for visibility in PNG export - positioned inside plot with large fonts p.legend.location = "top_left" p.legend.title = "Region" -p.legend.title_text_font_size = "28pt" -p.legend.label_text_font_size = "24pt" -p.legend.glyph_height = 40 -p.legend.glyph_width = 40 -p.legend.spacing = 10 -p.legend.padding = 20 -p.legend.background_fill_alpha = 0.8 +p.legend.title_text_font_size = "36pt" +p.legend.label_text_font_size = "32pt" +p.legend.glyph_height = 60 +p.legend.glyph_width = 60 +p.legend.spacing = 15 +p.legend.padding = 30 +p.legend.margin = 40 +p.legend.background_fill_alpha = 0.9 +p.legend.border_line_color = "#aaaaaa" +p.legend.border_line_width = 2 # Add hover tool hover = HoverTool( From 23aaae395a744ce9d2497edfae1828e540fcaf6f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 31 Dec 2025 14:49:39 +0000 Subject: [PATCH 7/7] chore(bokeh): update quality score 91 and review feedback for scatter-animated-controls --- .../implementations/bokeh.py | 4 +-- .../metadata/bokeh.yaml | 27 ++++++++++--------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/plots/scatter-animated-controls/implementations/bokeh.py b/plots/scatter-animated-controls/implementations/bokeh.py index 3f46b5d984..f19047f70c 100644 --- a/plots/scatter-animated-controls/implementations/bokeh.py +++ b/plots/scatter-animated-controls/implementations/bokeh.py @@ -1,7 +1,7 @@ -"""pyplots.ai +""" pyplots.ai scatter-animated-controls: Animated Scatter Plot with Play Controls Library: bokeh 3.8.1 | Python 3.13.11 -Created: 2025-12-31 +Quality: 91/100 | Created: 2025-12-31 """ import numpy as np diff --git a/plots/scatter-animated-controls/metadata/bokeh.yaml b/plots/scatter-animated-controls/metadata/bokeh.yaml index 199e3da1da..7a5f014b08 100644 --- a/plots/scatter-animated-controls/metadata/bokeh.yaml +++ b/plots/scatter-animated-controls/metadata/bokeh.yaml @@ -1,7 +1,7 @@ library: bokeh specification_id: scatter-animated-controls created: '2025-12-31T13:53:51Z' -updated: '2025-12-31T14:20:50Z' +updated: '2025-12-31T14:49:39Z' generated_by: claude-opus-4-5-20251101 workflow_run: 20620303226 issue: 3067 @@ -10,18 +10,19 @@ library_version: 3.8.1 preview_url: https://storage.googleapis.com/pyplots-images/plots/scatter-animated-controls/bokeh/plot.png preview_thumb: https://storage.googleapis.com/pyplots-images/plots/scatter-animated-controls/bokeh/plot_thumb.png preview_html: https://storage.googleapis.com/pyplots-images/plots/scatter-animated-controls/bokeh/plot.html -quality_score: 85 +quality_score: 91 review: strengths: - - Excellent Gapminder-style visualization with realistic country development data - - 'Good use of Bokeh interactive features: CustomJS animation, slider, play/pause - button, hover tooltips' - - Year label watermark clearly visible in the plot as specified - - Proper title format and descriptive axis labels with units - - Good color palette with 5 distinct region colors - - Appropriate bubble sizing based on population + - Excellent implementation of Gapminder-style animated visualization with all required + interactive controls + - Text sizing is well-calibrated for the 4800x2700 canvas with clear hierarchy + - Color palette is distinctive and colorblind-friendly with good contrast + - Year watermark provides clear temporal context without obscuring data + - Smooth animation implementation using CustomJS with proper play/pause toggle + - Hover tooltips provide detailed information for each country + - PNG snapshot shows middle frame (2014) as representative static view weaknesses: - - Legend in PNG export is extremely small and nearly illegible despite code setting - larger font sizes - appears cut off in top right corner - - Legend positioning and sizing need adjustment for static PNG export to match the - intended 4800x2700 canvas + - Legend glyph sizes (60px) appear small compared to actual bubble sizes in the + plot, making it harder to associate legend entries with data points + - Grid styling with dashed lines could be slightly more subtle (alpha 0.2 instead + of 0.3)