josefchen commited on
Commit
f0e4319
·
verified ·
1 Parent(s): d29e3ae

Revert to light theme with mint accent only. White plot backgrounds. Keep sibling cards visible.

Browse files
Files changed (1) hide show
  1. app.py +74 -135
app.py CHANGED
@@ -34,16 +34,16 @@ KAIKAKU_MINT_BRIGHT = "#D8F0E5"
34
  KAIKAKU_TEXT = "#E8F4F1"
35
  KAIKAKU_MUTED = "#7AA8A2"
36
 
37
- # Plotly default for dark mode
38
  plt.rcParams.update({
39
- "figure.facecolor": KAIKAKU_DARK,
40
- "axes.facecolor": KAIKAKU_DARK,
41
- "axes.edgecolor": KAIKAKU_EDGE,
42
- "axes.labelcolor": KAIKAKU_TEXT,
43
- "xtick.color": KAIKAKU_TEXT,
44
- "ytick.color": KAIKAKU_TEXT,
45
- "text.color": KAIKAKU_TEXT,
46
- "savefig.facecolor": KAIKAKU_DARK,
47
  })
48
 
49
  MODELS = {
@@ -60,14 +60,14 @@ NAMES_BY_IDX: list[str] = _lab["names"]
60
  FOOD_GROUPS: list[str] = _lab["food_groups"]
61
 
62
  FG_COLORS = {
63
- "Vegetable": "#9BD7A8",
64
- "Fruit": "#F0A8C8",
65
- "Grain": "#E8D67A",
66
- "Dairy": "#9BCFE8",
67
- "Spice": "#F08A7A",
68
- "Pantry": "#E8B47A",
69
- "Beverage": "#B59CE8",
70
- "Other": "#5A7878",
71
  }
72
 
73
  # Sanity-check log on import so Space logs show whether assets loaded
@@ -127,11 +127,9 @@ def _basket_heatmap(m, basket):
127
  fig, ax = plt.subplots(figsize=(6, 5))
128
  if len(valid) < 2:
129
  ax.text(0.5, 0.5, "Add 2+ ingredients to see pairwise cosines",
130
- ha="center", va="center", fontsize=13, color=KAIKAKU_MUTED,
131
  transform=ax.transAxes)
132
- ax.set_facecolor(KAIKAKU_DARK)
133
  ax.axis("off")
134
- fig.patch.set_facecolor(KAIKAKU_DARK)
135
  plt.tight_layout()
136
  return fig
137
  idxs = [m.vocab[n] for n in valid]
@@ -140,20 +138,16 @@ def _basket_heatmap(m, basket):
140
  im = ax.imshow(sim, cmap="viridis", vmin=-0.2, vmax=1.0, aspect="auto")
141
  ax.set_xticks(range(len(valid)))
142
  ax.set_yticks(range(len(valid)))
143
- ax.set_xticklabels(valid, rotation=35, ha="right", color=KAIKAKU_TEXT)
144
- ax.set_yticklabels(valid, color=KAIKAKU_TEXT)
145
  for i in range(len(valid)):
146
  for j in range(len(valid)):
147
  v = float(sim[i, j])
148
  color = "white" if v < 0.55 else "black"
149
  ax.text(j, i, f"{v:.2f}", ha="center", va="center", fontsize=10, color=color)
150
  cb = plt.colorbar(im, ax=ax)
151
- cb.ax.yaxis.set_tick_params(color=KAIKAKU_TEXT)
152
- plt.setp(plt.getp(cb.ax.axes, "yticklabels"), color=KAIKAKU_TEXT)
153
- cb.set_label("cosine", color=KAIKAKU_TEXT)
154
- ax.set_title("Pairwise cosine within the basket", color=KAIKAKU_TEXT, fontsize=12)
155
- ax.set_facecolor(KAIKAKU_DARK)
156
- fig.patch.set_facecolor(KAIKAKU_DARK)
157
  plt.tight_layout()
158
  return fig
159
 
@@ -215,35 +209,26 @@ def umap_view(sibling, basket, show_neighbours, k, three_d=False):
215
  showlegend=False,
216
  ))
