|
| 1 | +# ============================================================================= |
| 2 | +# Titanic survival prediction — OMC + pandas + sklearn end-to-end |
| 3 | +# ============================================================================= |
| 4 | +# Real Kaggle classic, 891 passengers, mix of numerical and categorical |
| 5 | +# features, missing values everywhere. The full data-science loop in |
| 6 | +# 50 lines of OMC. |
| 7 | +# |
| 8 | +# Pipeline: |
| 9 | +# seaborn.load_dataset("titanic") — load real CSV with nulls |
| 10 | +# pandas fillna + one-hot — clean + encode |
| 11 | +# OMC harmonic feature engineering — fare_attractor, age_group |
| 12 | +# sklearn train/test split + RF — fit + predict |
| 13 | +# accuracy + confusion matrix — report |
| 14 | +# |
| 15 | +# Run: |
| 16 | +# ./target/release/omnimcode-standalone examples/datascience/titanic.omc |
| 17 | +# ============================================================================= |
| 18 | + |
| 19 | +import "examples/lib/pd.omc" as pd; |
| 20 | +import "examples/lib/sklearn.omc" as sk; |
| 21 | +import "examples/lib/np.omc" as np; |
| 22 | + |
| 23 | +println("=== Titanic survival prediction ==="); |
| 24 | +println(""); |
| 25 | + |
| 26 | +# ---- 1. Load via seaborn --------------------------------------------------- |
| 27 | + |
| 28 | +h sns = py_import("seaborn"); |
| 29 | +h df = py_call(sns, "load_dataset", ["titanic"]); |
| 30 | +println(concat_many("loaded ", pd.nrows(df), " passengers, ", |
| 31 | + pd.ncols(df), " columns")); |
| 32 | + |
| 33 | +# ---- 2. Clean: fill missing ages with the median, drop other-null rows --- |
| 34 | + |
| 35 | +h ages_full = pd.col(df, "age"); |
| 36 | +# numpy's nanmedian skips NaN entries (the form pandas-nulls take |
| 37 | +# after py_to_omc conversion). Avoids a manual filter loop. |
| 38 | +h age_median = np.nanmedian(ages_full); |
| 39 | +println(concat_many("median age (after skipping nulls): ", age_median)); |
| 40 | + |
| 41 | +h df1 = pd.fillna_col(df, "age", age_median); |
| 42 | +h df2 = pd.fillna_col(df1, "fare", 0.0); |
| 43 | + |
| 44 | +# ---- 3. Harmonic feature engineering -------------------------------------- |
| 45 | +# Two new features: |
| 46 | +# age_attractor — fold(age) snaps each passenger to a Fibonacci age |
| 47 | +# cohort (8/13/21/34/55). Captures "natural age band" |
| 48 | +# independent of the raw value. |
| 49 | +# fare_class — fold(fare * 10) buckets fares onto attractors |
| 50 | +# (cheap third-class fares cluster on 8-21 attractors; |
| 51 | +# expensive cabins on 144-377). |
| 52 | +# |
| 53 | +# Both are computed by OMC fns called from Python via apply_omc — the |
| 54 | +# Python ↔ OMC bridge running ON the pandas DataFrame. |
| 55 | + |
| 56 | +fn age_attractor(age) { |
| 57 | + return fold(to_int(age)); |
| 58 | +} |
| 59 | +fn fare_class(fare) { |
| 60 | + return fold(to_int(fare * 10)); |
| 61 | +} |
| 62 | + |
| 63 | +h t0 = now_ms(); |
| 64 | +h age_attr = pd.apply_omc(df2, "age", "age_attractor"); |
| 65 | +h fare_attr = pd.apply_omc(df2, "fare", "fare_class"); |
| 66 | +h t1 = now_ms(); |
| 67 | +println(concat_many("computed harmonic features for ", arr_len(age_attr), |
| 68 | + " rows in ", t1 - t0, " ms (Python apply, OMC callbacks)")); |
| 69 | + |
| 70 | +# ---- 4. Build feature matrix ---------------------------------------------- |
| 71 | +# Columns: pclass, sex_male (0/1), age, fare, sibsp, parch, age_attr, fare_attr |
| 72 | + |
| 73 | +h pclass = pd.col(df2, "pclass"); |
| 74 | +h sex = pd.col(df2, "sex"); |
| 75 | +h age = pd.col(df2, "age"); |
| 76 | +h fare = pd.col(df2, "fare"); |
| 77 | +h sibsp = pd.col(df2, "sibsp"); |
| 78 | +h parch = pd.col(df2, "parch"); |
| 79 | +h y = pd.col(df2, "survived"); |
| 80 | + |
| 81 | +# Encode sex as 0/1. |
| 82 | +fn sex_to_int(s) { |
| 83 | + if s == "male" { return 1; } |
| 84 | + return 0; |
| 85 | +} |
| 86 | +h sex_enc = arr_map(sex, sex_to_int); |
| 87 | + |
| 88 | +# Stitch into per-row feature vectors. |
| 89 | +fn build_X(includes_harmonic) { |
| 90 | + h n = arr_len(pclass); |
| 91 | + h X = []; |
| 92 | + h i = 0; |
| 93 | + while i < n { |
| 94 | + h row = [ |
| 95 | + arr_get(pclass, i), |
| 96 | + arr_get(sex_enc, i), |
| 97 | + arr_get(age, i), |
| 98 | + arr_get(fare, i), |
| 99 | + arr_get(sibsp, i), |
| 100 | + arr_get(parch, i) |
| 101 | + ]; |
| 102 | + if includes_harmonic == 1 { |
| 103 | + row = arr_concat(row, [arr_get(age_attr, i), arr_get(fare_attr, i)]); |
| 104 | + } |
| 105 | + arr_push(X, row); |
| 106 | + i = i + 1; |
| 107 | + } |
| 108 | + return X; |
| 109 | +} |
| 110 | + |
| 111 | +h X_base = build_X(0); |
| 112 | +h X_aug = build_X(1); |
| 113 | +println(concat_many("feature matrix: ", arr_len(X_base), " rows, ", |
| 114 | + arr_len(arr_get(X_base, 0)), " base features, ", |
| 115 | + arr_len(arr_get(X_aug, 0)), " with harmonic")); |
| 116 | +println(""); |
| 117 | + |
| 118 | +# ---- 5. Train + evaluate baseline vs harmonic-augmented ------------------- |
| 119 | + |
| 120 | +fn run_model(X, y, label) { |
| 121 | + h split = sk.train_test_split(X, y, 0.3); |
| 122 | + h X_train = arr_get(split, 0); |
| 123 | + h X_test = arr_get(split, 1); |
| 124 | + h y_train = arr_get(split, 2); |
| 125 | + h y_test = arr_get(split, 3); |
| 126 | + |
| 127 | + h model = sk.random_forest_classifier(100); |
| 128 | + sk.fit(model, X_train, y_train); |
| 129 | + h preds = sk.predict(model, X_test); |
| 130 | + h acc = sk.accuracy_score(y_test, preds); |
| 131 | + println(concat_many(" ", label, ": accuracy = ", acc)); |
| 132 | + return acc; |
| 133 | +} |
| 134 | + |
| 135 | +println("=== Random Forest results ==="); |
| 136 | +h acc_base = run_model(X_base, y, "baseline (6 features) "); |
| 137 | +h acc_aug = run_model(X_aug, y, "with harmonic feats (8) "); |
| 138 | +println(""); |
| 139 | + |
| 140 | +h delta = acc_aug - acc_base; |
| 141 | +h verdict = "tie"; |
| 142 | +if delta > 0 { verdict = "harmonic helps"; } |
| 143 | +if delta < 0 { verdict = "harmonic hurts"; } |
| 144 | +println(concat_many("delta: ", delta, " (", verdict, ")")); |
| 145 | + |
| 146 | +println(""); |
| 147 | +println("=== Done ==="); |
0 commit comments