Where demographic weight does not match economic influence
Published
Nov 25, 2026
Keywords
divergence
Summary
The gap between a country’s share of the world’s population and its share of the global economy highlights a profound divergence. Some nations host large populations yet hold only a small fraction of global economic power, while others exert outsized influence relative to their demographic weight.
Code
# Libraries# =====================================================================import osfrom io import BytesIOimport requestsimport wbgapi as wbimport pandas as pdimport numpy as npimport seaborn as snsimport matplotlib.pyplot as pltimport matplotlib.image as mpimgimport matplotlib.ticker as tickerfrom matplotlib.offsetbox import OffsetImage, AnnotationBbox# Data Extraction (Countries)# =====================================================================# Extract JSON and bring data to a dataframeurl ='https://raw.githubusercontent.com/guillemmaya92/world_map/main/Dim_Country.json'response = requests.get(url)data = response.json()df = pd.DataFrame(data)df = pd.DataFrame.from_dict(data, orient='index').reset_index()df_countries = df.rename(columns={'index': 'ISO3'})# Data Extraction - WBD (2024)# ========================================================#Parametroparameters = ['LP', 'NGDPD']# Create an empty listrecords = []# Iterar sobre cada parámetrofor parameter in parameters:# Request URL url =f"https://www.imf.org/external/datamapper/api/v1/{parameter}" response = requests.get(url) data = response.json() values = data.get('values', {})# Iterate over each country and yearfor country, years in values.get(parameter, {}).items():for year, value in years.items(): records.append({'parameter': parameter,'iso3': country,'year': int(year),'value': float(value) })# Create dataframedf = pd.DataFrame(records)# Pivot Parameter to columns and filter nullsdf = df.pivot(index=['iso3', 'year'], columns='parameter', values='value').reset_index()# Add URSS 1980urss = {'iso3': 'SUN', 'year': 1980, 'LP': 264, 'NGDPD': 354}df = pd.concat([df, pd.DataFrame([urss])], ignore_index=True)# Filter after 2024df = df[df['year'] ==2024]df = df.dropna(subset=['LP', 'NGDPD'])# Data Manipulation# =====================================================================# Merge queriesdf = df.merge(df_countries, how='left', left_on='iso3', right_on='ISO3')df = df[(df['Country'].notna()) | (df['iso3'] =='SUN')]df = df[['iso3', 'Country', 'year', 'LP', 'NGDPD']]df.columns = df.columns.str.lower()# Add calculated fieldsdf['lp_percent'] = df['lp'] / df['lp'].sum()df['ngdpd_percent'] = df['ngdpd'] / df['ngdpd'].sum()df['gap'] = df['ngdpd_percent'] - df['lp_percent']# Ordenar por gapdf = df.sort_values(by='gap', ascending=True)# Calculate cumulative and left positionsdf['lp_cum'] = df['lp'].cumsum()df['lp_per'] = df['lp'] / df['lp'].sum()df['lp_cum_per'] = df['lp_cum'] / df['lp_cum'].max()df['left'] = df['lp_cum_per'] - df['lp_per']print(df)# Data Visualization# =====================================================================# Font and styleplt.rcParams.update({'font.family': 'sans-serif', 'font.sans-serif': ['Franklin Gothic'], 'font.size': 9})sns.set(style="white", palette="muted")# Create figurefig, ax = plt.subplots(figsize=(10, 6))# Create a palettepalette = sns.color_palette("coolwarm", as_cmap=True).reversed()gdp_min =-0.05gdp_max =0.05norm = plt.Normalize(gdp_min, gdp_max)colors = palette(norm(df['gap']))# Barplotbars = plt.bar( df['left'], df['gap'], width=df['lp_per'], # lp_percent asumido como lp_per alpha=1, align='edge', edgecolor='grey', color=colors, linewidth=0.1)# Add title and subtitlefig.add_artist(plt.Line2D([0.085, 0.085], [0.87, 0.97], linewidth=6, color='#203764', solid_capstyle='butt'))plt.text(0.02, 1.13, f'A Huge Democratic Divergence', fontsize=16, fontweight='bold', ha='left', transform=plt.gca().transAxes)plt.text(0.02, 1.09, f'Where demographic weight does not match economic influence', fontsize=11, color='#262626', ha='left', transform=plt.gca().transAxes)plt.text(0.02, 1.05, f'(difference between economic share and population share)', fontsize=9, color='#262626', ha='left', transform=plt.gca().transAxes)# Remove spinesax = plt.gca()ax.spines['top'].set_visible(False)ax.spines['right'].set_visible(False)ax.spines['bottom'].set(color='gray', linewidth=1)ax.spines['left'].set_linewidth(False)# Configurationplt.ylim(-0.25, 0.25)plt.xlim(0, df['lp_cum_per'].max())ax.yaxis.set_major_locator(ticker.MultipleLocator(0.05))ax.xaxis.set_major_locator(ticker.MultipleLocator(0.1))plt.grid(axis='y', linestyle='--', linewidth=0.5, color='lightgray')plt.xlabel('Cumulative Global Population (%)', fontsize=10, fontweight='bold')plt.ylabel('GAP GDP-Population Share', fontsize=10, fontweight='bold')plt.tick_params(axis='x', labelsize=9)plt.tick_params(axis='y', labelsize=9)plt.gca().xaxis.set_major_formatter(ticker.FuncFormatter(lambda x, _: f'{int(x*100):,}%'))plt.gca().yaxis.set_major_formatter(ticker.FuncFormatter(lambda x, _: f'{round(x*100, 1):.0f}%'))# Define flagsflag_urls = {'IND': 'https://raw.githubusercontent.com/matahombres/CSS-Country-Flags-Rounded/master/flags/IN.png','CHN': 'https://raw.githubusercontent.com/matahombres/CSS-Country-Flags-Rounded/master/flags/CN.png','USA': 'https://raw.githubusercontent.com/matahombres/CSS-Country-Flags-Rounded/master/flags/US.png','IDN': 'https://raw.githubusercontent.com/matahombres/CSS-Country-Flags-Rounded/master/flags/ID.png','PAK': 'https://raw.githubusercontent.com/matahombres/CSS-Country-Flags-Rounded/master/flags/PK.png','NGA': 'https://raw.githubusercontent.com/matahombres/CSS-Country-Flags-Rounded/master/flags/NG.png','BRA': 'https://raw.githubusercontent.com/matahombres/CSS-Country-Flags-Rounded/master/flags/BR.png','RUS': 'https://raw.githubusercontent.com/matahombres/CSS-Country-Flags-Rounded/master/flags/RU.png','MEX': 'https://raw.githubusercontent.com/matahombres/CSS-Country-Flags-Rounded/master/flags/MX.png','JPN': 'https://raw.githubusercontent.com/matahombres/CSS-Country-Flags-Rounded/master/flags/JP.png','SUN': 'https://raw.githubusercontent.com/guillemmaya92/circle_flags/refs/heads/gh-pages/flags/su.png',}# Load flags onceflags = {country: mpimg.imread(BytesIO(requests.get(url).content)) for country, url in flag_urls.items()}# For each bar, add the corresponding flag and country namefor bar, iso3 inzip(bars, df['iso3']):if iso3 in flags: img = flags[iso3] imagebox = OffsetImage(img, zoom=0.021) x = bar.get_x() + bar.get_width() /2 offset =0.005 text_offset =0.025# Positioning logicif bar.get_height() >=0: y_flag = bar.get_height() + offset box_align = (0.5, 0) text_y = y_flag + text_offset va_text ='bottom'else: y_flag = bar.get_height() - offset box_align = (0.5, 1) text_y = y_flag - text_offset va_text ='top'# Add flag ab = AnnotationBbox(imagebox, (x, y_flag), frameon=False, box_alignment=box_align) ax.add_artist(ab)# Add country name country_name = df.loc[df['iso3'] == iso3, 'iso3'].values[0] ax.text(x, text_y, country_name, ha='center', va=va_text, fontsize=6, fontweight='bold', color='#404040')# Add Year labelplt.text(1, 1.15, f'{df['year'].max()}', transform=plt.gca().transAxes, fontsize=22, ha='right', va='top', fontweight='bold', color='#D3D3D3', bbox=dict(facecolor='white', edgecolor='none', boxstyle='square,pad=0.3'))# Add label "Underrepresented" and "Overrepresented"plt.text(0, -0.1, 'Under-represented', transform=ax.transAxes, fontsize=10, fontweight='bold', color='darkred', ha='left', va='center')plt.text(0.88, -0.1, 'Over-represented', transform=ax.transAxes, fontsize=10, fontweight='bold', color='darkblue', va='center')# Add Data Sourceplt.text(0, -0.15, 'Data Source:', transform=plt.gca().transAxes, fontsize=8, fontweight='bold', color='gray')space =" "*23plt.text(0, -0.15, space +'IMF World Economic Outlook Database, 2024', transform=plt.gca().transAxes, fontsize=8, color='gray')# Adjust layoutplt.tight_layout()# Save it...max_year = df['year'].max()download_folder = os.path.join(os.path.expanduser("~"), "Downloads")filename = os.path.join(download_folder, f"FIG_IMG_GAP_GDP_Population_{max_year}.png")plt.savefig(filename, dpi=300, bbox_inches='tight')# Show :)plt.show()
Economic-Population Gap
Difference between economic share and population share.