217
 
218
- # Neighbour highlights (orange-ish mint glow)
219
  if neighbour_set:
220
  ni = [i for i in range(n) if NAMES_BY_IDX[i] in neighbour_set]
221
  nx = [float(coords2[i, 0]) for i in ni]
222
  ny = [float(coords2[i, 1]) for i in ni]
223
  nz = [float(z[i]) for i in ni] if three_d else None
224
  nlabels = [NAMES_BY_IDX[i] for i in ni]
225
- marker = dict(size=12 if not three_d else 7,
226
- color="#F4B86E", # warm amber against the dark teal
227
  opacity=0.95,
228
- line=dict(color=KAIKAKU_MINT_BRIGHT, width=1.2))
229
- if three_d:
230
- fig.add_trace(go.Scatter3d(
231
- x=nx, y=ny, z=nz, mode="markers+text",
232
- marker=marker, text=nlabels, textposition="top center",
233
- textfont=dict(color=KAIKAKU_TEXT, size=10),
234
- hovertemplate="<b>%{text}</b> (neighbour)<extra></extra>",
235
- name=f"top-{k} neighbours",
236
- ))
237
- else:
238
- fig.add_trace(go.Scatter(
239
- x=nx, y=ny, mode="markers+text",
240
- marker=marker, text=nlabels, textposition="top center",
241
- textfont=dict(color=KAIKAKU_TEXT, size=10),
242
- hovertemplate="<b>%{text}</b> (neighbour)<extra></extra>",
243
- name=f"top-{k} neighbours",
244
- ))
245
-
246
- # Basket highlights (mint star)
247
  if basket_idxs:
248
  bx = [float(coords2[i, 0]) for i in basket_idxs]
249
  by = [float(coords2[i, 1]) for i in basket_idxs]
@@ -252,47 +237,31 @@ def umap_view(sibling, basket, show_neighbours, k, three_d=False):
252
  marker = dict(size=18 if not three_d else 9,
253
  color=KAIKAKU_MINT,
254
  symbol="star" if not three_d else "diamond",
255
- line=dict(color=KAIKAKU_DARK, width=2.5))
256
- if three_d:
257
- fig.add_trace(go.Scatter3d(
258
- x=bx, y=by, z=bz, mode="markers+text",
259
- marker=marker, text=blabels, textposition="top center",
260
- textfont=dict(color=KAIKAKU_MINT_BRIGHT, size=13),
261
- hovertemplate="<b>%{text}</b> (basket)<extra></extra>", name="basket",
262
- ))
263
- else:
264
- fig.add_trace(go.Scatter(
265
- x=bx, y=by, mode="markers+text",
266
- marker=marker, text=blabels, textposition="top center",
267
- textfont=dict(color=KAIKAKU_MINT_BRIGHT, size=13),
268
- hovertemplate="<b>%{text}</b> (basket)<extra></extra>", name="basket",
269
- ))
270
 
271
  title_suffix = " (3D)" if three_d else ""
272
  fig.update_layout(
273
  title=dict(text=f"UMAP of Epicure-{sibling.capitalize()}{title_suffix} - {n} ingredients",
274
- font=dict(color=KAIKAKU_TEXT, size=15)),
275
  height=650, margin=dict(l=40, r=40, t=60, b=40),
276
- paper_bgcolor=KAIKAKU_DARK, plot_bgcolor=KAIKAKU_DARK,
277
- font=dict(color=KAIKAKU_TEXT),
278
- legend=dict(orientation="v", x=1.02, y=1,
279
- bgcolor="rgba(26,61,63,0.85)",
280
- bordercolor=KAIKAKU_EDGE,
281
- font=dict(color=KAIKAKU_TEXT, size=11)),
282
  )
