|
1 | | -""" pyplots.ai |
| 1 | +""" anyplot.ai |
2 | 2 | bland-altman-basic: Bland-Altman Agreement Plot |
3 | | -Library: letsplot 4.8.2 | Python 3.13.11 |
4 | | -Quality: 92/100 | Created: 2025-12-25 |
| 3 | +Library: letsplot 4.9.0 | Python 3.13.13 |
| 4 | +Quality: 92/100 | Updated: 2026-05-07 |
5 | 5 | """ |
6 | 6 |
|
| 7 | +import os |
| 8 | + |
7 | 9 | import numpy as np |
8 | 10 | import pandas as pd |
9 | 11 | from lets_plot import * |
10 | 12 |
|
11 | 13 |
|
12 | 14 | LetsPlot.setup_html() |
13 | 15 |
|
| 16 | +# Theme tokens |
| 17 | +THEME = os.getenv("ANYPLOT_THEME", "light") |
| 18 | +PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17" |
| 19 | +ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420" |
| 20 | +INK = "#1A1A17" if THEME == "light" else "#F0EFE8" |
| 21 | +INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" |
| 22 | + |
| 23 | +# Okabe-Ito palette |
| 24 | +BRAND = "#009E73" # Position 1 - first categorical series |
| 25 | +SECONDARY = "#D55E00" # Position 2 |
| 26 | +ACCENT = "#0072B2" # Position 3 |
| 27 | + |
14 | 28 | # Data: Simulated blood pressure readings from two sphygmomanometers |
15 | 29 | np.random.seed(42) |
16 | 30 | n = 80 |
|
38 | 52 |
|
39 | 53 | # Annotation data - position labels at the left side with offset from line |
40 | 54 | annot_x = df["mean"].min() + 2 |
41 | | -y_offset = 0.8 # Offset so labels don't overlap lines |
| 55 | +y_offset = 0.8 |
42 | 56 | annot_df = pd.DataFrame( |
43 | 57 | { |
44 | 58 | "x": [annot_x, annot_x, annot_x], |
|
48 | 62 | f"+1.96 SD: {upper_loa:.2f} mmHg", |
49 | 63 | f"-1.96 SD: {lower_loa:.2f} mmHg", |
50 | 64 | ], |
51 | | - "color": ["bias", "loa", "loa"], |
| 65 | + "line_type": ["bias", "loa", "loa"], |
52 | 66 | } |
53 | 67 | ) |
54 | 68 |
|
55 | | -# Build plot |
| 69 | +# Build plot with theme-adaptive styling |
56 | 70 | plot = ( |
57 | 71 | ggplot() |
58 | | - # Scatter points |
59 | | - + geom_point(aes(x="mean", y="diff"), data=df, color="#306998", size=5, alpha=0.7) |
60 | | - # Mean difference line (bias) |
61 | | - + geom_hline(yintercept=mean_diff, color="#16a34a", size=1.5) |
62 | | - # Upper limit of agreement |
63 | | - + geom_hline(yintercept=upper_loa, color="#dc2626", size=1.2, linetype="dashed") |
64 | | - # Lower limit of agreement |
65 | | - + geom_hline(yintercept=lower_loa, color="#dc2626", size=1.2, linetype="dashed") |
66 | | - # Annotations using geom_label for better visibility |
| 72 | + + geom_point(aes(x="mean", y="diff"), data=df, color=BRAND, size=6, alpha=0.7, stroke=0.5) |
| 73 | + + geom_hline(yintercept=mean_diff, color=BRAND, size=1.5) |
| 74 | + + geom_hline(yintercept=upper_loa, color=SECONDARY, size=1.2, linetype="dashed") |
| 75 | + + geom_hline(yintercept=lower_loa, color=SECONDARY, size=1.2, linetype="dashed") |
67 | 76 | + geom_label( |
68 | 77 | aes(x="x", y="y", label="label"), |
69 | | - data=annot_df[annot_df["color"] == "bias"], |
| 78 | + data=annot_df[annot_df["line_type"] == "bias"], |
70 | 79 | size=12, |
71 | | - color="#16a34a", |
72 | | - fill="white", |
| 80 | + color=BRAND, |
| 81 | + fill=ELEVATED_BG, |
73 | 82 | hjust=0, |
74 | 83 | label_padding=0.3, |
75 | 84 | ) |
76 | 85 | + geom_label( |
77 | 86 | aes(x="x", y="y", label="label"), |
78 | | - data=annot_df[annot_df["color"] == "loa"], |
| 87 | + data=annot_df[annot_df["line_type"] == "loa"], |
79 | 88 | size=12, |
80 | | - color="#dc2626", |
81 | | - fill="white", |
| 89 | + color=SECONDARY, |
| 90 | + fill=ELEVATED_BG, |
82 | 91 | hjust=0, |
83 | 92 | label_padding=0.3, |
84 | 93 | ) |
85 | | - # Labels and title |
86 | 94 | + labs( |
87 | 95 | x="Mean of Two Methods (mmHg)", |
88 | 96 | y="Difference (Method 1 - Method 2) (mmHg)", |
89 | | - title="bland-altman-basic · letsplot · pyplots.ai", |
| 97 | + title="bland-altman-basic · letsplot · anyplot.ai", |
90 | 98 | ) |
91 | | - # Size and theme |
92 | 99 | + ggsize(1600, 900) |
93 | 100 | + theme_minimal() |
94 | 101 | + theme( |
95 | | - plot_title=element_text(size=24), |
96 | | - axis_title=element_text(size=20), |
97 | | - axis_text=element_text(size=16), |
98 | | - panel_grid_major=element_line(color="#e5e5e5", size=0.5), |
| 102 | + plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG), |
| 103 | + panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG), |
| 104 | + panel_grid_major=element_line(color=INK, size=0.3, linetype="solid"), |
99 | 105 | panel_grid_minor=element_blank(), |
| 106 | + plot_title=element_text(size=24, color=INK), |
| 107 | + axis_title=element_text(size=20, color=INK), |
| 108 | + axis_text=element_text(size=16, color=INK_SOFT), |
| 109 | + axis_line=element_line(color=INK_SOFT, size=0.5), |
100 | 110 | ) |
101 | 111 | ) |
102 | 112 |
|
103 | | -# Save PNG (scale 3x for 4800x2700) |
104 | | -ggsave(plot, "plot.png", scale=3, path=".") |
105 | | - |
106 | | -# Save HTML for interactive version |
107 | | -ggsave(plot, "plot.html", path=".") |
| 113 | +# Save PNG and HTML with theme-suffixed filenames |
| 114 | +ggsave(plot, f"plot-{THEME}.png", scale=3, path=".") |
| 115 | +ggsave(plot, f"plot-{THEME}.html", path=".") |
0 commit comments