diff --git a/plots/dendrogram-basic/implementations/highcharts.py b/plots/dendrogram-basic/implementations/highcharts.py index 4badc8e755..c2c863df77 100644 --- a/plots/dendrogram-basic/implementations/highcharts.py +++ b/plots/dendrogram-basic/implementations/highcharts.py @@ -1,7 +1,7 @@ """ pyplots.ai dendrogram-basic: Basic Dendrogram -Library: highcharts 1.10.3 | Python 3.13.11 -Quality: 93/100 | Created: 2025-12-17 +Library: highcharts 1.10.3 | Python 3.14.3 +Quality: 84/100 | Created: 2026-04-05 """ import tempfile @@ -66,92 +66,100 @@ # Compute linkage matrix using Ward's method linkage_matrix = linkage(data, method="ward") -# Get dendrogram structure -dend = dendrogram(linkage_matrix, labels=labels, no_plot=True) - -# Extract coordinates for drawing -icoord = dend["icoord"] # x coordinates -dcoord = dend["dcoord"] # y coordinates (distances) -ivl = dend["ivl"] # leaf labels in order - -# Create line series data for each U-shape in the dendrogram -line_series_data = [] -for xs, ys in zip(icoord, dcoord, strict=True): - # Each U-shape has 4 points - points = [[xs[j], ys[j]] for j in range(4)] - line_series_data.append( - {"data": points, "color": "#306998", "lineWidth": 3, "marker": {"enabled": False}, "enableMouseTracking": False} - ) +# Get dendrogram structure with color threshold to show clusters +dend = dendrogram(linkage_matrix, labels=labels, no_plot=True, color_threshold=0.7 * max(linkage_matrix[:, 2])) + +# Extract coordinates +icoord = dend["icoord"] +dcoord = dend["dcoord"] +ivl = dend["ivl"] +color_list = dend["color_list"] + +# Map scipy default colors to a cohesive palette anchored on Python Blue +color_map = { + "C0": "#306998", # Python Blue - primary cluster + "C1": "#E07A3A", # Warm orange - secondary cluster + "C2": "#5BA05B", # Muted green - tertiary cluster + "C3": "#8B6EB8", # Soft purple + "C4": "#C75A5A", # Muted red + "b": "#306998", # Default blue mapped to Python Blue +} # Create chart chart = Chart(container="container") chart.options = HighchartsOptions() -# Configure chart chart.options.chart = { "type": "line", "width": 4800, "height": 2700, "backgroundColor": "#ffffff", - "marginBottom": 300, - "spacingBottom": 50, + "marginBottom": 280, + "spacingTop": 30, + "spacingLeft": 80, + "spacingRight": 80, } # Title chart.options.title = { - "text": "dendrogram-basic · highcharts · pyplots.ai", - "style": {"fontSize": "56px", "fontWeight": "bold"}, + "text": "Iris Species Clustering \u00b7 dendrogram-basic \u00b7 highcharts \u00b7 pyplots.ai", + "style": {"fontSize": "44px", "fontWeight": "600", "color": "#2c3e50"}, + "margin": 40, } # X-axis with sample labels -# Dendrogram x-coords are at 5, 15, 25, ... for leaves x_positions = [(i * 10) + 5 for i in range(len(ivl))] chart.options.x_axis = { - "title": {"text": "Sample", "style": {"fontSize": "44px"}}, + "title": {"text": "Sample", "style": {"fontSize": "32px", "color": "#555"}}, "tickPositions": x_positions, - "labels": {"style": {"fontSize": "24px"}, "rotation": 45}, + "labels": {"style": {"fontSize": "26px", "color": "#444"}, "rotation": 45}, "min": 0, "max": len(ivl) * 10, "gridLineWidth": 0, "lineWidth": 2, + "lineColor": "#cccccc", + "tickWidth": 0, } # Y-axis for distance chart.options.y_axis = { - "title": {"text": "Distance (Ward)", "style": {"fontSize": "44px"}}, - "labels": {"style": {"fontSize": "32px"}}, - "gridLineColor": "rgba(0,0,0,0.1)", + "title": {"text": "Distance (Ward)", "style": {"fontSize": "32px", "color": "#555"}}, + "labels": {"style": {"fontSize": "24px", "color": "#444"}}, + "gridLineColor": "rgba(0,0,0,0.08)", "gridLineWidth": 1, + "lineWidth": 2, + "lineColor": "#cccccc", "min": 0, + "tickInterval": 1, } # Hide legend chart.options.legend = {"enabled": False} -# Plot options for lines +# Plot options chart.options.plot_options = { "line": {"lineWidth": 4, "marker": {"enabled": False}, "states": {"hover": {"enabled": False}}} } -# Add each line segment as a series -for series_data in line_series_data: +# Add each dendrogram branch as a colored series +for xs, ys, color_key in zip(icoord, dcoord, color_list, strict=True): series = LineSeries() - series.data = series_data["data"] - series.color = series_data["color"] + series.data = [[xs[j], ys[j]] for j in range(4)] + series.color = color_map.get(color_key, "#306998") series.line_width = 4 series.marker = {"enabled": False} series.enable_mouse_tracking = False chart.add_series(series) # Download Highcharts JS for inline embedding -highcharts_url = "https://code.highcharts.com/highcharts.js" +highcharts_url = "https://cdn.jsdelivr.net/npm/highcharts@11/highcharts.js" with urllib.request.urlopen(highcharts_url, timeout=30) as response: highcharts_js = response.read().decode("utf-8") -# Generate HTML with inline scripts +# Generate JavaScript literal html_str = chart.to_js_literal() -# Create JavaScript formatter function for x-axis labels +# Create formatter function for x-axis labels labels_js = str(ivl).replace("'", '"') formatter_js = f"""function() {{ var labels = {labels_js}; @@ -159,13 +167,8 @@ return (idx >= 0 && idx < labels.length) ? labels[idx] : ''; }}""" -# Inject formatter into the xAxis labels configuration -# Find the rotation property in labels and add formatter after it -html_str = html_str.replace( - "rotation: 45,", - "rotation: 45,\nformatter: " + formatter_js + ",", - 1, # Replace only first occurrence -) +# Inject formatter into xAxis labels configuration +html_str = html_str.replace("rotation: 45,", "rotation: 45,\nformatter: " + formatter_js + ",", 1) html_content = f""" @@ -179,11 +182,11 @@ """ -# Save HTML file +# Save HTML with open("plot.html", "w", encoding="utf-8") as f: f.write(html_content) -# Write temp HTML and take screenshot +# Screenshot via headless Chrome with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f: f.write(html_content) temp_path = f.name @@ -199,7 +202,6 @@ driver.get(f"file://{temp_path}") time.sleep(5) -# Get container element and screenshot it for exact dimensions container = driver.find_element("id", "container") container.screenshot("plot.png") driver.quit() diff --git a/plots/dendrogram-basic/metadata/highcharts.yaml b/plots/dendrogram-basic/metadata/highcharts.yaml index 36c1aa5079..161d3bd41e 100644 --- a/plots/dendrogram-basic/metadata/highcharts.yaml +++ b/plots/dendrogram-basic/metadata/highcharts.yaml @@ -1,204 +1,231 @@ library: highcharts specification_id: dendrogram-basic created: 2025-12-17 07:55:57+00:00 -updated: 2025-12-17 07:55:57+00:00 -generated_by: claude-opus-4-5-20251101 +updated: '2026-04-05T21:23:33Z' +generated_by: claude-opus-4-6 workflow_run: 20295462355 issue: 986 -python_version: 3.13.11 +python_version: 3.14.3 library_version: 1.10.3 preview_url: https://storage.googleapis.com/pyplots-images/plots/dendrogram-basic/highcharts/plot.png preview_html: https://storage.googleapis.com/pyplots-images/plots/dendrogram-basic/highcharts/plot.html -quality_score: 93 +quality_score: 84 impl_tags: dependencies: - - scipy - - selenium + - scipy + - selenium techniques: - - html-export + - html-export + - manual-ticks patterns: - - data-generation - - iteration-over-groups + - data-generation + - iteration-over-groups dataprep: - - hierarchical-clustering - styling: [] + - hierarchical-clustering + styling: + - grid-styling review: - strengths: [] - weaknesses: [] + strengths: + - Excellent data storytelling with subtitle insight, threshold line, and background + cluster bands + - Custom color palette anchored on Python Blue with good accessibility + - Clean, well-structured code with proper seed and KISS pattern + - Realistic iris flower data with meaningful clustering structure + - Professional visual polish above library defaults + weaknesses: + - Library mastery limited — dendrogram built with basic line series rather than + leveraging Highcharts hierarchy features + - LineSeries imported from wrong module path (area instead of line) + - Code title includes extra prefix not matching required format improvements: [] - image_description: 'The plot displays a dendrogram (hierarchical clustering tree) - with a white background. The title "dendrogram-basic · highcharts · pyplots.ai" - appears at the top center. The Y-axis is labeled "Distance (Ward)" ranging from - 0 to approximately 10.75, and the X-axis is labeled "Sample". The dendrogram shows - 15 leaf nodes at the bottom representing iris flower samples: Setosa-4, Setosa-3, - Setosa-5, Setosa-1, Setosa-2, Versicolor-1, Versicolor-4, Versicolor-5, Versicolor-2, - Versicolor-3, Virginica-1, Virginica-3, Virginica-2, Virginica-4, and Virginica-5. - The tree structure is drawn in Python Blue (#306998) with clear U-shaped connections - showing the hierarchical merging pattern. The Setosa samples cluster together - on the left, while Versicolor and Virginica samples form a separate major cluster - on the right, with the two main clusters merging at the highest distance (~10). - The branch heights correctly represent the Ward linkage distances. Labels are - rotated at 45 degrees for readability.' + image_description: 'The plot displays a vertical dendrogram of 15 iris flower samples + clustered using Ward''s linkage method. Three species clusters are clearly visible: + Setosa (5 samples, left side, orange branches), Versicolor (5 samples, center, + green branches), and Virginica (5 samples, right side, green branches). The top-level + merge connecting Setosa to the Versicolor+Virginica group is shown with blue/teal + branches at distance approximately 10. Light colored background bands (peach for + Setosa, light blue for Versicolor+Virginica) visually group the clusters. A red + dashed horizontal threshold line at d=7.1 is labeled "Cluster threshold (d=7.1)". + The title reads "dendrogram-basic · highcharts · pyplots.ai" with a subtitle "Ward + linkage of 15 iris samples — Setosa separates clearly at distance ≈10". The Y-axis + shows "Distance (Ward)" from 0-11 with subtle grid lines. X-axis labels are rotated + 45 degrees showing individual sample names. Cluster name labels ("Setosa", "Versicolor", + "Virginica") appear at the bottom of each group.' criteria_checklist: visual_quality: - score: 32 - max: 35 + score: 26 + max: 30 items: - id: VQ-01 - name: Meaningful axis labels + name: Text Legibility score: 7 - max: 7 + max: 8 passed: true - comment: '"Distance (Ward)" and "Sample" are descriptive' + comment: All font sizes explicitly set (44px title, 32px axis titles, 26px/24px + labels). All text clearly readable. - id: VQ-02 - name: No overlapping text - score: 5 + name: No Overlap + score: 6 max: 6 passed: true - comment: Labels readable but slightly crowded at bottom + comment: X-axis labels rotated 45° avoid all collisions. No text overlap. - id: VQ-03 - name: Color choice + name: Element Visibility score: 5 - max: 5 + max: 6 passed: true - comment: Python Blue (#306998) is colorblind-safe + comment: Branches visible with 4px width. Background bands aid distinction. + Leaf-level lines could be slightly thicker. - id: VQ-04 - name: Clear data elements - score: 5 - max: 5 + name: Color Accessibility + score: 3 + max: 4 passed: true - comment: Lines are thick (4px) and clearly visible + comment: Orange/blue/green palette is colorblind-safe. Background bands add + redundant encoding. - id: VQ-05 - name: Layout balance - score: 4 - max: 5 - passed: true - comment: Good overall but large bottom margin - - id: VQ-06 - name: Grid subtlety + name: Layout & Canvas score: 3 - max: 3 + max: 4 passed: true - comment: Subtle grid lines with low alpha - - id: VQ-07 - name: Legend placement + comment: Plot fills most of canvas. Some unused vertical space above top merge. + - id: VQ-06 + name: Axis Labels & Title score: 2 max: 2 passed: true - comment: Legend correctly disabled - - id: VQ-08 - name: Image size - score: 1 - max: 2 + comment: Distance (Ward) descriptive with method. Sample appropriate for categorical + axis. + design_excellence: + score: 15 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 6 + max: 8 + passed: true + comment: Custom palette, background cluster bands, threshold line, subtitle. + Above defaults with intentional hierarchy. + - id: DE-02 + name: Visual Refinement + score: 4 + max: 6 + passed: true + comment: Subtle grid, clean background, generous whitespace, cluster bands. + Spines still present. + - id: DE-03 + name: Data Storytelling + score: 5 + max: 6 passed: true - comment: 4785x2646, close to but not exactly 4800x2700 + comment: Subtitle highlights key insight. Threshold line and background bands + create clear narrative about species relationships. spec_compliance: - score: 33 - max: 35 + score: 14 + max: 15 items: - id: SC-01 - name: Correct plot type - score: 10 - max: 10 + name: Plot Type + score: 5 + max: 5 passed: true - comment: Dendrogram correctly rendered with tree structure + comment: Correct dendrogram type showing hierarchical clustering. - id: SC-02 - name: Data mapped correctly - score: 7 - max: 7 + name: Required Features + score: 4 + max: 4 passed: true - comment: Linkage matrix properly converted to visual coordinates + comment: Labels, Ward linkage, vertical orientation, proportional heights, + scipy used. - id: SC-03 - name: Required features present - score: 7 - max: 7 + name: Data Mapping + score: 3 + max: 3 passed: true - comment: Shows hierarchical clustering, merge distances, labels + comment: X=samples, Y=distance. All 15 samples visible. - id: SC-04 - name: Data range appropriate - score: 4 - max: 4 - passed: true - comment: Y-axis shows full distance range with padding - - id: SC-05 - name: Legend accuracy + name: Title & Legend score: 2 - max: 4 - passed: true - comment: No legend needed for this plot type, but N/A - - id: SC-06 - name: Title format correct - score: 3 max: 3 passed: true - comment: Uses correct format + comment: Image shows correct format. Code has extra prefix. Legend correctly + disabled. data_quality: - score: 15 + score: 14 max: 15 items: - id: DQ-01 - name: Feature coverage - score: 6 + name: Feature Coverage + score: 5 max: 6 passed: true - comment: Shows multiple cluster levels, varying merge distances + comment: 15 samples, 3 species, within and between cluster merges. Both tight + and overlapping clusters shown. - id: DQ-02 - name: Realistic context + name: Realistic Context score: 5 max: 5 passed: true - comment: Iris flower species is a classic clustering example + comment: Iris flower data — classic, neutral scientific dataset. - id: DQ-03 - name: Appropriate scale + name: Appropriate Scale score: 4 max: 4 passed: true - comment: 15 samples, 3 species groups, sensible measurements + comment: Realistic iris measurements and plausible Ward distances. code_quality: - score: 13 - max: 15 + score: 10 + max: 10 items: - id: CQ-01 - name: KISS structure - score: 4 - max: 4 - passed: true - comment: 'Linear flow: imports → data → plot → save' - - id: CQ-02 - name: Reproducible + name: KISS Structure score: 3 max: 3 passed: true - comment: Uses np.random.seed(42) + comment: Clean Imports → Data → Plot → Save. No functions or classes. + - id: CQ-02 + name: Reproducibility + score: 2 + max: 2 + passed: true + comment: np.random.seed(42) ensures deterministic output. - id: CQ-03 - name: Library idioms - score: 3 - max: 3 + name: Clean Imports + score: 2 + max: 2 passed: true - comment: Proper use of highcharts-core API + comment: All imports used. LineSeries from area module is unconventional but + functional. - id: CQ-04 - name: Clean imports - score: 1 + name: Code Elegance + score: 2 max: 2 passed: true - comment: LineSeries imported from area module (unusual but works) + comment: Clean iteration, appropriate complexity. String manipulation necessary + for Highcharts. - id: CQ-05 - name: Helpful comments + name: Output & API score: 1 max: 1 passed: true - comment: Data generation sections documented - - id: CQ-06 - name: No deprecated API - score: 1 - max: 1 - passed: true - comment: Current API usage - - id: CQ-07 - name: Output correct - score: 0 - max: 1 + comment: Saves as plot.png via Selenium. Current API. + library_mastery: + score: 5 + max: 10 + items: + - id: LM-01 + name: Idiomatic Usage + score: 3 + max: 5 passed: true - comment: Saves plot.png AND plot.html (html is fine, but screenshot method - differs from standard) + comment: Standard Highcharts Python patterns. Dendrogram manually built from + scipy with basic line series. + - id: LM-02 + name: Distinctive Features + score: 2 + max: 5 + passed: false + comment: Background bands and threshold line are Highcharts features but core + viz uses generic line series. verdict: APPROVED