283
  if not three_d:
284
- fig.update_xaxes(showgrid=True, gridcolor=KAIKAKU_EDGE, zeroline=False,
285
- title=dict(text="UMAP 1", font=dict(color=KAIKAKU_TEXT)),
286
- tickfont=dict(color=KAIKAKU_TEXT))
287
- fig.update_yaxes(showgrid=True, gridcolor=KAIKAKU_EDGE, zeroline=False,
288
- title=dict(text="UMAP 2", font=dict(color=KAIKAKU_TEXT)),
289
- tickfont=dict(color=KAIKAKU_TEXT))
290
  else:
291
  fig.update_layout(scene=dict(
292
- xaxis=dict(title="UMAP 1", color=KAIKAKU_TEXT, backgroundcolor=KAIKAKU_DARK, gridcolor=KAIKAKU_EDGE),
293
- yaxis=dict(title="UMAP 2", color=KAIKAKU_TEXT, backgroundcolor=KAIKAKU_DARK, gridcolor=KAIKAKU_EDGE),
294
- zaxis=dict(title="PC1 (z)", color=KAIKAKU_TEXT, backgroundcolor=KAIKAKU_DARK, gridcolor=KAIKAKU_EDGE),
295
- bgcolor=KAIKAKU_DARK,
296
  ))
297
  return fig
298
 
@@ -456,78 +425,48 @@ def parse_fridge(raw_text, sibling, min_score=70):
456
 
457
  # ===== UI =====
458
 
459
- # Brand-coloured Soft theme. Mint primary, dark teal background.
460
- mint_palette = gr.themes.Color(
461
- c50="#F0FAF6", c100=KAIKAKU_MINT_BRIGHT, c200=KAIKAKU_MINT,
462
- c300="#92DCBE", c400="#6FD2AA", c500="#4CC896",
463
- c600="#3FA579", c700="#32835C", c800=KAIKAKU_MID,
464
- c900=KAIKAKU_DARK, c950=KAIKAKU_DEEP,
465
- )
466
-
467
- THEME = gr.themes.Base(
468
- primary_hue=mint_palette,
469
- secondary_hue=mint_palette,
470
  neutral_hue="slate",
471
  font=[gr.themes.GoogleFont("Inter"), "ui-sans-serif", "system-ui", "sans-serif"],
472
- ).set(
473
- body_background_fill=KAIKAKU_DARK,
474
- body_text_color=KAIKAKU_TEXT,
475
- background_fill_primary=KAIKAKU_DARK,
476
- background_fill_secondary=KAIKAKU_MID,
477
- block_background_fill=KAIKAKU_MID,
478
- block_border_color=KAIKAKU_EDGE,
479
- block_border_width="1px",
480
- block_label_background_fill=KAIKAKU_MID,
481
- block_label_text_color=KAIKAKU_MINT,
482
- block_title_text_color=KAIKAKU_MINT,
483
- button_primary_background_fill=KAIKAKU_MINT,
484
- button_primary_background_fill_hover=KAIKAKU_MINT_BRIGHT,
485
- button_primary_text_color=KAIKAKU_DARK,
486
- button_primary_border_color=KAIKAKU_MINT,
487
- button_secondary_background_fill=KAIKAKU_MID,
488
- button_secondary_background_fill_hover=KAIKAKU_EDGE,
489
- button_secondary_text_color=KAIKAKU_MINT,
490
- border_color_primary=KAIKAKU_EDGE,
491
- input_background_fill=KAIKAKU_DEEP,
492
- input_border_color=KAIKAKU_EDGE,
493
- input_placeholder_color=KAIKAKU_MUTED,
494
- checkbox_background_color=KAIKAKU_DEEP,
495
- checkbox_background_color_selected=KAIKAKU_MINT,
496
- slider_color=KAIKAKU_MINT,
497
- color_accent=KAIKAKU_MINT,
498
- color_accent_soft=KAIKAKU_MID,
499
  )
