Marcel0123 commited on
Commit
14d5b46
·
verified ·
1 Parent(s): c4cdfd9

Upload 2 files

Browse files
Files changed (1) hide show
  1. app.py +93 -86
app.py CHANGED
@@ -7,68 +7,109 @@ from sklearn import datasets
7
  from sklearn.preprocessing import StandardScaler
8
  from sklearn.decomposition import PCA
9
 
10
- # ------------------------------
11
- # Data loading
12
- # ------------------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  def load_diabetes_df():
14
  d = datasets.load_diabetes()
15
- X = pd.DataFrame(d.data, columns=d.feature_names)
16
  y = pd.Series(d.target, name="target")
17
- # Voeg target erbij voor mogelijke kleurselecties, al is default BMI
18
  df = X.copy()
19
  df["target"] = y
20
  return df
21
 
22
- # ------------------------------
23
- # PCA computation + visuals
24
- # ------------------------------
25
- def pca_biplot(color_feature="bmi", arrow_scale=2.0, point_size=32, alpha=0.85, n_components=10, standardize=True):
26
  df = load_diabetes_df()
27
- feats = [c for c in df.columns if c != "target"]
28
- X = df[feats].values
 
 
 
29
 
30
- # Standardize (diabetes is al ongeveer gestandaardiseerd, maar we doen dit expliciet voor duidelijkheid)
31
  if standardize:
32
  scaler = StandardScaler(with_mean=True, with_std=True)
33
  Xs = scaler.fit_transform(X)
34
  else:
35
  Xs = X
36
 
37
- # PCA
38
- pca = PCA(n_components=min(n_components, Xs.shape[1]))
39
  Z = pca.fit_transform(Xs) # scores
40
- loadings = pca.components_.T # shape (features, components)
41
  expl = pca.explained_variance_ratio_
42
 
43
- # Kleur op geselecteerde feature
44
- if color_feature not in df.columns:
45
- color_feature = "bmi"
46
- cvals = df[color_feature].values
47
 
48
- # ---------------- Plot 1: PCA biplot (scores + feature vectors) ----------------
49
- fig1 = plt.figure(figsize=(7.5, 5.5))
50
  ax = fig1.add_subplot(111)
51
  sc = ax.scatter(Z[:, 0], Z[:, 1], c=cvals, s=point_size, alpha=alpha)
52
  cbar = plt.colorbar(sc, ax=ax, pad=0.02)
53
- cbar.set_label(f"Kleur: {color_feature}")
54
  ax.set_xlabel("PC1")
55
  ax.set_ylabel("PC2")
56
- ax.set_title("PCA biplot — punten (projectie) + pijlen (feature-bijdragen)")
57
 
58
- # pijlen voor feature loadings (alleen PC1/PC2)
59
- for i, feat in enumerate(feats):
60
  x_arrow = loadings[i, 0] * arrow_scale
61
  y_arrow = loadings[i, 1] * arrow_scale
62
  ax.arrow(0, 0, x_arrow, y_arrow, head_width=0.05, head_length=0.08, fc="k", ec="k", length_includes_head=True)
63
- ax.text(x_arrow * 1.08, y_arrow * 1.08, feat, fontsize=9, ha="center", va="center")
64
 
65
  ax.axhline(0, color="grey", linewidth=0.6, linestyle=":")
66
  ax.axvline(0, color="grey", linewidth=0.6, linestyle=":")
67
  ax.grid(True, linestyle=":", linewidth=0.6)
68
  plt.tight_layout()
69
 
70
- # ---------------- Plot 2: Explained variance (bar + cumulative line) ----------------
71
- fig2 = plt.figure(figsize=(7.5, 3.8))
72
  ax2 = fig2.add_subplot(111)
73
  xs = np.arange(1, len(expl) + 1)
74
  ax2.bar(xs, expl, width=0.8, align="center")
