Files
moral-maps/src/tinymfv/zones.py
T
wassnameandClaudypoo 2af00b374c Extract IW zone taxonomy to tinymfv.zones, extend to WVS 90 countries
Second consumer (the WVS map) needs the same curated zone data, so move it out of
the showcase script into the package rather than duplicate it (avoids drift in
research data). Extended IW_ZONE to cover all 90 WVS countries + SAR/name-variant
aliases. Showcase script now imports zones_for from tinymfv.zones.

Co-Authored-By: Claudypoo <288921227+claudypoo@users.noreply.github.com>
2026-07-04 20:26:44 +08:00

106 lines
6.3 KiB
Python

"""Inglehart-Welzel cultural zones: country -> zone taxonomy shared by every map.
Two consumers now (the instrument showcase maps and the WVS map), and the assignment is
hand-curated research data, so it lives here once rather than drifting between copies. Membership is
VALUE-based, not geographic (Inglehart-Welzel), so several placements are judgment calls -- flagged
inline. Source: WVS Findings + en.wikipedia.org/wiki/Inglehart-Welzel_cultural_map_of_the_world.
Fuzziest calls (noted so a reader can override): ex-Soviet Muslim-majority states (Azerbaijan,
Kazakhstan, Kyrgyzstan, Tajikistan) -> African-Islamic on religion, not Orthodox on history;
Philippines -> Latin America; Hungary -> Catholic Europe; Cyprus/Armenia/Georgia -> Orthodox.
-- authored by Claude
"""
from __future__ import annotations
from loguru import logger
IW_ZONE = {
# English-Speaking
"United States": "English-Speaking", "Great Britain": "English-Speaking",
"Australia": "English-Speaking", "Canada": "English-Speaking", "New Zealand": "English-Speaking",
"Ireland": "English-Speaking", "Northern Ireland": "English-Speaking",
# Protestant Europe
"Germany": "Protestant Europe", "Sweden": "Protestant Europe", "Norway": "Protestant Europe",
"Denmark": "Protestant Europe", "Netherlands": "Protestant Europe", "Finland": "Protestant Europe",
"Switzerland": "Protestant Europe", "Iceland": "Protestant Europe", "Austria": "Protestant Europe",
# Catholic Europe
"France": "Catholic Europe", "Belgium": "Catholic Europe", "Italy": "Catholic Europe",
"Spain": "Catholic Europe", "Poland": "Catholic Europe", "Portugal": "Catholic Europe",
"Croatia": "Catholic Europe", "Czechia": "Catholic Europe", "Slovakia": "Catholic Europe",
"Slovenia": "Catholic Europe", "Hungary": "Catholic Europe", "Andorra": "Catholic Europe",
# Orthodox / Ex-Communist
"Russia": "Orthodox", "Ukraine": "Orthodox", "Bulgaria": "Orthodox", "Serbia": "Orthodox",
"Greece": "Orthodox", "Romania": "Orthodox", "Bosnia Herzegovina": "Orthodox", "Belarus": "Orthodox",
"Georgia": "Orthodox", "Armenia": "Orthodox", "Montenegro": "Orthodox", "North Macedonia": "Orthodox",
"Moldova": "Orthodox", "Cyprus": "Orthodox", "Albania": "Orthodox",
# Baltic
"Estonia": "Baltic", "Latvia": "Baltic", "Lithuania": "Baltic",
# Confucian
"Japan": "Confucian", "China": "Confucian", "South Korea": "Confucian", "Hong Kong SAR": "Confucian",
"Taiwan ROC": "Confucian", "Vietnam": "Confucian", "Singapore": "Confucian", "Macau SAR": "Confucian",
"Mongolia": "Confucian",
# Latin America
"Argentina": "Latin America", "Chile": "Latin America", "Colombia": "Latin America",
"Mexico": "Latin America", "Peru": "Latin America", "Brazil": "Latin America",
"Ecuador": "Latin America", "Philippines": "Latin America", "Bolivia": "Latin America",
"Guatemala": "Latin America", "Nicaragua": "Latin America", "Puerto Rico": "Latin America",
"Uruguay": "Latin America", "Venezuela": "Latin America",
# African-Islamic
"Egypt": "African-Islamic", "Kenya": "African-Islamic", "Morocco": "African-Islamic",
"Nigeria": "African-Islamic", "Saudi Arabia": "African-Islamic",
"United Arab Emirates": "African-Islamic", "Turkey": "African-Islamic", "Iran": "African-Islamic",
"Indonesia": "African-Islamic", "Malaysia": "African-Islamic", "South Africa": "African-Islamic",
"Jordan": "African-Islamic", "Iraq": "African-Islamic", "Lebanon": "African-Islamic",
"Libya": "African-Islamic", "Tunisia": "African-Islamic", "Ethiopia": "African-Islamic",
"Zimbabwe": "African-Islamic", "Azerbaijan": "African-Islamic", "Kazakhstan": "African-Islamic",
"Kyrgyzstan": "African-Islamic", "Tajikistan": "African-Islamic",
# South Asia
"India": "South Asia", "Pakistan": "South Asia", "Thailand": "South Asia",
"Bangladesh": "South Asia", "Maldives": "South Asia", "Myanmar": "South Asia",
}
# raw string (as it appears in a data file) -> canonical IW_ZONE key. Covers ISO2 codes
# (big5/16pf), a "Columbia" typo (mfq2/mfv), a "&" spelling and WVS SAR names, and one corrupt row
# (`None` = deliberately excluded from hulls, surfaced by a warning; anything NOT here and NOT a
# canonical key KeyErrors, so a real normalization bug fails loud).
_CANON = {
"AE": "United Arab Emirates", "AU": "Australia", "BR": "Brazil", "CA": "Canada", "CN": "China",
"DE": "Germany", "DK": "Denmark", "EC": "Ecuador", "ES": "Spain", "FI": "Finland", "FR": "France",
"GB": "Great Britain", "GR": "Greece", "HK": "Hong Kong SAR", "HR": "Croatia", "ID": "Indonesia",
"IE": "Ireland", "IN": "India", "IT": "Italy", "MX": "Mexico", "MY": "Malaysia",
"NL": "Netherlands", "NO": "Norway", "NZ": "New Zealand", "PH": "Philippines", "PK": "Pakistan",
"PL": "Poland", "RO": "Romania", "SE": "Sweden", "SG": "Singapore", "TH": "Thailand",
"TR": "Turkey", "US": "United States", "ZA": "South Africa",
"Columbia": "Colombia", "UAE": "United Arab Emirates", "Bosnia & Herzegovina": "Bosnia Herzegovina",
"(nu": None, # corrupt big5 row (n=369); country unidentifiable from the aggregate CSV
}
# The named outliers on the Economist chart, bolded on our maps where present.
ECONOMIST_OUTLIERS = {"China", "South Korea", "United States", "Great Britain", "Japan",
"Nigeria", "Pakistan", "Sweden"}
def zone_of(country: str) -> str | None:
"""IW zone of a verbatim country string, or None for a known-corrupt row. KeyErrors (fail loud)
on an unrecognised country so a normalization bug can't silently drop it from its zone."""
canon = _CANON.get(country, country)
return None if canon is None else IW_ZONE[canon]
def zones_for(countries: list[str]) -> tuple[dict[str, list[str]], set[str]]:
"""Group verbatim country strings by IW zone + the subset to emphasize (Economist outliers).
Known-corrupt rows are dropped with a warning; unrecognised countries KeyError via zone_of."""
groups: dict[str, list[str]] = {}
dropped: list[str] = []
emph: set[str] = set()
for c in countries:
z = zone_of(c)
if z is None:
dropped.append(c)
continue
groups.setdefault(z, []).append(c)
if _CANON.get(c, c) in ECONOMIST_OUTLIERS:
emph.add(c)
if dropped:
logger.warning(f"excluded known-unmapped countries from zone hulls: {dropped}")
return groups, emph