500
 
501
  CUSTOM_CSS = f"""
502
- .gradio-container {{max-width: 1280px !important; background: {KAIKAKU_DARK} !important;}}
503
  footer {{visibility: hidden;}}
504
- h1, h2, h3 {{color: {KAIKAKU_MINT};}}
505
- a {{color: {KAIKAKU_MINT};}}
506
  .sibling-card {{
507
- background: {KAIKAKU_MID}; border: 1px solid {KAIKAKU_EDGE};
508
- border-radius: 8px; padding: 12px 16px; margin: 4px 0;
 
 
 
509
  }}
510
- .sibling-name {{color: {KAIKAKU_MINT}; font-weight: 600; font-size: 1.05em;}}
511
- .sibling-desc {{color: {KAIKAKU_TEXT}; opacity: 0.85; font-size: 0.95em; line-height: 1.4;}}
512
- .gr-dataframe table {{color: {KAIKAKU_TEXT} !important;}}
513
  """
514
 
515
  # Precompute initial figures so plots are populated on first page load
516
  _INITIAL_UMAP = umap_view("chem", ["chicken","lemon","garlic"], True, 8, three_d=False)
517
  _INITIAL_HEATMAP = _basket_heatmap(MODELS["chem"], ["chicken","lemon","garlic"])
518
 
519
- SIBLING_CARDS = f"""
520
  <div class="sibling-card">
521
- <span class="sibling-name">Cooc</span>
522
- <span class="sibling-desc"> - Walks recipe co-occurrence only. Neighbours are recipe companions: ingredients that <em>get cooked with</em> the seed. Isotropic geometry (PR=173.6 of 300). Best for "what else do I cook with X".</span>
523
  </div>
524
  <div class="sibling-card">
525
- <span class="sibling-name">Core</span>
526
- <span class="sibling-desc"> - Blends typed FlavorDB compound walks with injected I-I walks at ii_repeat=10. Concentrated geometry (PR=94.2), tightest emergent modes. The middle-ground sibling: chemistry-aware but keeps recipe context.</span>
527
  </div>
528
  <div class="sibling-card">
529
- <span class="sibling-name">Chem</span>
530
- <span class="sibling-desc"> - Walks typed FlavorDB compound metapaths only (ii_repeat=0). Neighbours are flavour-profile peers: ingredients that <em>share aroma chemistry</em>. Best supervised-direction recovery; cuisine Cohen's d = 3.07 over 8 macro-regions.</span>
531
  </div>
532
  """
533
 
 
34
  KAIKAKU_TEXT = "#E8F4F1"
35
  KAIKAKU_MUTED = "#7AA8A2"
36
 
37
+ # Light matplotlib defaults; mint is an accent only
38
  plt.rcParams.update({
39
+ "figure.facecolor": "#ffffff",
40
+ "axes.facecolor": "#ffffff",
41
+ "axes.edgecolor": "#cccccc",
42
+ "axes.labelcolor": "#111111",
43
+ "xtick.color": "#333333",
44
+ "ytick.color": "#333333",
45
+ "text.color": "#111111",
46
+ "savefig.facecolor": "#ffffff",
47
  })
48
 
