Kogann commited on
Commit
62d2692
·
verified ·
1 Parent(s): b24ae64

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +54 -26
app.py CHANGED
@@ -225,8 +225,12 @@ def run_code(code: str, df: pd.DataFrame) -> dict:
225
  except Exception:
226
  err = traceback.format_exc(limit=3)
227
 
228
- for i, num in enumerate(plt.get_fignums()): # rescue orphan figures
229
- plt.figure(num).savefig(f"{PLOTS_DIR}/rescued_{i}.png", bbox_inches="tight")
 
 
 
 
230
  plt.close("all")
231
 
232
  return {"ok": err is None, "stdout": buf.getvalue(), "error": err,
@@ -247,12 +251,40 @@ def format_error(err: str, code: str) -> str:
247
  # --------------------------------------------------------------------------- #
248
  # 5. Deterministic facts and tables
249
  # --------------------------------------------------------------------------- #
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
  def make_tables(df: pd.DataFrame, max_card: int = 6) -> dict:
251
  t = {}
252
  n = df.isna().sum()
253
  n = n[n > 0].sort_values(ascending=False)
254
- t["missing"] = (pd.DataFrame({"missing": n, "pct": (100 * n / len(df)).round(1)})
255
- .to_markdown() if len(n) else "_No missing values._")
 
 
 
 
 
 
 
256
 
257
  bins = [c for c in df.select_dtypes(include="number") if df[c].nunique() == 2]
258
  blocks = [
@@ -263,21 +295,18 @@ def make_tables(df: pd.DataFrame, max_card: int = 6) -> dict:
263
  ]
264
  t["groups"] = "\n\n".join(blocks) if blocks else "_No low-cardinality groupings._"
265
 
 
 
 
 
 
 
 
 
 
266
  num = df.select_dtypes(include="number")
267
- corr = num.corr()
268
- pr = corr.where(~np.eye(len(corr), dtype=bool)).stack().sort_values(
269
- key=abs, ascending=False)
270
- seen, rows = set(), []
271
- for (a, b), v in pr.items():
272
- if (b, a) in seen:
273
- continue
274
- seen.add((a, b))
275
- rows.append({"pair": f"{a} ~ {b}", "r": round(v, 3)})
276
- if len(rows) >= 8:
277
- break
278
- t["corr"] = (pd.DataFrame(rows).to_markdown(index=False)
279
- if rows else "_Too few numeric columns._")
280
- t["describe"] = num.describe().round(2).to_markdown() if len(num.columns) else "_None._"
281
  return t
282
 
283
 
@@ -304,14 +333,11 @@ def key_facts(df: pd.DataFrame, max_card: int = 6) -> dict:
304
  b, c, k, v = best
305
  f["group"] = f"the highest mean {b} is {v:.3f}, for {c} = {k}"
306
 
307
- num = df.select_dtypes(include="number")
308
- if len(num.columns) >= 2:
309
- corr = num.corr()
310
- pr = corr.where(~np.eye(len(corr), dtype=bool)).stack().sort_values(
311
- key=abs, ascending=False)
312
- (a, b2), v = pr.index[0], pr.iloc[0]
313
- f["corr"] = (f"the strongest correlation is {a} ~ {b2} at r = {v:+.3f} "
314
- f"({'positive' if v > 0 else 'negative'})")
315
  return f
316
 
317
 
@@ -415,6 +441,8 @@ def run_agent(instruction: str, dataset: str):
415
  P += ["## Correlations", t["corr"], ""]
416
  if "corr" in kf:
417
  P += [one_liner(kf["corr"]), ""]
 
 
418
  P += ["## Numeric Summary", t["describe"], ""]
419
 
420
  take = generate("Rewrite each fact as one markdown bullet. Add nothing.",
 
225
  except Exception:
226
  err = traceback.format_exc(limit=3)
227
 
228
+ # safety net: model called plt.show(), or crashed with a figure still open
229
+ for i, num in enumerate(plt.get_fignums(), start=1):
230
+ fig = plt.figure(num)
231
+ if not fig.get_axes(): # skip blank figures
232
+ continue
233
+ fig.savefig(f"{PLOTS_DIR}/figure_{i}.png", bbox_inches="tight")
234
  plt.close("all")
235
 
236
  return {"ok": err is None, "stdout": buf.getvalue(), "error": err,
 
251
  # --------------------------------------------------------------------------- #
252
  # 5. Deterministic facts and tables
253
  # --------------------------------------------------------------------------- #
254
+ REDUNDANT_R = 0.99 # |r| at or above this is a duplicate encoding, not a finding
255
+
256
+
257
+ def corr_pairs(df: pd.DataFrame):
258
+ """Split correlations into real findings and near-duplicate columns."""
259
+ num = df.select_dtypes(include="number")
260
+ if len(num.columns) < 2:
261
+ return [], []
262
+ corr = num.corr()
263
+ pr = corr.where(~np.eye(len(corr), dtype=bool)).stack().sort_values(
264
+ key=abs, ascending=False)
265
+ seen, dup, real = set(), [], []
266
+ for (a, b), v in pr.items():
267
+ if (b, a) in seen:
268
+ continue
269
+ seen.add((a, b))
270
+ row = {"pair": f"{a} ~ {b}", "r": round(v, 3)}
271
+ (dup if abs(v) >= REDUNDANT_R else real).append(row)
272
+ return real, dup
273
+
274
+
275
  def make_tables(df: pd.DataFrame, max_card: int = 6) -> dict:
276
  t = {}
277
  n = df.isna().sum()
278
  n = n[n > 0].sort_values(ascending=False)
279
+ if len(n):
280
+ head = n.head(15)
281
+ tbl = pd.DataFrame({"missing": head,
282
+ "pct": (100 * head / len(df)).round(1)}).to_markdown()
283
+ if len(n) > 15:
284
+ tbl += f"\n\n_+{len(n) - 15} more columns with missing values._"
285
+ t["missing"] = tbl
286
+ else:
287
+ t["missing"] = "_No missing values._"
288
 
289
  bins = [c for c in df.select_dtypes(include="number") if df[c].nunique() == 2]
290
  blocks = [
 
295
  ]
296
  t["groups"] = "\n\n".join(blocks) if blocks else "_No low-cardinality groupings._"
297
 
298
+ real, dup = corr_pairs(df)
299
+ t["corr"] = (pd.DataFrame(real[:8]).to_markdown(index=False)
300
+ if real else "_Too few numeric columns._")
301
+ if dup:
302
+ t["redundant"] = (
303
+ f"_{len(dup)} column pairs correlate at |r| >= {REDUNDANT_R} — likely duplicate "
304
+ "encodings rather than findings._\n\n"
305
+ + pd.DataFrame(dup[:8]).to_markdown(index=False))
306
+
307
  num = df.select_dtypes(include="number")
308
+ t["describe"] = (num.describe().T.round(2).to_markdown() # transposed
309
+ if len(num.columns) else "_None._")
 
 
 
 
 
 
 
 
 
 
 
 
310
  return t
311
 
312
 
 
333
  b, c, k, v = best
334
  f["group"] = f"the highest mean {b} is {v:.3f}, for {c} = {k}"
335
 
336
+ real, _ = corr_pairs(df)
337
+ if real:
338
+ r = real[0]
339
+ f["corr"] = (f"the strongest correlation is {r['pair']} at r = {r['r']:+.3f} "
340
+ f"({'positive' if r['r'] > 0 else 'negative'})")
 
 
 
341
  return f
342
 
343
 
 
441
  P += ["## Correlations", t["corr"], ""]
442
  if "corr" in kf:
443
  P += [one_liner(kf["corr"]), ""]
444
+ if "redundant" in t:
445
+ P += ["## Redundant Columns", t["redundant"], ""]
446
  P += ["## Numeric Summary", t["describe"], ""]
447
 
448
  take = generate("Rewrite each fact as one markdown bullet. Add nothing.",