outcome_vars = {
'life_expectancy': ('Life expectancy (years)', False),
'infant_mortality': ('Infant mortality (per 1000)', True),
'homicide_rate': ('Homicides (per 100k)', True),
}
if not spirit_data.empty and 'gini_disp' in spirit_data.columns:
available = {k: v for k, v in outcome_vars.items() if k in spirit_data.columns}
n_plots = len(available)
if n_plots > 0:
fig, axes = plt.subplots(1, n_plots, figsize=(6 * n_plots, 5))
if n_plots == 1:
axes = [axes]
for ax, (var, (label, higher_is_worse)) in zip(axes, available.items()):
plot_df = spirit_data.dropna(subset=['gini_disp', var])
if plot_df.empty:
continue
colors = [country_color(c) if c in COUNTRY_NAMES else '#999'
for c in plot_df['country_code']]
ax.scatter(plot_df['gini_disp'], plot_df[var],
c=colors, s=80, zorder=5, edgecolors='white', linewidth=0.5)
# Regression line
x = plot_df['gini_disp'].values
y = plot_df[var].values
mask = np.isfinite(x) & np.isfinite(y)
if mask.sum() > 3:
slope, intercept, r, p, se = stats.linregress(x[mask], y[mask])
x_line = np.linspace(x[mask].min(), x[mask].max(), 100)
ax.plot(x_line, intercept + slope * x_line,
color='gray', linestyle='--', alpha=0.7)
ax.text(0.05, 0.95, f'R\u00b2 = {r**2:.2f}\np = {p:.3f}',
transform=ax.transAxes, fontsize=9, va='top',
bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))
annotate_countries(ax, plot_df['gini_disp'].values,
plot_df[var].values,
plot_df['country_code'].values)
ax.set_xlabel('Gini (disposable income)')
ax.set_ylabel(label)
direction = 'worse \u2192' if higher_is_worse else '\u2190 better'
ax.set_title(f'Inequality vs. {label.split("(")[0].strip()}')
fig.suptitle('The Spirit Level: Inequality and Social Outcomes (~2019)',
fontsize=14, y=1.02)
plt.tight_layout()
save_figure(fig, '05_spirit_level_small_multiples')
plt.show()
else:
print('No outcome variables available in the data.')
else:
print('Spirit Level data or Gini column not available.')