49
  MODELS = {
 
60
  FOOD_GROUPS: list[str] = _lab["food_groups"]
61
 
62
  FG_COLORS = {
63
+ "Vegetable": "#2ca02c",
64
+ "Fruit": "#e377c2",
65
+ "Grain": "#bcbd22",
66
+ "Dairy": "#17becf",
67
+ "Spice": "#d62728",
68
+ "Pantry": "#ff7f0e",
69
+ "Beverage": "#9467bd",
70
+ "Other": "#cccccc",
71
  }
72
 
73
  # Sanity-check log on import so Space logs show whether assets loaded
 
127
  fig, ax = plt.subplots(figsize=(6, 5))
128
  if len(valid) < 2:
129
  ax.text(0.5, 0.5, "Add 2+ ingredients to see pairwise cosines",
130
+ ha="center", va="center", fontsize=13, color="#888",
131
  transform=ax.transAxes)
 
132
  ax.axis("off")
 
133
  plt.tight_layout()
134
  return fig
135
  idxs = [m.vocab[n] for n in valid]
 
138
  im = ax.imshow(sim, cmap="viridis", vmin=-0.2, vmax=1.0, aspect="auto")
139
  ax.set_xticks(range(len(valid)))
140
  ax.set_yticks(range(len(valid)))
141
+ ax.set_xticklabels(valid, rotation=35, ha="right")
142
+ ax.set_yticklabels(valid)
143
  for i in range(len(valid)):
144
  for j in range(len(valid)):
145
  v = float(sim[i, j])
146
  color = "white" if v < 0.55 else "black"
147
  ax.text(j, i, f"{v:.2f}", ha="center", va="center", fontsize=10, color=color)
148
  cb = plt.colorbar(im, ax=ax)
149
+ cb.set_label("cosine")
150
+ ax.set_title("Pairwise cosine within the basket", fontsize=12)
 
 
 
 
151
  plt.tight_layout()
152
  return fig
153
 
 
209
  showlegend=False,
210
  ))
211
 
212
+ # Neighbour highlights (amber)
213
  if neighbour_set:
214
  ni = [i for i in range(n) if NAMES_BY_IDX[i] in neighbour_set]
215
  nx = [float(coords2[i, 0]) for i in ni]
216
  ny = [float(coords2[i, 1]) for i in ni]
217
  nz = [float(z[i]) for i in ni] if three_d else None
218
  nlabels = [NAMES_BY_IDX[i] for i in ni]
219
+ marker = dict(size=11 if not three_d else 6,
220
+ color="#ff8800",
221
  opacity=0.95,
222
+ line=dict(color="#ffffff", width=1.2))
223
+ TR = go.Scatter3d if three_d else go.Scatter
224
+ kwargs = dict(mode="markers+text",
225
+ marker=marker, text=nlabels, textposition="top center",
226
+ textfont=dict(size=10),
227
+ hovertemplate="<b>%{text}</b> (neighbour)<extra></extra>",
228
+ name=f"top-{k} neighbours")
229
+ fig.add_trace(TR(x=nx, y=ny, z=nz, **kwargs) if three_d else TR(x=nx, y=ny, **kwargs))
230
+
231
+ # Basket highlights (mint star, accent only)
 
 
 
 
 
 
 
 
 
232
  if basket_idxs:
233
  bx = [float(coords2[i, 0]) for i in basket_idxs]
234
  by = [float(coords2[i, 1]) for i in basket_idxs]
 
237
  marker = dict(size=18 if not three_d else 9,
238
  color=KAIKAKU_MINT,
239
  symbol="star" if not three_d else "diamond",
240
+ line=dict(color="#111111", width=1.5))
241
+ TR = go.Scatter3d if three_d else go.Scatter
242
+ kwargs = dict(mode="markers+text",
243
+ marker=marker, text=blabels, textposition="top center",
244
+ textfont=dict(size=13, color="#111111"),
245
+ hovertemplate="<b>%{text}</b> (basket)<extra></extra>", name="basket")
246
+ fig.add_trace(TR(x=bx, y=by, z=bz, **kwargs) if three_d else TR(x=bx, y=by, **kwargs))
 
 
 
 
 
 
 
 
247
 
248
  title_suffix = " (3D)" if three_d else ""
249
  fig.update_layout(
250
  title=dict(text=f"UMAP of Epicure-{sibling.capitalize()}{title_suffix} - {n} ingredients",
251
+ font=dict(size=15)),
252
  height=650, margin=dict(l=40, r=40, t=60, b=40),
253
+ paper_bgcolor="#ffffff", plot_bgcolor="#ffffff",
254
+ legend=dict(orientation="v", x=1.02, y=1, font=dict(size=11)),
 
 
 
 
255
  )
