|
| 1 | +""" pyplots.ai |
| 2 | +point-basic: Point Estimate Plot |
| 3 | +Library: pygal 3.1.0 | Python 3.13.11 |
| 4 | +Quality: 91/100 | Created: 2025-12-30 |
| 5 | +""" |
| 6 | + |
| 7 | +import pygal |
| 8 | +from pygal.style import Style |
| 9 | + |
| 10 | + |
| 11 | +# Data: Treatment effects with 95% confidence intervals |
| 12 | +categories = ["Treatment A", "Treatment B", "Treatment C", "Treatment D", "Control"] |
| 13 | +estimates = [2.4, 1.8, 3.2, 0.9, 0.0] |
| 14 | +lower_bounds = [1.6, 0.9, 2.5, -0.2, -0.5] |
| 15 | +upper_bounds = [3.2, 2.7, 3.9, 2.0, 0.5] |
| 16 | + |
| 17 | +# Custom style for 4800x2700 canvas |
| 18 | +ci_color = "#306998" |
| 19 | +point_color = "#FFD43B" |
| 20 | +ref_color = "#888888" |
| 21 | + |
| 22 | +custom_style = Style( |
| 23 | + background="white", |
| 24 | + plot_background="white", |
| 25 | + foreground="#333333", |
| 26 | + foreground_strong="#333333", |
| 27 | + foreground_subtle="#999999", |
| 28 | + colors=(ref_color, ci_color, ci_color, ci_color, ci_color, ci_color, point_color), |
| 29 | + title_font_size=72, |
| 30 | + label_font_size=48, |
| 31 | + major_label_font_size=42, |
| 32 | + legend_font_size=42, |
| 33 | + value_font_size=36, |
| 34 | + stroke_width=6, |
| 35 | + guide_stroke_color="#e0e0e0", |
| 36 | +) |
| 37 | + |
| 38 | +# Create XY chart for point estimates with confidence intervals |
| 39 | +chart = pygal.XY( |
| 40 | + width=4800, |
| 41 | + height=2700, |
| 42 | + style=custom_style, |
| 43 | + title="point-basic · pygal · pyplots.ai", |
| 44 | + x_title="Effect Size", |
| 45 | + show_legend=True, |
| 46 | + legend_at_bottom=True, |
| 47 | + legend_at_bottom_columns=3, |
| 48 | + show_y_guides=True, |
| 49 | + show_x_guides=True, |
| 50 | + dots_size=30, |
| 51 | + stroke=False, |
| 52 | + margin_left=120, |
| 53 | + margin_right=120, |
| 54 | + margin_top=150, |
| 55 | + margin_bottom=180, |
| 56 | + xrange=(-1.5, 4.5), |
| 57 | + range=(0, 6), |
| 58 | +) |
| 59 | + |
| 60 | +# Map categories to y-values (numeric) - reversed for top-to-bottom display |
| 61 | +y_positions = list(range(len(categories), 0, -1)) |
| 62 | + |
| 63 | +# Add reference line at zero first (so it appears behind other elements) |
| 64 | +ref_line = [(0, 0.3), (0, 5.7)] |
| 65 | +chart.add("Reference (x=0)", ref_line, stroke=True, show_dots=False, stroke_width=3) |
| 66 | + |
| 67 | +# Add each CI as a separate series (to avoid connecting lines) |
| 68 | +for i, (low, high, y) in enumerate(zip(lower_bounds, upper_bounds, y_positions, strict=True)): |
| 69 | + ci_data = [(low, y), (high, y)] |
| 70 | + # First CI gets label, others are hidden from legend using None |
| 71 | + if i == 0: |
| 72 | + chart.add("95% CI", ci_data, stroke=True, show_dots=False, stroke_width=8) |
| 73 | + else: |
| 74 | + chart.add(None, ci_data, stroke=True, show_dots=False, stroke_width=8) |
| 75 | + |
| 76 | +# Add point estimates (on top of CI lines) |
| 77 | +point_data = [(est, y) for est, y in zip(estimates, y_positions, strict=True)] |
| 78 | +chart.add("Point Estimate", point_data, dots_size=32, stroke=False) |
| 79 | + |
| 80 | +# Custom y-axis labels with category names - use major labels |
| 81 | +chart.y_labels_major = [5, 4, 3, 2, 1] |
| 82 | +chart.y_labels = [ |
| 83 | + {"value": 5, "label": "Treatment A"}, |
| 84 | + {"value": 4, "label": "Treatment B"}, |
| 85 | + {"value": 3, "label": "Treatment C"}, |
| 86 | + {"value": 2, "label": "Treatment D"}, |
| 87 | + {"value": 1, "label": "Control"}, |
| 88 | +] |
| 89 | + |
| 90 | +# Save as PNG and HTML |
| 91 | +chart.render_to_png("plot.png") |
| 92 | +chart.render_to_file("plot.html") |
0 commit comments