@@ -80,40 +121,36 @@ def pca_biplot(color_feature="bmi", arrow_scale=2.0, point_size=32, alpha=0.85,
80
  ax2.grid(True, linestyle=":", linewidth=0.6)
81
  plt.tight_layout()
82
 
83
- # ---------------- Tabel: top-features per PC1 en PC2 ----------------
84
  load_df = pd.DataFrame({
85
- "feature": feats,
86
  "PC1_loading": loadings[:, 0],
87
  "PC2_loading": loadings[:, 1],
88
  "PC1_abs": np.abs(loadings[:, 0]),
89
  "PC2_abs": np.abs(loadings[:, 1]),
90
  })
91
- # sorteer per component en merge een compacte weergave
92
- top_pc1 = load_df.sort_values("PC1_abs", ascending=False)[["feature", "PC1_loading"]].head(6).reset_index(drop=True)
93
- top_pc2 = load_df.sort_values("PC2_abs", ascending=False)[["feature", "PC2_loading"]].head(6).reset_index(drop=True)
94
 
95
- top_pc1.rename(columns={"feature": "Feature (PC1)", "PC1_loading": "Loading PC1"}, inplace=True)
96
- top_pc2.rename(columns={"feature": "Feature (PC2)", "PC2_loading": "Loading PC2"}, inplace=True)
97
 
98
- # Combineer netjes naast elkaar
99
  max_len = max(len(top_pc1), len(top_pc2))
100
  top_pc1 = top_pc1.reindex(range(max_len))
101
  top_pc2 = top_pc2.reindex(range(max_len))
102
  table = pd.concat([top_pc1, top_pc2], axis=1)
103
 
104
- # Beschrijving in gewone taal
 
105
  summary_md = f"""### Wat zie je hier?
106
- - **Punten (personen)** geprojecteerd in 2D met **PCA**. Dicht bij elkaar = **lijkt op elkaar** over meerdere metingen.
107
- - **Kleur** = waarde van **{color_feature}** (bijv. BMI). Zo zie je meteen of die eigenschap een **gradiënt** vormt.
108
- - **Pijlen** = bijdrage van **features** aan de richting van **PC1/PC2**. Lengte hoe sterk die feature die richting beïnvloedt.
109
  - **Balkgrafiek** = per component hoeveel variatie hij uitlegt; **lijn** = cumulatief.
110
 
111
  ### Hoe lees je de biplot?
112
- - Staat een pijl **rechts/boven**, dan drukt die feature de data die kant op in PC1/PC2.
113
- - Punten in de richting van een pijl hebben vaak **hogere waarden** voor die feature.
114
- - Kleurgradiënt (bijv. BMI): als kleuren geleidelijk veranderen langs een as, is dat **consistentie** met die component.
115
-
116
- > Tip: verander **pijl-schaal**, **puntgrootte** en **transparantie** om het patroon beter te zien.
117
  """
118
 
119
  return fig1, fig2, table, summary_md
@@ -121,63 +158,33 @@ def pca_biplot(color_feature="bmi", arrow_scale=2.0, point_size=32, alpha=0.85,
121
  # ------------------------------
122
  # UI
123
  # ------------------------------
124
- STORY_MD_MEDICAL = r'''
125
- ### Wat is dit en waarom is het belangrijk?
126
-
127
- Artsen en onderzoekers meten vaak heel veel dingen van patiënten: **BMI, bloeddruk, bloedwaarden, cholesterol, suikers** enzovoort.
128
- Al die getallen tegelijk vergelijken is bijna onmogelijk.
129
-
130
- Met deze techniek (**PCA-biplot**) maak je er een **kaart** van:
131
- - Elk **punt** = één patiënt.
132
- - Patiënten die **op elkaar lijken** in hun metingen liggen dicht bij elkaar.
133
- - **Kleuren** (bijv. BMI) laten zien of een eigenschap een duidelijke **trend** of **gradiënt** vormt.
134
- - **Pijlen** tonen welke metingen het meest bijdragen aan de richting van de kaart.
135
-
136
- > Belangrijk: dit voorbeeld gebruikt een **echte medische dataset** (de `diabetes` dataset uit de scikit-learn bibliotheek).
137
- > De gegevens zijn afkomstig uit een onderzoek met **echte mensen**, maar zijn **geanonimiseerd** en uitsluitend bedoeld voor educatie en analyse.
138
-
139
- ---
140
-
141
- ### Wat levert dit op in de medische wetenschap?
142
- - Ziekenhuizen kunnen **patronen ontdekken** in grote hoeveelheden patiëntgegevens.
143
- - Je ziet of **bepaalde metingen samenhangen** met ziektebeelden of risico’s (bijvoorbeeld of BMI samen oploopt met bepaalde bloedwaarden).
144
- - Onderzoekers krijgen zo **hypotheses**: *“Deze groep patiënten lijkt op elkaar — misschien hebben ze een vergelijkbaar risico of dezelfde behandeling nodig.”*
145
- - Het helpt ook om **uitbijters** te vinden: patiënten die heel anders scoren dan de rest, wat een signaal kan zijn om nader te onderzoeken.
146
-
147
- ---
148
-
149
- ### Simpel gezegd
150
- Dit is een manier om **veel medische informatie overzichtelijk te maken**.
151
- Artsen kunnen sneller zien **welke metingen ertoe doen** en **waar mogelijk verbanden zitten**.
152
- Dat maakt onderzoek en diagnose een stuk slimmer en efficiënter.
153
- '''
154
-
155
- with gr.Blocks(title="PCA Biplot — Diabetes (kleur: BMI)") as demo:
156
- gr.Markdown("# PCA Biplot — Diabetes (kleur: BMI)")
157
- gr.Markdown("""In deze demo zie je **live** hoe PCA de data samenvat. De punten zijn personen; pijlen laten zien welke features
158
- (zoals **bmi**, **bp**, **s1..s6**) de richting van de componenten bepalen. De **kleur** toont standaard **bmi**.
159
  """)
160
 
161
  with gr.Row():
162
  with gr.Column(scale=1):
 
163
  color_feat = gr.Dropdown(
164
- choices=["bmi", "bp", "s1", "s2", "s3", "s4", "s5", "s6", "age", "sex", "target"],
165
- value="bmi",
166
- label="Kleur op feature"
167
  )
168
  arrow_scale = gr.Slider(0.5, 5.0, value=2.0, step=0.1, label="Pijl-schaal (loadings)")
169
  point_size = gr.Slider(8, 80, value=32, step=2, label="Puntgrootte")
170
  alpha = gr.Slider(0.2, 1.0, value=0.85, step=0.05, label="Transparantie (punten)")
171
  n_components = gr.Slider(2, 10, value=10, step=1, label="Aantal PCA-componenten (voor variatieplot)")
172
- standardize = gr.Checkbox(value=True, label="Standaardiseer features (aanbevolen)")
173
  run_btn = gr.Button("Update visualisaties")
174
  gr.Markdown("> *Medisch nut:* deze biplot maakt grote hoeveelheden patiëntmetingen direct inzichtelijk.")
175
  with gr.Column(scale=2):
176
- plot_biplot = gr.Plot(label="PCA biplot — punten + pijlen")
177
  plot_expl = gr.Plot(label="Uitlegvariantie per component")
178
  table = gr.Dataframe(headers=["Feature (PC1)", "Loading PC1", "Feature (PC2)", "Loading PC2"], row_count=6)
179
  summary = gr.Markdown()
180
- medical_md_block = gr.Markdown(STORY_MD_MEDICAL)
181
 
182
  inputs = [color_feat, arrow_scale, point_size, alpha, n_components, standardize]
183
 
 
7
  from sklearn.preprocessing import StandardScaler
8
  from sklearn.decomposition import PCA
9
 
10
+ # Mapping van sklearn-featurecodes naar begrijpelijke NL labels
11
+ FEATURE_LABELS = {
12
+ "age": "Leeftijd",
13
+ "sex": "Geslacht",
14
+ "bmi": "BMI (Body Mass Index)",
15
+ "bp": "Bloeddruk",
16
+ "s1": "Totale cholesterol",
17
+ "s2": "LDL-cholesterol",
18
+ "s3": "HDL-cholesterol",
19
+ "s4": "Chol./HDL-verhouding",
20
+ "s5": "Triglyceriden",
21
+ "s6": "Bloedsuiker (glucose)",
22
+ "target": "Doelscore (progressie)",
23
+ }
24
+
25
+ # Omgekeerde mapping voor dropdown-keuze -> kolomnaam
26
+ LABEL_TO_KEY = {v: k for k, v in FEATURE_LABELS.items()}
27
+
28
+ MEDICAL_MD = r"""
29
+ ### Wat is dit en waarom is het belangrijk?
30
+
31
+ Artsen en onderzoekers meten vaak heel veel dingen van patiënten: **BMI, bloeddruk, bloedwaarden, cholesterol, suikers** enzovoort.
32
+ Al die getallen tegelijk vergelijken is bijna onmogelijk.
33
+
34
+ Met deze techniek (**PCA-biplot**) maak je er een **kaart** van:
35
+ - Elk **punt** = één patiënt.
36
+ - Patiënten die **op elkaar lijken** in hun metingen liggen dicht bij elkaar.
37
+ - **Kleuren** (bijv. BMI) laten zien of een eigenschap een duidelijke **trend** of **gradiënt** vormt.
38
+ - **Pijlen** tonen welke metingen het meest bijdragen aan de richting van de kaart.
39
+
40
+ > Belangrijk: dit voorbeeld gebruikt een **echte medische dataset** (de `diabetes` dataset uit de scikit-learn bibliotheek).
41
+ > De gegevens zijn afkomstig uit een onderzoek met **echte mensen**, maar zijn **geanonimiseerd** en uitsluitend bedoeld voor educatie en analyse.
42
+
43
+ ---
44
+
45
+ ### Wat levert dit op in de medische wetenschap?
46
+ - Ziekenhuizen kunnen **patronen ontdekken** in grote hoeveelheden patiëntgegevens.
47
+ - Je ziet of **bepaalde metingen samenhangen** met ziektebeelden of risico’s (bijvoorbeeld of BMI samen oploopt met bepaalde bloedwaarden).
48
+ - Onderzoekers krijgen zo **hypotheses**: *“Deze groep patiënten lijkt op elkaar — misschien hebben ze een vergelijkbaar risico of dezelfde behandeling nodig.”*
49
+ - Het helpt ook om **uitbijters** te vinden: patiënten die heel anders scoren dan de rest, wat een signaal kan zijn om nader te onderzoeken.
50
+
51
+ ---
52
+
53
+ ### Simpel gezegd
54
+ Dit is een manier om **veel medische informatie overzichtelijk te maken**.
55
+ Artsen kunnen sneller zien **welke metingen ertoe doen** en **waar mogelijk verbanden zitten**.
56
+ Dat maakt onderzoek en diagnose een stuk slimmer en efficiënter.
57
+ """
58
+
59
  def load_diabetes_df():
60
  d = datasets.load_diabetes()
61
+ X = pd.DataFrame(d.data, columns=d.feature_names) # ['age','sex','bmi','bp','s1'..'s6']
62
  y = pd.Series(d.target, name="target")
 
63
  df = X.copy()
64
  df["target"] = y
65
  return df
66
 
67
+ def pca_biplot(color_label="BMI (Body Mass Index)", arrow_scale=2.0, point_size=32, alpha=0.85, n_components=10, standardize=True):
 
 
 
68
  df = load_diabetes_df()
69
+ feature_keys = [c for c in df.columns if c != "target"] # codes
70
+ # Kleur-feature van label -> key
71
+ color_key = LABEL_TO_KEY.get(color_label, "bmi")
72
+
73
+ X = df[feature_keys].values
74
 
 
75
  if standardize:
76
  scaler = StandardScaler(with_mean=True, with_std=True)
77
  Xs = scaler.fit_transform(X)
78
  else:
79
  Xs = X
80
 
81
+ pca = PCA(n_components=min(int(n_components), Xs.shape[1]))
 
82
  Z = pca.fit_transform(Xs) # scores
83
+ loadings = pca.components_.T # (features, components)
84
  expl = pca.explained_variance_ratio_
85
 
86
+ # kleurwaarden
87
+ cvals = df[color_key].values
 
 
88
 
89
+ # ---------------- Plot 1: Biplot ----------------
90
+ fig1 = plt.figure(figsize=(7.8, 5.6))
91
  ax = fig1.add_subplot(111)
92
  sc = ax.scatter(Z[:, 0], Z[:, 1], c=cvals, s=point_size, alpha=alpha)
93
  cbar = plt.colorbar(sc, ax=ax, pad=0.02)
94
+ cbar.set_label(f"Kleur: {FEATURE_LABELS.get(color_key, color_key)}")
95
  ax.set_xlabel("PC1")
96
  ax.set_ylabel("PC2")
97
+ ax.set_title("PCA-biplot — punten (patiënten) + pijlen (belangrijkste metingen)")
98
 
99
+ # pijlen + labels in NL
100
+ for i, key in enumerate(feature_keys):
101
  x_arrow = loadings[i, 0] * arrow_scale
102
  y_arrow = loadings[i, 1] * arrow_scale
103
  ax.arrow(0, 0, x_arrow, y_arrow, head_width=0.05, head_length=0.08, fc="k", ec="k", length_includes_head=True)
104
+ ax.text(x_arrow * 1.08, y_arrow * 1.08, FEATURE_LABELS.get(key, key), fontsize=9, ha="center", va="center")
105
 
106
  ax.axhline(0, color="grey", linewidth=0.6, linestyle=":")
107
  ax.axvline(0, color="grey", linewidth=0.6, linestyle=":")
108
  ax.grid(True, linestyle=":", linewidth=0.6)
109
  plt.tight_layout()
110
 
111
+ # ---------------- Plot 2: Explained variance ----------------
112
+ fig2 = plt.figure(figsize=(7.8, 3.8))
113
  ax2 = fig2.add_subplot(111)
114
  xs = np.arange(1, len(expl) + 1)
115
  ax2.bar(xs, expl, width=0.8, align="center")
 
121
  ax2.grid(True, linestyle=":", linewidth=0.6)
122
  plt.tight_layout()
123
 
124
+ # ---------------- Tabel: top-features per PC1/PC2 (NL labels) ----------------
125
  load_df = pd.DataFrame({
126
+ "feature_key": feature_keys,
127
  "PC1_loading": loadings[:, 0],
128
  "PC2_loading": loadings[:, 1],
129
  "PC1_abs": np.abs(loadings[:, 0]),
130
  "PC2_abs": np.abs(loadings[:, 1]),
131
  })
132
+ load_df["Feature (PC1)"] = load_df["feature_key"].map(lambda k: FEATURE_LABELS.get(k, k))
133
+ load_df["Feature (PC2)"] = load_df["feature_key"].map(lambda k: FEATURE_LABELS.get(k, k))
 
134
 
135
+ top_pc1 = load_df.sort_values("PC1_abs", ascending=False)[["Feature (PC1)", "PC1_loading"]].head(6).reset_index(drop=True)
136
+ top_pc2 = load_df.sort_values("PC2_abs", ascending=False)[["Feature (PC2)", "PC2_loading"]].head(6).reset_index(drop=True)
137
 
 
138
  max_len = max(len(top_pc1), len(top_pc2))
139
  top_pc1 = top_pc1.reindex(range(max_len))
140
  top_pc2 = top_pc2.reindex(range(max_len))
141
  table = pd.concat([top_pc1, top_pc2], axis=1)
142
 
143
+ # Uitlegtekst bijwerken met NL-label van kleur
144
+ color_label_nl = FEATURE_LABELS.get(color_key, color_key)
145
  summary_md = f"""### Wat zie je hier?
146
+ - **Punten (patiënten)** geprojecteerd in 2D met **PCA**. Dicht bij elkaar = **lijken op elkaar** over meerdere metingen.
147
+ - **Kleur** = **{color_label_nl}**. Zo zie je meteen of deze eigenschap een **gradiënt** vormt.
148
+ - **Pijlen** = bijdrage van **metingen** aan de richting van **PC1/PC2**. **Langere pijlen** wegen zwaarder.
149
  - **Balkgrafiek** = per component hoeveel variatie hij uitlegt; **lijn** = cumulatief.
150
 
151
  ### Hoe lees je de biplot?
152
+ - Staat een pijl **rechts/boven**, dan duwt die meting de punten die kant op in PC1/PC2.
153
+ - Liggen punten **in de richting** van een pijl? Dan hebben die patiënten gemiddeld **hogere waarden** voor die meting.
 
 
 
154
  """
155
 
156
  return fig1, fig2, table, summary_md
 
158
  # ------------------------------
159
  # UI
160
  # ------------------------------
161
+ with gr.Blocks(title="PCA Biplot — Diabetes (NL labels)") as demo:
162
+ gr.Markdown("# PCA Biplot Diabetes (NL labels)")
163
+ gr.Markdown("""In deze demo zie je **live** hoe PCA de data samenvat. De punten zijn patiënten; pijlen laten zien welke metingen
164
+ (zoals **BMI**, **Bloeddruk**, **Cholesterol**, **Glucose**) de richting van de componenten bepalen. De **kleur** toont standaard **BMI**.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  """)
166
 
167
  with gr.Row():
168
  with gr.Column(scale=1):
169
+ color_choices = [FEATURE_LABELS[k] for k in ["bmi","bp","s1","s2","s3","s4","s5","s6","age","sex","target"]]
170
  color_feat = gr.Dropdown(
171
+ choices=color_choices,
172
+ value=FEATURE_LABELS["bmi"],
173
+ label="Kleur op meting"
174
  )
175
  arrow_scale = gr.Slider(0.5, 5.0, value=2.0, step=0.1, label="Pijl-schaal (loadings)")
176
  point_size = gr.Slider(8, 80, value=32, step=2, label="Puntgrootte")
177
  alpha = gr.Slider(0.2, 1.0, value=0.85, step=0.05, label="Transparantie (punten)")
178
  n_components = gr.Slider(2, 10, value=10, step=1, label="Aantal PCA-componenten (voor variatieplot)")
179
+ standardize = gr.Checkbox(value=True, label="Standaardiseer metingen (aanbevolen)")
180
  run_btn = gr.Button("Update visualisaties")
181
  gr.Markdown("> *Medisch nut:* deze biplot maakt grote hoeveelheden patiëntmetingen direct inzichtelijk.")
182
  with gr.Column(scale=2):
183
+ plot_biplot = gr.Plot(label="PCA-biplot — punten + pijlen")
184
  plot_expl = gr.Plot(label="Uitlegvariantie per component")
185
  table = gr.Dataframe(headers=["Feature (PC1)", "Loading PC1", "Feature (PC2)", "Loading PC2"], row_count=6)
186
  summary = gr.Markdown()
187
+ medical_md_block = gr.Markdown(MEDICAL_MD)
188
 
189
  inputs = [color_feat, arrow_scale, point_size, alpha, n_components, standardize]
190