256
  if not three_d:
257
+ fig.update_xaxes(showgrid=True, gridcolor="#eeeeee", zeroline=False, title="UMAP 1")
258
+ fig.update_yaxes(showgrid=True, gridcolor="#eeeeee", zeroline=False, title="UMAP 2")
 
 
 
 
259
  else:
260
  fig.update_layout(scene=dict(
261
+ xaxis=dict(title="UMAP 1"),
262
+ yaxis=dict(title="UMAP 2"),
263
+ zaxis=dict(title="PC1 (z)"),
264
+ bgcolor="#ffffff",
265
  ))
266
  return fig
267
 
 
425
 
426
  # ===== UI =====
427
 
428
+ # Simple default light theme with mint as the primary accent only
429
+ THEME = gr.themes.Soft(
430
+ primary_hue=gr.themes.Color(
431
+ c50="#F0FAF6", c100=KAIKAKU_MINT_BRIGHT, c200=KAIKAKU_MINT,
432
+ c300="#92DCBE", c400="#6FD2AA", c500=KAIKAKU_MINT,
433
+ c600="#3FA579", c700="#32835C", c800="#1F5A3F",
434
+ c900=KAIKAKU_DARK, c950=KAIKAKU_DEEP,
435
+ ),
 
 
 
436
  neutral_hue="slate",
437
  font=[gr.themes.GoogleFont("Inter"), "ui-sans-serif", "system-ui", "sans-serif"],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
438
  )
439
 
440
  CUSTOM_CSS = f"""
441
+ .gradio-container {{max-width: 1280px !important;}}
442
  footer {{visibility: hidden;}}
 
 
443
  .sibling-card {{
444
+ border-left: 3px solid {KAIKAKU_MINT};
445
+ padding: 10px 14px;
446
+ margin: 6px 0;
447
+ background: #fafafa;
448
+ border-radius: 4px;
449
  }}
450
+ .sibling-name {{color: {KAIKAKU_DARK}; font-weight: 700;}}
451
+ .sibling-desc {{color: #333; font-size: 0.95em; line-height: 1.5;}}
 
452
  """
453
 
454
  # Precompute initial figures so plots are populated on first page load
455
  _INITIAL_UMAP = umap_view("chem", ["chicken","lemon","garlic"], True, 8, three_d=False)
456
  _INITIAL_HEATMAP = _basket_heatmap(MODELS["chem"], ["chicken","lemon","garlic"])
457
 
458
+ SIBLING_CARDS = """
459
  <div class="sibling-card">
460
+ <div class="sibling-name">Cooc - recipe-context only</div>
461
+ <div class="sibling-desc">Walks recipe co-occurrence (NPMI graph) only. Neighbours are recipe <em>companions</em>: things that get cooked with the seed. Isotropic geometry (PR=173.6 of 300). Best for "what else do I cook with X".</div>
462
  </div>
463
  <div class="sibling-card">
464
+ <div class="sibling-name">Core - blended (the middle ground)</div>
465
+ <div class="sibling-desc">Typed FlavorDB compound walks blended with injected I-I walks at ii_repeat=10. Concentrated geometry (PR=94.2), tightest emergent modes. Chemistry-aware but keeps recipe context.</div>
466
  </div>
467
  <div class="sibling-card">
468
+ <div class="sibling-name">Chem - chemistry only</div>
469
+ <div class="sibling-desc">Typed FlavorDB compound metapaths only (ii_repeat=0). Neighbours are flavour-profile <em>peers</em>: things that share aroma chemistry with the seed. Best supervised-direction recovery; cuisine Cohen's d = 3.07 across 8 macro-regions.</div>
470
  </div>
471
  """
472