Commit Graph
24 Commits
Author SHA1 Message Date
wassnameandClaudypoo f39a0763ab Add WVS/Inglehart-Welzel culture map placing LLMs among societies
wvs_map.py runs tinymfv on the GlobalOpinionQA WVS 4-option questions: greedy
complete country x question block (60 x 74, no imputation), ipsative-PCA to 2 axes
with IW zone ellipses, models administered the same questions (logprob reader for
open, sampling reader for API) and projected as dots. Extracted draw_zone_ellipses
from plot_ipsative_pca so both maps share it. Human map reproduces the Economist
layout: African-Islamic/South-Asia (survival) left, English-Speaking/Protestant
Europe (self-expression) right, Confucian/Latin America/Orthodox separating.

Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
2026-07-04 20:37:02 +08:00
wassnameandClaudypoo b2532acd90 Switch zone blobs to country-mean covariance ellipses, fit PCA on means
Per feedback the individual-respondent contours filled the frame (within >>
between variance). Now each IW zone is a ~1.6-sigma covariance ellipse over its
member country-mean dots, with an eigenvalue floor so 1-2 country zones get a
visible blob instead of a dot/line (fixes big5 SG/PK orphans). PCA now fits on the
country means M so the axes are between-country and zones separate. mfq2/big5/mfv
read cleanly; humor still overlaps (real negative result: humor country profiles
don't cluster the IW way).

Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
2026-07-04 20:22:23 +08:00
wassnameandClaudypoo 2c2dd9b9bf Add MFQ-2 per-zone p90 respondent ellipses to ipsative map
respondent_profiles now returns countries alongside profiles; plot_ipsative_pca
gains respondent_zones -> a p90 Gaussian ellipse per IW zone of the projected
Atari respondent cloud (edge-only, no scipy). mfq2 uses these real-respondent
ellipses; the other instruments keep country-mean hulls. Journal notes the
finding: individual profiles overlap across cultures (within >> between variance),
so only the country-mean hull reproduces the Economist's clean zone blobs.

Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
2026-07-04 19:25:52 +08:00
wassnameandClaudypoo 0e7178dfbd Add Inglehart-Welzel zone hulls + outlier labels to ipsative maps
Echoes the Economist WVS 'Godless hippies' chart: shaded convex-hull blobs per
IW cultural zone (inline 2D hull, no scipy dep so the maps extra stays
matplotlib-only) and bold-first labels for named outliers. Caller owns the zone
taxonomy + name/ISO2 normalizer, fails loud on unmapped countries; the corrupt
'(nu' big5 row is explicitly excluded with a warning.

Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
2026-07-04 19:16:41 +08:00
wassname 6fcdfea30f Support sampled survey reads and MFV c-grid plots 2026-06-30 13:28:20 +08:00
wassname d8941f47ac Rewrite README around profile plots 2026-06-30 12:38:20 +08:00
wassname 5a39b7ba9c Simplify showcase plot encodings 2026-06-29 21:32:05 +08:00
wassnameandClaudypoo f1d23b338e plots: range+map only for every instrument; legend = c-multiplier not full desc
- drop splom/splom_zoom/range_zoom (ordinal) and the MFV dlogit dumbbell; every
  instrument now yields exactly map_pca_ipsative + range, uniform.
- range pole label is now 'c=+1'/'c=-1' (the multiplier); the full steer desc
  stays in the title only (was crammed into each pole annotation).

Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
2026-06-26 04:30:02 +08:00
wassnameandClaudypoo 2160304541 plots: MFV reuses shared ipsative map + range in z-space; fix range coherence-gate
MFV got two bespoke figures (z-emphasis bars + dlogit dumbbell). Per GPT-5.5 code
review, reuse the geometry not the ordinal semantics: a _mfv_zspace adapter feeds the
same plot_ipsative_pca + plot_range the ordinal instruments use, in z-scored relative-
emphasis space (logit-violation and 1-5 wrongness cannot share a raw axis). Keep the
dlogit dumbbell as a raw-magnitude diagnostic; drop the bespoke map_emphasis.

Also fix a real bug the review found: plot_ordinal gated the map/SPLOM trajectory to
coherent c (pmass >= 0.95*base) but still passed ALL cs to range/range_zoom, so the
figures disagreed on which poles were valid. Range/zoom now use the same coh_cs.
plot_range gains an ylabel param (MFV is z-space, not 1-M); draw_steer asserts sorted
cs; MFV asserts only Social Norms is dropped.

Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
2026-06-25 15:51:40 +08:00
wassnameandClaudypoo fe0d27b9bd maps: more bottom padding + clearer minimap title
Bump the bottom legend strip (PAD_B 0.52->0.62) and lower/shrink the compass + minimap insets so
the compass title clears the lowest data points. Rename the minimap title "full space" ->
"all human respondents" (says what the backdrop cloud is).

Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
2026-06-25 11:48:31 +08:00
wassnameandClaudypoo 22f4f64201 maps: deterministic bottom legend strip (compass left, minimap right)
Replace the per-plot least-crowded-corner heuristic (+ label pruning) with a fixed layout: pad the
y-axis bottom by PAD_B of the data range to reserve a clean strip, then always place the minimap
bottom-right (reads like a small map) and the compass bottom-left. Insets are opaque so the backdrop
haze stays behind them. Same on every instrument, no dependence on where the trajectory heads, and
drops the crowd/rank/prune code + the now-unused pad param.

Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
2026-06-25 11:46:12 +08:00
wassnameandClaudypoo 5d2e0c3630 maps: violin SPLOM diagonals, de-grid off-diagonals, label-aware compass
- SPLOM diagonal: replace the confusing histogram/rug + vertical AI rules with a horizontal violin
  of the human spread + the AI base/+C/-C as the same trajectory dots used off-diagonal (1-D
  analogue), so the diagonal reads the same way as the rest. Unifies full + zoom (no branch).
- SPLOM off-diagonal: jitter the respondent cloud -- MFQ-2 ordinal scores land on a lattice that
  reads as grid-dots; jitter softens it to a density (it is the human joint covariance backdrop).
- Compass corner now counts text labels (society codes, steer labels), not just data points, and a
  final pass drops any society label still under the compass/minimap box. No legend-over-label.

Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
2026-06-25 10:58:00 +08:00
wassnameandClaudypoo 00dca35f85 maps: place-or-omit society labels, unconditional society+steer crop, readable zoom diagonals
- Society labels: pin each ISO code beside its dot (small fixed offset) and DROP any that would
  collide rather than fling it far on a leader line -- close or omitted, never ambiguous.
- Crop to societies + steer for EVERY instrument (drop the synthetic/real branch): the human cloud
  (mfq2 respondents too) is far wider than the societies, so it buried them in a central blob. The
  cloud stays a clipped backdrop + a "full space" minimap shows where the frame sits.
- SPLOM zoom diagonals: the narrow window slices the marginal into solid blocks, so swap the
  histogram for a visible mid-panel society rug + the AI steer rules. Full SPLOM keeps the histogram.
- Minimap: no labels (inset too small); orientation comes from the viewport rectangle.

Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
2026-06-25 10:49:17 +08:00
wassnameandClaudypoo 761bf7412a maps: drop society label leader-lines (draw_lines=False)
The textalloc leader lines made a spider-web across the map; 2-letter ISO codes
are short enough to sit beside their dot with only a small overlap-avoidance
nudge. Cleaner, less ink, label stays next to its point in almost all cases.

Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
2026-06-25 10:31:36 +08:00
wassnameandClaudypoo a2fe33ac53 maps: foundation SPLOM + minimap, crop synthetic-haze maps to societies+steer
- plot_splom: KxK pairs-plot (mfq2 real joint only -- others ship independent-
  marginal haze that would fabricate off-diagonal correlation). lower=joint
  scatter w/ AI base->steer trajectory, diag=marginal+AI rules, upper=Pearson r
  sized by |r|. Ordered by PC1 loading so binding (authority/loyalty) cluster.
  full + AI-zoom (macro/micro). NaN-safe at collapsed poles.
- ipsative map: synthetic haze is far wider than the societies, so it now crops to
  societies+steer (haze clips) and adds a 'full space' minimap with a viewport
  rectangle -- readable big5/16pf/humor maps with macro context kept.
- compass labels via textalloc (was overlapping); NaN-safe crop.

Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
2026-06-25 08:45:01 +08:00
wassnameandClaudypoo 9b13db3390 maps: multi-C trajectory overlay on ipsative map (path + hollow-at-incoherent + adaptive compass)
Driver writes the signed c-multiplier per row; plotter feeds the full c-sweep to
the range (already multi-c capable) and a connected path to the map. Headline
arrows point to calibrated c=+-1; trajectory dots at |c|>1 extend beyond, growing
with |c|, drawn hollow where admin pmass fell below the coherence floor. Compass
moves to the least-crowded corner so the +c arm (which heads toward its own
loading) stops colliding with it.

Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
2026-06-25 08:35:29 +08:00
wassnameandClaudypoo 0e92e66415 maps: human haze on all ipsative maps (synthetic resample for big5/16pf/humor)
Split the PCA-fit basis from the scattered cloud in plot_ipsative_pca: add a
'haze' arg so instruments with only society-level mean+sd get a backdrop
(marginal Normal resample per country) without that resample dictating the
axes. mfq2 keeps its real Atari respondents (fit + haze). Previously only mfq2
had any human scatter; big5/16pf/humor showed bare society dots.

Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
2026-06-25 07:30:37 +08:00
wassnameandClaudypoo 9a52bf3641 nan-aware zoom y-limits; render valid ordinal showcase on unified eval
plot_range_zoom crashed on the NaN-at-collapse cells (a collapsed steer pole
reads NaN by design, "do not compare"). nanmin/nanmax frame the un-collapsed
cells; draw_steer already skips the NaN arm. Figures are the first valid
ordinal showcase: mfq2/big5/16pf/humor pmass 0.99+ and the steer registers at
think=64 (mfq2 care 3.86 base -> 3.13 at C=+1), unlike the old think=0 run.

Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
2026-06-24 21:23:43 +08:00
wassnameandClaudypoo d20f717251 maps: respondent-haze ipsative map + glossed axes (port from mft_honesty)
plot_ipsative_pca gains an optional respondents arg: when given it fits the
ipsative PCA basis on the individual cloud (3902 Atari respondents) and scatters
them behind the society dots, with an envelope-core crop, matching the
mft_honesty fig_pca_ipsative the project standardized on. Axis labels carry a
foundation-loading gloss. respondents=None keeps the old society-only behavior.

Bundles atari_study2_raw.csv + a respondent_profiles loader (keying verbatim
from Atari Code_Study2.R) so tinymfv owns the data, no mft_honesty dependency.
plot_steer_showcase passes respondents for mfq2 only.

Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
2026-06-24 15:49:24 +08:00
wassnameandClaudypoo e2c6c9cfe8 maps: restore joint dots on main-range arms (poles stay head-only)
The c=+-1,+-2 steps were dropped from the main range as clutter, but they
carry the steer's spacing/curvature; bring them back small (ms*0.6) while
the calibrated poles (cs[0]/cs[-1] = -+signed_C) keep head-only arrowheads.

Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
2026-06-24 12:33:18 +08:00
wassnameandClaudypoo 95b397cabf maps: base->pole fan arrows + human/AI brackets + trimmed titles
plot_range/plot_range_zoom now draw the steer via a shared draw_steer: two arms
fanning from the base dot (red +c, blue -c), each a line + constant-size triangle
head pointing away from base (matches plot_ipsative_pca's base->pole convention,
not the old -c->+c single arrow). Marker heads not FancyArrows so short arms don't
shrink/flip; arms x-offset so a non-bidirectional steer reads as two parallel arms;
poles are head-only (no dot blob); interior step dots off on the main range, on in
the zoom. human/AI direct-label brackets pinned to the panel top. Legend prose out
of the suptitle (short headline only); callers put it in the figure caption.

Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
2026-06-24 12:15:33 +08:00
wassnameandClaudypoo d5876a46d3 correctness fixes from gpt-5.5 review (ordinal path + fail-fast asserts)
Second external review (gpt-5.5, correctness-focused) on the post-cleanup tree.
Found no off-by-one/double-flip in the ordinal canonicalization+keying. Fixed the
parts I agreed with and could verify on the path the experiment uses:
- read.py: NaN-safe answer-token renorm. p_a/pmass poisons the profile with NaN when
  pmass underflows to 0 at coherence collapse -- exactly when pmass should just flag it.
  softmax(logp_allowed) is identical when pmass>0 and stable at collapse.
- maps.ipsative_pca: move SVD sign-stabilization INTO the helper so it and
  plot_ipsative_pca share one orientation (saved coords could otherwise mirror the figure).
- instrument: assert ordinal answer_space is ['1'..scale_max] IN ORDER (reduce_ordinal
  weights by position; a reordered space silently inverts E) -- was length-only.
- instrument.per_item_categorical: assert per-item dimension/sign agree across frames and
  frames are distinct, instead of silently averaging under rows[0]'s metadata.
- pyproject: move matplotlib+textalloc to an optional `maps` extra; evals stay headless.
- tests: drop imports of the deleted reduce_nominal/expected_value, inline the expectation,
  remove the now-impossible nominal-reducer test.

Deferred (flagged to maintainer): two NaN/window issues in guided.py's forced-choice
rollout (nominal evaluate() path) -- not exercised by this experiment, can't smoke-test,
and the NaN-as-collapse-signal there is a deliberate design.

Verified: experiment smoke green on all 4 instruments (no assert false-fires), 6 pure
unit tests pass, headless import clean, 16pf map renders.

Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
2026-06-23 21:25:33 +08:00
wassnameandClaudypoo 442bc5bba3 address external API review: lazy maps, prune dead code, type administer return
Whole-library review (deepseek-v4-pro) flagged tinymfv as not-yet-ready as a shared
dep. Fixes for the parts I agreed with:
- lazy `maps` import via module __getattr__ so `import tinymfv` stays headless/fast
  (no forced matplotlib) for numeric-only consumers; `tinymfv.maps.*` still works.
- trim __all__ to the front door (entrypoints + types + data api); plumbing stays
  importable but out of `import *`.
- delete dead code: reduce_nominal + REDUCERS (evaluate folds its profile inline),
  expected_value, HF_REPO, ROOT, _DEFAULT_FORCED_HINT.
- type administer's return as a TypedDict (AdministerResult/ItemRow/ItemFrameRow) so
  the schema is documented + checkable without reading source; still a plain dict at
  runtime (zero consumer churn).
- maps.plot_ipsative_pca: parametrize the legend labels (defaults preserve output)
  and rename hon/dis -> pos/neg so a non-honesty steer gets a correct legend.
- drop 'canary' jargon and panel/review-# archaeology from comments.

Verified: `import tinymfv` no longer loads matplotlib; lazy maps still resolves;
experiment mfq2 smoke green through the typed administer.

Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
2026-06-23 20:40:04 +08:00
wassnameandClaudypoo 0bef8b0d3d instrument: fail-fast asserts from external review (#57)
deepseek-v4-pro review of the admin->tinymfv port (algebra proven correct, diff
8.25e-08 = float noise). Closes its flagged fail-fast gaps:
- per_item_categorical asserts uniform frame count per item (else the per-item
  average would silently reweight a factor)
- reduce_ordinal asserts dimension is not None (no phantom-factor pooling)
- administer asserts every ordinal item carries meta['task'] (else build_prompt
  would silently drop the response-scale legend and the profile would be junk)
Also commits maps.py + viz deps from the stage-3 port.

Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
2026-06-23 09:41:36 +08:00