mirror of
https://github.com/wassname/moral-maps.git
synced 2026-09-09 11:27:22 +08:00
wvs map: drop CI whiskers (too messy), move uncertainty to a table
With a dozen+ models the 95% CI crosses overlap into noise. Remove the whiskers from plot_value_map (clean red dots) and instead write a widest-first CI table (wvs_model_ci.md) next to the figure. The table also documents that the wide CIs are item-disagreement (a model rating different WVS items inconsistently on an axis), which more samples will not shrink -- not sampling noise. Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
This commit is contained in:
@@ -269,6 +269,19 @@ def main() -> None:
|
||||
x, y, xs, ys = models[key]
|
||||
logger.info(f"cached {key}: ({x:.2f}, {y:.2f}) +-({1.96*xs:.02f}, {1.96*ys:.02f}) 95% CI")
|
||||
|
||||
# Uncertainty as a TABLE, not whiskers on the map (the CI crosses overlap into noise with a dozen+
|
||||
# models). Sorted widest-first so the mushy models are obvious. The CI is bootstrap over items +
|
||||
# samples; for the wide ones it's item-disagreement (the model rates different WVS items
|
||||
# inconsistently on an axis), which more samples will NOT shrink -- see the readme/journal note.
|
||||
ci_rows = [(k, v[0], v[1], 1.96 * v[2], 1.96 * v[3]) for k, v in models.items() if len(v) > 3]
|
||||
if ci_rows:
|
||||
from tabulate import tabulate
|
||||
ci_rows.sort(key=lambda r: -(r[3] + r[4]))
|
||||
table = tabulate(ci_rows, headers=["model", "x self-expr", "y secular", "x 95%CI", "y 95%CI"],
|
||||
tablefmt="pipe", floatfmt="+.2f")
|
||||
Path(args.out).with_name("wvs_model_ci.md").write_text(table + "\n")
|
||||
logger.info("model coords + 95% CI (widest first):\n" + table)
|
||||
|
||||
# Render through the SHARED value-map renderer (same one the instrument value maps use): pole
|
||||
# signposts through the human median, 4 auto-selected zone hulls, textalloc labels, model stars.
|
||||
_, emph = zones_for(countries)
|
||||
|
||||
+4
-7
@@ -268,16 +268,13 @@ def plot_value_map(display: str, countries: list[str], P: np.ndarray,
|
||||
tcol = ["#111"] * len(lab_i)
|
||||
sx, sy = list(P[:, 0]), list(P[:, 1])
|
||||
if models:
|
||||
# models carry (x, y[, x_se, y_se]); the CI is NOT drawn -- with a dozen+ models the whisker
|
||||
# crosses overlap into noise. Uncertainty lives in the companion table (wvs_map save_ci_table),
|
||||
# which also shows it's item-disagreement (irreducible by N), not sampling noise.
|
||||
mnames = list(models)
|
||||
mx = np.array([models[k][0] for k in mnames])
|
||||
my = np.array([models[k][1] for k in mnames])
|
||||
# optional bootstrap SE (rated models carry (x, y, x_se, y_se); logprob models just (x, y))
|
||||
xse = np.array([models[k][2] if len(models[k]) > 2 else 0.0 for k in mnames])
|
||||
yse = np.array([models[k][3] if len(models[k]) > 3 else 0.0 for k in mnames])
|
||||
if (xse > 0).any() or (yse > 0).any(): # 95% CI -> a mushy model reads as uncertain
|
||||
ax.errorbar(mx, my, xerr=1.96 * xse, yerr=1.96 * yse, fmt="none", ecolor=MODEL_RED,
|
||||
elinewidth=1.0, alpha=0.4, capsize=2.5, capthick=0.8, zorder=7)
|
||||
ax.scatter(mx, my, s=150, marker="o", c=MODEL_RED, # Economist: bigger red dots
|
||||
ax.scatter(mx, my, s=120, marker="o", c=MODEL_RED, # Economist: bigger red dots
|
||||
edgecolors="white", linewidths=1.0, zorder=8)
|
||||
tx += list(mx); ty += list(my); txt += mnames
|
||||
tcol += [MODEL_RED] * len(mnames)
|
||||
|
||||
Reference in New Issue
Block a user