From 6cb88fd1bd60718142c60114d0a6cc01294d8074 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 31 Dec 2025 10:59:55 +0000 Subject: [PATCH 1/5] feat(bokeh): implement chernoff-basic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- plots/chernoff-basic/implementations/bokeh.py | 216 ++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 plots/chernoff-basic/implementations/bokeh.py diff --git a/plots/chernoff-basic/implementations/bokeh.py b/plots/chernoff-basic/implementations/bokeh.py new file mode 100644 index 0000000000..ddde0d6887 --- /dev/null +++ b/plots/chernoff-basic/implementations/bokeh.py @@ -0,0 +1,216 @@ +"""pyplots.ai +chernoff-basic: Chernoff Faces for Multivariate Data +Library: bokeh | Python 3.13 +Quality: pending | Created: 2025-12-31 +""" + +import numpy as np +from bokeh.io import export_png +from bokeh.models import Label +from bokeh.plotting import figure + + +# Generate synthetic company performance data (4 metrics for 12 companies) +# Metrics: Revenue Growth, Profit Margin, Customer Satisfaction, Market Share +np.random.seed(42) + +# Three company sectors with different profiles +sectors = ["Tech", "Retail", "Energy"] +sector_idx = np.array([0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2]) + +# Tech companies: high growth, high margin +tech_data = np.column_stack( + [ + np.random.uniform(0.6, 1.0, 4), # Revenue Growth + np.random.uniform(0.5, 0.9, 4), # Profit Margin + np.random.uniform(0.6, 0.95, 4), # Customer Satisfaction + np.random.uniform(0.3, 0.7, 4), # Market Share + ] +) + +# Retail companies: moderate growth, moderate margin +retail_data = np.column_stack( + [ + np.random.uniform(0.2, 0.5, 4), # Revenue Growth + np.random.uniform(0.15, 0.4, 4), # Profit Margin + np.random.uniform(0.5, 0.8, 4), # Customer Satisfaction + np.random.uniform(0.4, 0.8, 4), # Market Share + ] +) + +# Energy companies: low growth, variable margin +energy_data = np.column_stack( + [ + np.random.uniform(0.05, 0.3, 4), # Revenue Growth + np.random.uniform(0.3, 0.6, 4), # Profit Margin + np.random.uniform(0.3, 0.6, 4), # Customer Satisfaction + np.random.uniform(0.5, 0.9, 4), # Market Share + ] +) + +data = np.vstack([tech_data, retail_data, energy_data]) + +# Normalize each feature to 0-1 +data_norm = (data - data.min(axis=0)) / (data.max(axis=0) - data.min(axis=0) + 1e-10) + +# Colors for sectors +colors = ["#306998", "#FFD43B", "#8B4513"] + + +def draw_chernoff_face(p, cx, cy, features, color, label, face_size=0.35): + """Draw a Chernoff face at position (cx, cy) using normalized features (0-1).""" + # features: [revenue_growth, profit_margin, customer_satisfaction, market_share] + # Mapping: + # - revenue_growth -> face width + # - profit_margin -> face height + # - customer_satisfaction -> eye size + # - market_share -> mouth curvature + + face_width = 0.3 + features[0] * 0.3 # 0.3 to 0.6 + face_height = 0.35 + features[1] * 0.25 # 0.35 to 0.6 + eye_size = 0.03 + features[2] * 0.05 # 0.03 to 0.08 + mouth_curve = features[3] # 0 to 1 (sad to happy) + + # Scale by face_size + face_width *= face_size + face_height *= face_size + eye_size *= face_size + + # Draw face outline (ellipse approximation using patches) + theta = np.linspace(0, 2 * np.pi, 50) + face_x = cx + face_width * np.cos(theta) + face_y = cy + face_height * np.sin(theta) + p.patch(face_x, face_y, fill_color=color, fill_alpha=0.3, line_color=color, line_width=3) + + # Draw eyes + eye_spacing = face_width * 0.5 + eye_y = cy + face_height * 0.25 + + # Left eye + left_eye_x = cx - eye_spacing + eye_theta = np.linspace(0, 2 * np.pi, 30) + left_ex = left_eye_x + eye_size * np.cos(eye_theta) + left_ey = eye_y + eye_size * np.sin(eye_theta) + p.patch(left_ex, left_ey, fill_color="white", line_color="#333333", line_width=2) + + # Left pupil + pupil_size = eye_size * 0.5 + left_px = left_eye_x + pupil_size * np.cos(eye_theta) * 0.6 + left_py = eye_y + pupil_size * np.sin(eye_theta) * 0.6 + p.patch(left_px, left_py, fill_color="#333333", line_color="#333333") + + # Right eye + right_eye_x = cx + eye_spacing + right_ex = right_eye_x + eye_size * np.cos(eye_theta) + right_ey = eye_y + eye_size * np.sin(eye_theta) + p.patch(right_ex, right_ey, fill_color="white", line_color="#333333", line_width=2) + + # Right pupil + right_px = right_eye_x + pupil_size * np.cos(eye_theta) * 0.6 + right_py = eye_y + pupil_size * np.sin(eye_theta) * 0.6 + p.patch(right_px, right_py, fill_color="#333333", line_color="#333333") + + # Draw eyebrows + brow_y = eye_y + eye_size * 1.8 + brow_width = eye_size * 1.2 + eyebrow_slant = (features[0] - 0.5) * 0.02 * face_size # Based on revenue_growth + + p.line( + [left_eye_x - brow_width, left_eye_x + brow_width], + [brow_y + eyebrow_slant, brow_y - eyebrow_slant], + line_color="#333333", + line_width=3, + ) + p.line( + [right_eye_x - brow_width, right_eye_x + brow_width], + [brow_y - eyebrow_slant, brow_y + eyebrow_slant], + line_color="#333333", + line_width=3, + ) + + # Draw nose + nose_length = 0.02 + features[1] * 0.03 # Based on profit_margin + nose_length *= face_size + nose_y_top = cy + face_height * 0.1 + nose_y_bottom = cy - face_height * 0.1 + p.line([cx, cx], [nose_y_top, nose_y_bottom], line_color="#333333", line_width=2) + p.line( + [cx - nose_length * 0.5, cx, cx + nose_length * 0.5], + [nose_y_bottom, nose_y_bottom - nose_length * 0.3, nose_y_bottom], + line_color="#333333", + line_width=2, + ) + + # Draw mouth (curved based on market_share) + mouth_y = cy - face_height * 0.4 + mouth_width = face_width * 0.5 + mouth_x = np.linspace(cx - mouth_width, cx + mouth_width, 20) + # mouth_curve: 0=sad (curve down), 1=happy (curve up) + curve_amount = (mouth_curve - 0.5) * 0.08 * face_size + mouth_y_curve = mouth_y + curve_amount * (1 - ((mouth_x - cx) / mouth_width) ** 2) * 4 + p.line(mouth_x, mouth_y_curve, line_color="#333333", line_width=3) + + # Add label below face + label_obj = Label( + x=cx, y=cy - face_height - 0.1, text=label, text_align="center", text_font_size="20pt", text_color="#333333" + ) + p.add_layout(label_obj) + + +# Create figure with 4x3 grid for 12 faces +p = figure( + width=4800, + height=2700, + title="chernoff-basic \u00b7 bokeh \u00b7 pyplots.ai", + x_range=(-0.1, 4.1), + y_range=(-0.2, 3.2), + tools="", +) + +# Style +p.title.text_font_size = "32pt" +p.title.align = "center" +p.xaxis.visible = False +p.yaxis.visible = False +p.xgrid.visible = False +p.ygrid.visible = False +p.outline_line_color = None +p.background_fill_color = "#FAFAFA" + +# Draw faces in a 4x3 grid +for i, (features, sec_idx) in enumerate(zip(data_norm, sector_idx, strict=True)): + col = i % 4 + row = 2 - i // 4 # Start from top row + cx = col + 0.5 + cy = row + 0.5 + + label = f"{sectors[sec_idx]} #{i % 4 + 1}" + draw_chernoff_face(p, cx, cy, features, colors[sec_idx], label, face_size=0.4) + +# Add legend manually using patches and labels (positioned below grid) +legend_y_base = 2.95 +legend_x_positions = [0.5, 1.5, 2.5] # Spread horizontally +for i, (name, color) in enumerate(zip(sectors, colors, strict=True)): + lx_center = legend_x_positions[i] + theta = np.linspace(0, 2 * np.pi, 30) + lx = lx_center + 0.06 * np.cos(theta) + ly = legend_y_base + 0.06 * np.sin(theta) + p.patch(lx, ly, fill_color=color, fill_alpha=0.3, line_color=color, line_width=2) + legend_label = Label( + x=lx_center + 0.12, y=legend_y_base - 0.02, text=name, text_font_size="20pt", text_color="#333333" + ) + p.add_layout(legend_label) + +# Add subtitle with feature mapping explanation +subtitle = Label( + x=2.0, + y=-0.02, + text="Face width=Revenue Growth, Face height=Profit Margin, Eye size=Satisfaction, Mouth=Market Share", + text_align="center", + text_font_size="18pt", + text_color="#666666", +) +p.add_layout(subtitle) + +# Save +export_png(p, filename="plot.png") From b045971d3b40239b1b2c8d4f39e454a2e3134c5e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 31 Dec 2025 11:00:13 +0000 Subject: [PATCH 2/5] chore(bokeh): add metadata for chernoff-basic --- plots/chernoff-basic/metadata/bokeh.yaml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 plots/chernoff-basic/metadata/bokeh.yaml diff --git a/plots/chernoff-basic/metadata/bokeh.yaml b/plots/chernoff-basic/metadata/bokeh.yaml new file mode 100644 index 0000000000..5d7bdde020 --- /dev/null +++ b/plots/chernoff-basic/metadata/bokeh.yaml @@ -0,0 +1,19 @@ +# Per-library metadata for bokeh implementation of chernoff-basic +# Auto-generated by impl-generate.yml + +library: bokeh +specification_id: chernoff-basic +created: '2025-12-31T11:00:13Z' +updated: '2025-12-31T11:00:13Z' +generated_by: claude-opus-4-5-20251101 +workflow_run: 20617520899 +issue: 3003 +python_version: 3.13.11 +library_version: 3.8.1 +preview_url: https://storage.googleapis.com/pyplots-images/plots/chernoff-basic/bokeh/plot.png +preview_thumb: https://storage.googleapis.com/pyplots-images/plots/chernoff-basic/bokeh/plot_thumb.png +preview_html: https://storage.googleapis.com/pyplots-images/plots/chernoff-basic/bokeh/plot.html +quality_score: null +review: + strengths: [] + weaknesses: [] From 2b8a4292f6eb534ceb6899b7ab117b32135e661a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 31 Dec 2025 11:11:16 +0000 Subject: [PATCH 3/5] chore(bokeh): update quality score 88 and review feedback for chernoff-basic --- plots/chernoff-basic/implementations/bokeh.py | 6 ++--- plots/chernoff-basic/metadata/bokeh.yaml | 24 +++++++++++++------ 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/plots/chernoff-basic/implementations/bokeh.py b/plots/chernoff-basic/implementations/bokeh.py index ddde0d6887..abd3b48803 100644 --- a/plots/chernoff-basic/implementations/bokeh.py +++ b/plots/chernoff-basic/implementations/bokeh.py @@ -1,7 +1,7 @@ -"""pyplots.ai +""" pyplots.ai chernoff-basic: Chernoff Faces for Multivariate Data -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/chernoff-basic/metadata/bokeh.yaml b/plots/chernoff-basic/metadata/bokeh.yaml index 5d7bdde020..36bb095d2e 100644 --- a/plots/chernoff-basic/metadata/bokeh.yaml +++ b/plots/chernoff-basic/metadata/bokeh.yaml @@ -1,10 +1,7 @@ -# Per-library metadata for bokeh implementation of chernoff-basic -# Auto-generated by impl-generate.yml - library: bokeh specification_id: chernoff-basic created: '2025-12-31T11:00:13Z' -updated: '2025-12-31T11:00:13Z' +updated: '2025-12-31T11:11:16Z' generated_by: claude-opus-4-5-20251101 workflow_run: 20617520899 issue: 3003 @@ -13,7 +10,20 @@ library_version: 3.8.1 preview_url: https://storage.googleapis.com/pyplots-images/plots/chernoff-basic/bokeh/plot.png preview_thumb: https://storage.googleapis.com/pyplots-images/plots/chernoff-basic/bokeh/plot_thumb.png preview_html: https://storage.googleapis.com/pyplots-images/plots/chernoff-basic/bokeh/plot.html -quality_score: null +quality_score: 88 review: - strengths: [] - weaknesses: [] + strengths: + - Excellent implementation of Chernoff faces concept with clear visual differentiation + between sectors + - Smart use of realistic business context (Tech/Retail/Energy companies with meaningful + metrics) + - Clean grid layout that maximizes canvas utilization with balanced spacing + - Good color accessibility with distinct, colorblind-safe palette + - Helpful feature mapping subtitle that aids interpretation + - Proper normalization of data as specified + weaknesses: + - Code uses a helper function which violates the KISS principle (should be inline + code) + - Could leverage more Bokeh-specific features like ColumnDataSource or hover tooltips + for interactivity + - Subtitle text at 18pt is slightly small for the 4800×2700 canvas - consider 20pt+ From 9f0f5794c37e927428d75d8963b71e6bda602ebb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 31 Dec 2025 11:20:58 +0000 Subject: [PATCH 4/5] fix(bokeh): address review feedback for chernoff-basic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Attempt 1/3 - fixes based on AI review: - Remove helper function (KISS principle) - inline all face drawing code - Increase subtitle font size from 18pt to 22pt for better legibility - Add ColumnDataSource and HoverTool for Bokeh-specific interactivity - Add hover tooltips showing company metrics on face hover 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- plots/chernoff-basic/implementations/bokeh.py | 149 +++++++++++------- 1 file changed, 94 insertions(+), 55 deletions(-) diff --git a/plots/chernoff-basic/implementations/bokeh.py b/plots/chernoff-basic/implementations/bokeh.py index abd3b48803..1d6fd9bddd 100644 --- a/plots/chernoff-basic/implementations/bokeh.py +++ b/plots/chernoff-basic/implementations/bokeh.py @@ -1,4 +1,4 @@ -""" pyplots.ai +"""pyplots.ai chernoff-basic: Chernoff Faces for Multivariate Data Library: bokeh 3.8.1 | Python 3.13.11 Quality: 88/100 | Created: 2025-12-31 @@ -6,7 +6,7 @@ import numpy as np from bokeh.io import export_png -from bokeh.models import Label +from bokeh.models import ColumnDataSource, HoverTool, Label from bokeh.plotting import figure @@ -56,25 +56,63 @@ # Colors for sectors colors = ["#306998", "#FFD43B", "#8B4513"] +# Store face center data for hover tooltips using ColumnDataSource +face_centers_x = [] +face_centers_y = [] +face_labels = [] +face_revenue = [] +face_margin = [] +face_satisfaction = [] +face_market_share = [] -def draw_chernoff_face(p, cx, cy, features, color, label, face_size=0.35): - """Draw a Chernoff face at position (cx, cy) using normalized features (0-1).""" - # features: [revenue_growth, profit_margin, customer_satisfaction, market_share] - # Mapping: - # - revenue_growth -> face width - # - profit_margin -> face height - # - customer_satisfaction -> eye size - # - market_share -> mouth curvature +# Create figure with 4x3 grid for 12 faces +p = figure( + width=4800, + height=2700, + title="chernoff-basic · bokeh · pyplots.ai", + x_range=(-0.1, 4.1), + y_range=(-0.2, 3.2), + tools="", +) - face_width = 0.3 + features[0] * 0.3 # 0.3 to 0.6 - face_height = 0.35 + features[1] * 0.25 # 0.35 to 0.6 - eye_size = 0.03 + features[2] * 0.05 # 0.03 to 0.08 - mouth_curve = features[3] # 0 to 1 (sad to happy) +# Style +p.title.text_font_size = "32pt" +p.title.align = "center" +p.xaxis.visible = False +p.yaxis.visible = False +p.xgrid.visible = False +p.ygrid.visible = False +p.outline_line_color = None +p.background_fill_color = "#FAFAFA" - # Scale by face_size - face_width *= face_size - face_height *= face_size - eye_size *= face_size +# Draw faces in a 4x3 grid (inline, no helper function) +face_size = 0.4 +for i, (features, sec_idx) in enumerate(zip(data_norm, sector_idx, strict=True)): + col = i % 4 + row = 2 - i // 4 # Start from top row + cx = col + 0.5 + cy = row + 0.5 + color = colors[sec_idx] + label_text = f"{sectors[sec_idx]} #{i % 4 + 1}" + + # Store data for hover tooltip + face_centers_x.append(cx) + face_centers_y.append(cy) + face_labels.append(label_text) + face_revenue.append(f"{data[i, 0] * 100:.1f}%") + face_margin.append(f"{data[i, 1] * 100:.1f}%") + face_satisfaction.append(f"{data[i, 2] * 100:.1f}%") + face_market_share.append(f"{data[i, 3] * 100:.1f}%") + + # Features mapping: + # - revenue_growth (features[0]) -> face width + # - profit_margin (features[1]) -> face height + # - customer_satisfaction (features[2]) -> eye size + # - market_share (features[3]) -> mouth curvature + face_width = (0.3 + features[0] * 0.3) * face_size + face_height = (0.35 + features[1] * 0.25) * face_size + eye_size = (0.03 + features[2] * 0.05) * face_size + mouth_curve = features[3] # Draw face outline (ellipse approximation using patches) theta = np.linspace(0, 2 * np.pi, 50) @@ -85,10 +123,10 @@ def draw_chernoff_face(p, cx, cy, features, color, label, face_size=0.35): # Draw eyes eye_spacing = face_width * 0.5 eye_y = cy + face_height * 0.25 + eye_theta = np.linspace(0, 2 * np.pi, 30) # Left eye left_eye_x = cx - eye_spacing - eye_theta = np.linspace(0, 2 * np.pi, 30) left_ex = left_eye_x + eye_size * np.cos(eye_theta) left_ey = eye_y + eye_size * np.sin(eye_theta) p.patch(left_ex, left_ey, fill_color="white", line_color="#333333", line_width=2) @@ -113,7 +151,7 @@ def draw_chernoff_face(p, cx, cy, features, color, label, face_size=0.35): # Draw eyebrows brow_y = eye_y + eye_size * 1.8 brow_width = eye_size * 1.2 - eyebrow_slant = (features[0] - 0.5) * 0.02 * face_size # Based on revenue_growth + eyebrow_slant = (features[0] - 0.5) * 0.02 * face_size p.line( [left_eye_x - brow_width, left_eye_x + brow_width], @@ -129,8 +167,7 @@ def draw_chernoff_face(p, cx, cy, features, color, label, face_size=0.35): ) # Draw nose - nose_length = 0.02 + features[1] * 0.03 # Based on profit_margin - nose_length *= face_size + nose_length = (0.02 + features[1] * 0.03) * face_size nose_y_top = cy + face_height * 0.1 nose_y_bottom = cy - face_height * 0.1 p.line([cx, cx], [nose_y_top, nose_y_bottom], line_color="#333333", line_width=2) @@ -145,51 +182,53 @@ def draw_chernoff_face(p, cx, cy, features, color, label, face_size=0.35): mouth_y = cy - face_height * 0.4 mouth_width = face_width * 0.5 mouth_x = np.linspace(cx - mouth_width, cx + mouth_width, 20) - # mouth_curve: 0=sad (curve down), 1=happy (curve up) curve_amount = (mouth_curve - 0.5) * 0.08 * face_size mouth_y_curve = mouth_y + curve_amount * (1 - ((mouth_x - cx) / mouth_width) ** 2) * 4 p.line(mouth_x, mouth_y_curve, line_color="#333333", line_width=3) # Add label below face label_obj = Label( - x=cx, y=cy - face_height - 0.1, text=label, text_align="center", text_font_size="20pt", text_color="#333333" + x=cx, + y=cy - face_height - 0.1, + text=label_text, + text_align="center", + text_font_size="20pt", + text_color="#333333", ) p.add_layout(label_obj) - -# Create figure with 4x3 grid for 12 faces -p = figure( - width=4800, - height=2700, - title="chernoff-basic \u00b7 bokeh \u00b7 pyplots.ai", - x_range=(-0.1, 4.1), - y_range=(-0.2, 3.2), - tools="", +# Create ColumnDataSource for hover tooltips (Bokeh-specific feature) +hover_source = ColumnDataSource( + data={ + "x": face_centers_x, + "y": face_centers_y, + "label": face_labels, + "revenue": face_revenue, + "margin": face_margin, + "satisfaction": face_satisfaction, + "market_share": face_market_share, + } ) -# Style -p.title.text_font_size = "32pt" -p.title.align = "center" -p.xaxis.visible = False -p.yaxis.visible = False -p.xgrid.visible = False -p.ygrid.visible = False -p.outline_line_color = None -p.background_fill_color = "#FAFAFA" - -# Draw faces in a 4x3 grid -for i, (features, sec_idx) in enumerate(zip(data_norm, sector_idx, strict=True)): - col = i % 4 - row = 2 - i // 4 # Start from top row - cx = col + 0.5 - cy = row + 0.5 - - label = f"{sectors[sec_idx]} #{i % 4 + 1}" - draw_chernoff_face(p, cx, cy, features, colors[sec_idx], label, face_size=0.4) +# Add invisible scatter for hover interaction +hover_renderer = p.scatter("x", "y", source=hover_source, size=80, fill_alpha=0, line_alpha=0) + +# Add HoverTool for interactivity (distinctive Bokeh feature) +hover_tool = HoverTool( + renderers=[hover_renderer], + tooltips=[ + ("Company", "@label"), + ("Revenue Growth", "@revenue"), + ("Profit Margin", "@margin"), + ("Satisfaction", "@satisfaction"), + ("Market Share", "@market_share"), + ], +) +p.add_tools(hover_tool) # Add legend manually using patches and labels (positioned below grid) legend_y_base = 2.95 -legend_x_positions = [0.5, 1.5, 2.5] # Spread horizontally +legend_x_positions = [0.5, 1.5, 2.5] for i, (name, color) in enumerate(zip(sectors, colors, strict=True)): lx_center = legend_x_positions[i] theta = np.linspace(0, 2 * np.pi, 30) @@ -201,13 +240,13 @@ def draw_chernoff_face(p, cx, cy, features, color, label, face_size=0.35): ) p.add_layout(legend_label) -# Add subtitle with feature mapping explanation +# Add subtitle with feature mapping explanation (increased font size) subtitle = Label( x=2.0, y=-0.02, text="Face width=Revenue Growth, Face height=Profit Margin, Eye size=Satisfaction, Mouth=Market Share", text_align="center", - text_font_size="18pt", + text_font_size="22pt", text_color="#666666", ) p.add_layout(subtitle) From 107833f207393dd304c2449916de3af59530690d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 31 Dec 2025 14:50:05 +0000 Subject: [PATCH 5/5] chore(bokeh): update quality score 91 and review feedback for chernoff-basic --- plots/chernoff-basic/implementations/bokeh.py | 4 +-- plots/chernoff-basic/metadata/bokeh.yaml | 26 ++++++++----------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/plots/chernoff-basic/implementations/bokeh.py b/plots/chernoff-basic/implementations/bokeh.py index 1d6fd9bddd..2e6285ba7a 100644 --- a/plots/chernoff-basic/implementations/bokeh.py +++ b/plots/chernoff-basic/implementations/bokeh.py @@ -1,7 +1,7 @@ -"""pyplots.ai +""" pyplots.ai chernoff-basic: Chernoff Faces for Multivariate Data Library: bokeh 3.8.1 | Python 3.13.11 -Quality: 88/100 | Created: 2025-12-31 +Quality: 91/100 | Created: 2025-12-31 """ import numpy as np diff --git a/plots/chernoff-basic/metadata/bokeh.yaml b/plots/chernoff-basic/metadata/bokeh.yaml index 36bb095d2e..9f71dc84fe 100644 --- a/plots/chernoff-basic/metadata/bokeh.yaml +++ b/plots/chernoff-basic/metadata/bokeh.yaml @@ -1,7 +1,7 @@ library: bokeh specification_id: chernoff-basic created: '2025-12-31T11:00:13Z' -updated: '2025-12-31T11:11:16Z' +updated: '2025-12-31T14:50:05Z' generated_by: claude-opus-4-5-20251101 workflow_run: 20617520899 issue: 3003 @@ -10,20 +10,16 @@ library_version: 3.8.1 preview_url: https://storage.googleapis.com/pyplots-images/plots/chernoff-basic/bokeh/plot.png preview_thumb: https://storage.googleapis.com/pyplots-images/plots/chernoff-basic/bokeh/plot_thumb.png preview_html: https://storage.googleapis.com/pyplots-images/plots/chernoff-basic/bokeh/plot.html -quality_score: 88 +quality_score: 91 review: strengths: - - Excellent implementation of Chernoff faces concept with clear visual differentiation - between sectors - - Smart use of realistic business context (Tech/Retail/Energy companies with meaningful - metrics) - - Clean grid layout that maximizes canvas utilization with balanced spacing - - Good color accessibility with distinct, colorblind-safe palette - - Helpful feature mapping subtitle that aids interpretation - - Proper normalization of data as specified + - Clear visual differentiation between sectors through color and facial expression + patterns + - Excellent business context with realistic company performance metrics + - Proper normalization of data as spec requires + - 'Good use of Bokeh-specific features: ColumnDataSource, HoverTool for interactivity' + - Feature mapping subtitle explains what each facial feature represents + - Well-organized 4x3 grid layout makes comparison easy weaknesses: - - Code uses a helper function which violates the KISS principle (should be inline - code) - - Could leverage more Bokeh-specific features like ColumnDataSource or hover tooltips - for interactivity - - Subtitle text at 18pt is slightly small for the 4800×2700 canvas - consider 20pt+ + - Legend positioned too far from the faces (at very top of plot) + - Faces could be slightly larger to better utilize the canvas space