Migraciones

Un caso práctico de análisis exploratorio con datos de poblacionales




Roque Leal

DataScience

Introducción

Durante los últimos diez años, los países desarrollados han experimentado una variedad de problemas relacionados con la migración. La Organización Internacional para las Migraciones (OIM) declaró en la Agenda 2030 para el Desarrollo Sostenible una serie de lineamientos que contribuyen directamente a muchos Objetivos de Desarrollo Sostenible (ODS). Por ejemplo:

  • 10: Reducir las desigualdades
  • 10.7: Políticas de migración bien administradas
  • 10.c: Remesas.

Investigadores de todo el mundo buscan respuestas y soluciones para colaborar con estos lineamientos. En esta caso el papel de los científicos de datos, consiste en aplicar diferentes algoritmos para identificar causas raíz y hacer predicciones. Por ello, en este caso particular el presente estudio intenta colaborar en responder la siguiente pregunta:

"Cuáles son las posibles causas de la migración y cómo la migración evolucionará con el tiempo".

Para encontrar respuestas, se realizará una exploración de la migración a nivel global y un caso particular de la migración en América del Sur. En general, el estudio actual se puede tratar como análisis de datos exploratorios. Es decir, se explorarán los conjuntos de datos sobre la correlación y se construirán algunos modelos de regresión. Para el desarrollo de las predicciones se consideran los problemas y limitaciones de dicho análisis (falsa correlación, multicolinealidad, etc.) sin embargo, los resultados obtenidos se compararán con los resultados del modelo System Dynamics realizado por Wigman (2018).

Desde el punto de vista práctico, este Jupyter Notebook consta de los siguientes pasos. Primero, temas relacionados con los conjuntos de datos descargados y preprocesados. En segundo lugar, los resultados se trazan para obtener algunas ideas. En tercer lugar, se aplicaran un par de algoritmos para identificar relaciones y predecir flujos de migración futuros. Y finalmente, una comparación con los resultados del modelo System Dynamics realizado.

Para responder a la pregunta de investigación establecida se utilizaron 4 conjuntos de datos, a saber,

  1. Naciones Unidas Stock internacional de migrantes 2015 (Naciones Unidas, 2015),
  2. Migración neta del Banco Mundial (Banco Mundial, 2018),
  3. Índice de Estado Frágil (Fondo para la Paz, 2018),
  4. Resultados de la simulación de System Dynamics (Wigman, 2018).

El primero servirá principalmente para fines de visualización. Los valores del segundo servirán como variables predictivas (y), mientras que el Índice de estado frágil como predictores (x). El cuarto a efectos comparativos.

Finalmente, se utilizó Natural Earth para unsar los shapefiles y trazar los datos en el mapa mundial.

Exploración de los datos

Naciones Unidas Stock internacional de migrantes 2015

Este conjunto de datos no es más que datos de migración por país de origen y destino. Se descargó de las Naciones Unidas (2015) como un archivo Excel en formato xlsx. Los datos sobre diferentes años se dividen en hojas de Excel separadas. Las filas representan región, subregión o país de destino, mientras que las columnas son países de origen. El archivo original se modificó ligeramente, por ejemplo, los encabezados se dividieron para evitar problemas con el índice múltiple.

Desafortunadamente, este conjunto de datos tiene un número bastante limitado de puntos de datos y, por lo tanto, difícilmente puede usarse para algoritmos. Sin embargo, se puede utilizar para visualizar dinámicas de migración. Igualmente se descartó utilizar los datos preliminares para el 2017, ya que para el caso práctico de América del Sur es evidente el ruido en los datos para Venezuela debido al propio ajuste que debe realizarse.

Load the data

In [102]:
import numpy as np
import matplotlib.pyplot as plt
plt.style.use('ggplot')
%matplotlib inline
import seaborn as sns
import pandas as pd
import geopandas as gpd
from sklearn import preprocessing
In [103]:
import time

# Almacenaremos todas las hojas para varios años como un dictado de pandas para un fácil acceso por año
sheet_name = "Table "  # Nombre original de la hoja 
sheet_numbers = [1, 4, 7, 10, 13, 16]  # Número de hojas
years = [1990, 1995, 2000, 2005, 2010, 2015]  # Años disponibles
n = len(sheet_numbers)  # Número de datos disponibles

my_dict = {}  # estructura de los datos 
# {'1990' : df_1990, '1995' : df_1995 ...}

path = "data/UN_MigrantStockByOriginAndDestination_2015.xlsx"

start_time = time.time()
for i in range(n):
    sheet_name = "Table " + str(sheet_numbers[i])
    my_dict[years[i]] = pd.read_excel(path, sheet_name=sheet_name, header=[15])
    print(str(years[i]) + " datos cargados")
elapsed_time = time.time() - start_time

print(str(round(elapsed_time, 2)) + " tiempo")
1990 datos cargados
1995 datos cargados
2000 datos cargados
2005 datos cargados
2010 datos cargados
2015 datos cargados
36.26 tiempo
In [104]:
# Datos
my_dict[2010].head()
Out[104]:
Sort order Major area, region, country or area of destination Notes Country code Type of data (a) Total Other North Other South Afghanistan Albania ... Uruguay Uzbekistan Vanuatu Venezuela (Bolivarian Republic of) Viet Nam Wallis and Futuna Islands Western Sahara Yemen Zambia Zimbabwe
0 1 WORLD NaN 900 NaN 221714243 2077453.0 7964398.0 5003276.0 1110296.0 ... 332362.0 1913257.0 7939.0 550420.0 2306669.0 7461.0 91965.0 880927.0 209456.0 666407.0
1 2 Developed regions (b) 901 NaN 132560325 516607.0 4390410.0 395566.0 1105788.0 ... 171734.0 1467082.0 2243.0 446524.0 2008501.0 1.0 58.0 60669.0 61985.0 223434.0
2 3 Developing regions (c) 902 NaN 89153918 1560846.0 3573988.0 4607710.0 4508.0 ... 160628.0 446175.0 5696.0 103896.0 298168.0 7460.0 91907.0 820258.0 147471.0 442973.0
3 4 Least developed countries (d) 941 NaN 10018128 272306.0 905106.0 0.0 0.0 ... 527.0 1199.0 71.0 4616.0 77715.0 0.0 0.0 358.0 59408.0 79972.0
4 5 Less developed regions excluding least develop... NaN 934 NaN 79135790 1288540.0 2668882.0 4607710.0 4508.0 ... 160101.0 444976.0 5625.0 99280.0 220453.0 7460.0 91907.0 819900.0 88063.0 363001.0

5 rows × 240 columns

Pre-procesamiento

In [105]:
for key, value in my_dict.items():
    # Se ajustan nombres de las columnas
    s = my_dict[key].columns.values
    s[1] = 'Destination'
    my_dict[key].columns = s

    # Se eliminan datos irrelevantes
    my_dict[key] = my_dict[key].drop(
        ['Sort\norder', 'Notes', 'Country code', 'Type of data (a)'], axis=1)
    my_dict[key] = my_dict[key].drop([0, 1, 2, 3, 4, 5]).reset_index(drop=True)

    # Datos Nulos
    my_dict[key] = my_dict[key].fillna(0)
In [106]:
# Data
my_dict[2010].head()
Out[106]:
Destination Total Other North Other South Afghanistan Albania Algeria American Samoa Andorra Angola ... Uruguay Uzbekistan Vanuatu Venezuela (Bolivarian Republic of) Viet Nam Wallis and Futuna Islands Western Sahara Yemen Zambia Zimbabwe
0 Africa 16840014 358625.0 1053239.0 594.0 509.0 33269.0 0.0 0.0 403031.0 ... 91.0 440.0 0.0 270.0 128.0 0.0 91905.0 29046.0 146579.0 442484.0
1 Eastern Africa 4657063 98293.0 371707.0 0.0 0.0 0.0 0.0 0.0 38633.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 358.0 89462.0 79904.0
2 Burundi 235259 2660.0 16897.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
3 Comoros 12618 602.0 761.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
4 Djibouti 101575 1518.0 4558.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 240.0 0.0 0.0

5 rows × 236 columns

In [107]:
from copy import deepcopy


# Se estructuran los datos agrupandolos y ordenandolos para una mejor manipulación
# Para evitar problemas de columnas dejaremos el nombre de los datos tal cual como se presentan en idioma Inglés

df = deepcopy(my_dict[2010])

regions = ['Africa', 'Asia', 'Europe',
           'Latin America and the Caribbean', 'Northern America', 'Oceania']

subregions = {regions[0]: ['Eastern Africa', 'Middle Africa', 'Northern Africa', 'Southern Africa', 'Western Africa'],
              regions[1]: ['Central Asia', 'Eastern Asia', 'South-Eastern Asia', 'Southern Asia', 'Western Asia'],
              regions[2]: ['Eastern Europe', 'Northern Europe', 'Southern Europe', 'Western Europe'],
              regions[3]: ['Caribbean', 'Central America', 'South America'],
              regions[4]: [],
              regions[5]: ['Australia and New Zealand',
                           'Melanesia', 'Micronesia', 'Polynesia']
              }

countries = {}  # Clave para las regiones y subregiones 

for key in subregions:
    for i in range(0, len(subregions[key])):
        if (i == len(subregions[key]) - 1):
            first_country = df[df.Destination ==
                               subregions[key][i]].index.values[0] + 1
            if (key != 'Oceania'):
                next_region = regions.index(key) + 1
                last_country = df[df.Destination ==
                                  regions[next_region]].index.values[0]
            else:
                last_country = len(df)

        else:
            # primer pais
            first_country = df[df.Destination ==
                               subregions[key][i]].index.values[0] + 1
            last_country = df[df.Destination == subregions[key]
                              [i + 1]].index.values[0]  # último país de la región

        l = []  # lista de paises dentro de la region

        for k in range(first_country, last_country):
            l.append(df.Destination[k])

        countries[subregions[key][i]] = l  
In [108]:
# Modificamos el nombre d elas filas
for key, value in my_dict.items() :
    my_dict[key].index = my_dict[key].Destination
    my_dict[key] = my_dict[key].drop(columns='Destination')
In [109]:
# Datos
my_dict[2010].head()
Out[109]:
Total Other North Other South Afghanistan Albania Algeria American Samoa Andorra Angola Anguilla ... Uruguay Uzbekistan Vanuatu Venezuela (Bolivarian Republic of) Viet Nam Wallis and Futuna Islands Western Sahara Yemen Zambia Zimbabwe
Destination
Africa 16840014 358625.0 1053239.0 594.0 509.0 33269.0 0.0 0.0 403031.0 0.0 ... 91.0 440.0 0.0 270.0 128.0 0.0 91905.0 29046.0 146579.0 442484.0
Eastern Africa 4657063 98293.0 371707.0 0.0 0.0 0.0 0.0 0.0 38633.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 358.0 89462.0 79904.0
Burundi 235259 2660.0 16897.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
Comoros 12618 602.0 761.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
Djibouti 101575 1518.0 4558.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 240.0 0.0 0.0

5 rows × 235 columns

In [110]:
from pprint import pprint


def get_countries_from_region(region, subregions, countries):
    """List all countries within a given region

    Keyword arguments:
    region -- region of interest
    subregions -- dict with regions and subregions
    countries -- dict with subregions and countries
    """
    res = []
    for subregion in subregions[region]:
        res.append(countries[subregion])
    res = [item for sublist in res for item in sublist]
    return res
# Si todo sale bien deberia imprimir la lista de paises según región y subregión
In [111]:
America = get_countries_from_region("Latin America and the Caribbean", subregions, countries)
pprint(America[:5])
['Anguilla', 'Antigua and Barbuda', 'Aruba', 'Bahamas', 'Barbados']
In [112]:
# Extraemos los datos de migraciones para los años de estudio para Colombia

def extract_temporal_data(country, my_dict, years=[1990, 1995, 2000, 2005, 2010, 2015]):
    """Extract total migration values for a given time frame 

    Keyword arguments
    country -- country of interest
    years -- years under consideration
    my_dict -- data set that contains all migration data
    """
    res = []
    for year in years:
        res.append(my_dict[year].loc[country, "Total"])
    return res


extract_temporal_data("Colombia", my_dict)
Out[112]:
[104277.0, 106943.0, 109609.0, 107612.0, 124271, 133134]
In [113]:
# Extraemos los datos de migraciones para los años de estudio para Ecuador
def extract_temporal_data(country, my_dict, years=[1990, 1995, 2000, 2005, 2010, 2015]):
    """Extract total migration values for a given time frame 

    Keyword arguments
    country -- country of interest
    years -- years under consideration
    my_dict -- data set that contains all migration data
    """
    res = []
    for year in years:
        res.append(my_dict[year].loc[country, "Total"])
    return res


extract_temporal_data("Ecuador", my_dict)
Out[113]:
[78663.0, 115093.0, 151523.0, 187404.0, 325366, 387513]
In [114]:
# Graficamos los valores de migración
def bar_plot(origin, destination, year, my_dict, countries, figsize=(8, 6)):
    """Plot migration as a bar plot where bars are contries of origin 
    and y axis is values for specified country of destination 

    Keyword arguments:
    origin -- country or list of countries of origin
    destination -- country of destination
    year -- year under consideration
    my_dict -- data set that contains all migration data
    countries -- dict with subregions and countries
    figsize -- size of the figure if should be adjusted
    """

    # Select data to plot
    X = my_dict[year].loc[destination, countries[origin]]

    # Set up plot parameters
    fig, ax = plt.subplots(figsize=figsize)
    plt.style.use('ggplot')

    # Set tick labels
    ax.set_xticks(list(range(1, len(X) + 1)))
    ax.set_xticklabels(countries[origin])
    ax.xaxis.set_tick_params(rotation=45)

    # Set up title and labels
    title = "Migración desde " + origin + \
        " a " + destination + " en " + str(year)
    ax.set_title(title)
    ax.set_ylabel("Número de personas")
    ax.set_xlabel("Paises de Origen")
    ax.bar(list(range(1, len(X) + 1)), X)
In [115]:
bar_plot("South America", 'Colombia', 2015, my_dict, countries)
In [116]:
#
time_frame = [1990, 1995, 2000, 2005, 2010, 2015]
plt.title("Migración Total a Colombia")
plt.xlabel("Año")
plt.ylabel("Personas")
plt.bar(time_frame, extract_temporal_data("Colombia", my_dict))
Out[116]:
<BarContainer object of 6 artists>
In [117]:
#
time_frame = [1990, 1995, 2000, 2005, 2010, 2015]
plt.title("Migración Total a Ecuador")
plt.xlabel("Año")
plt.ylabel("Personas")
plt.bar(time_frame, extract_temporal_data("Ecuador", my_dict))
Out[117]:
<BarContainer object of 6 artists>
In [118]:
#
time_frame = [1990, 1995, 2000, 2005, 2010, 2015]
plt.title("Migración Total a Venezuela")
plt.xlabel("Año")
plt.ylabel("Personas")
plt.bar(time_frame, extract_temporal_data("Venezuela (Bolivarian Republic of)", my_dict))
Out[118]:
<BarContainer object of 6 artists>
In [119]:
def line_chart(origin, destination, my_dict,
               years=[1990, 1995, 2000, 2005, 2010, 2015], figsize=(8, 6)):
    """Plot migration as a line chart within a given time frame

    Keyword arguments:
    origin -- country or list of countries of origin
    destination -- country of destination
    years -- years under consideration
    my_dict -- data set that contains all migration data
    figsize -- size of the figure if should be adjusted
    """

    X = []

    # Extraemos los datos a graficar
    for year in years:
        X.append(year)
        X.append(my_dict[year].loc[destination, origin])

    X = np.array(X)
    sh = (len(years), 2)
    X = X.reshape(sh)

    # Configuramos parámetros
    fig, ax = plt.subplots(figsize=figsize)
    plt.style.use('ggplot')

    # Configuramos titulos
    title = "Migración desde " + origin + " a " + destination + \
        " Desde " + str(years[0]) + " a " + str(years[-1])
    ax.set_title(title)
    ax.set_ylabel("Número de personas migradas")
    ax.set_xlabel("Año")
    ax.plot(X[:, 0], X[:, 1])
In [120]:
line_chart("Venezuela (Bolivarian Republic of)", "Colombia", my_dict)
line_chart("Colombia", "Venezuela (Bolivarian Republic of)", my_dict)
In [121]:
line_chart("Venezuela (Bolivarian Republic of)", "Ecuador", my_dict)
line_chart("Ecuador", "Venezuela (Bolivarian Republic of)", my_dict)
In [122]:
# Graficamos la migración total para la región desde un determinado país
def lines_chart(origins, destination, my_dict,
                years=[1990, 1995, 2000, 2005, 2010, 2015], figsize=(8, 6)):
    """Plot single or multiple line cahrts representing migration 
    from origin(-s) to destination within a given time frame

    Keyword arguments:
    origins -- country or countries of origin
    destination -- country of destination
    years -- years under consideration
    my_dict -- data set that contains all migration data
    figsize -- size of the figure if should be adjusted
    """

    x = []
    # Extraemos los datos a graficar
    for origin in origins:
        X = []
        for year in years:
            X.append(year)
            X.append(my_dict[year].loc[destination, origin])
        x.append(X)

    for i in range(len(x)):
        x[i] = np.array(x[i])
        sh = (len(years), 2)
        x[i] = x[i].reshape(sh)

    # Configuramos los parámetros
    fig, ax = plt.subplots(figsize=figsize)
    plt.style.use('ggplot')

    # Configuramos los titulos
    title = "Migración a " + destination + " Desde " + \
        str(years[0]) + " to " + str(years[-1])
    ax.set_title(title)
    ax.set_ylabel("Número de personas Migradas")
    ax.set_xlabel("Año")
    for i in range(len(x)):
        ax.plot(x[i][:, 0], x[i][:, 1])
    ax.legend(origins)
In [123]:
lines_chart(countries["South America"], "Venezuela (Bolivarian Republic of)", my_dict)

Heatmap y Clúster Map

Para obtener una mejor visión general de alto nivel, se pueden utilizar dos formas de visualización de datos: Heatmap y mapa de Clúster Map. Para identificar los posibles grupos de países (según sus valores de migración). Una vez identificados los es posible identificar los responsables de la formulación de políticas a manera de establecer coaliciones o asociaciones.

In [124]:
# Hacemos un Heatmap por regiones
def migration_heatmap(origin, destination, year, my_dict, subregions, countries, scale=True):
    """Plot migration values as a heatmap

    Keyword arguemnts:
    origin -- region of origin (Asia, for example)
    destination -- region of destination (Europe, for example)
    year -- year of interest
    my_dict -- data set that contains all migration data
    subregions -- subregions dict created earlier
    countries -- countires dict created earlier
    scale -- make scaling or not
    """
    a = get_countries_from_region(origin, subregions, countries)
    b = get_countries_from_region(destination, subregions, countries)

    # Ajustamos el tamaño
    fig_size = plt.rcParams["figure.figsize"]
    fig_size[0] = 12
    fig_size[1] = 9
    plt.rcParams["figure.figsize"] = fig_size

    X = my_dict[year].loc[b, a]

    if (scale == True):
        # Escalamos para una mejor representación
        min_max_scaler = preprocessing.MinMaxScaler(feature_range=(0, 1))
        X_train_minmax = min_max_scaler.fit_transform(X)
        xticks = X.columns.values
        yticks = X.index.values
        sns.heatmap(X_train_minmax, xticklabels=xticks,
                    yticklabels=yticks)
    else:
        sns.heatmap(X)
In [125]:
migration_heatmap("Latin America and the Caribbean", "Europe", 2015, my_dict, subregions, countries)

Un patrón interesante se puede observar aquí. Hay un par de países que están tomando un número significativo de inmigrantes de América del Sur: España principalmente para los paises americanos de habla castellana y para los paises de habla inglesa las naciones receptoras principalmente figuran Reino Unido y Holanda.

In [126]:
# Hacemos un Clustermap por regiones en este caso continuamos comparando la emigración desde América Latina a Europa

def migration_clustermap(origin, destination, year, my_dict, subregions, countries, scale = True) : 
    """Plot migration values as a clustermap
    
    Keyword arguemnts:
    origin -- region of origin (Asia, for example)
    destination -- region of destination (Europe, for example)
    year -- year of interest
    my_dict -- data set that contains all migration data
    subregions -- subregions dict created earlier
    countries -- countires dict created earlier
    scale -- make scaling or not
    """
    a = get_countries_from_region(origin, subregions, countries)
    b = get_countries_from_region(destination, subregions, countries)
    
    # Ajustamos el tamaño
    fig_size = plt.rcParams["figure.figsize"]
    fig_size[0] = 12
    fig_size[1] = 9
    plt.rcParams["figure.figsize"] = fig_size
    
    X = my_dict[year].loc[b, a]
    
    if (scale == True) : 
        # Escalamos para una mejor representación
        min_max_scaler = preprocessing.MinMaxScaler(feature_range=(0, 1))
        X_train_minmax = min_max_scaler.fit_transform(X)
        xticks = X.columns.values
        yticks = X.index.values
        sns.clustermap(X_train_minmax, xticklabels=xticks,
                yticklabels=yticks)
    else : 
        sns.clustermap(X)
In [127]:
migration_clustermap("Latin America and the Caribbean", "Europe", 2015, my_dict, subregions, countries)

Ahora es posible observar cuáles son los grupos de países que experimentan los mayores flujos migratorios. Sin embargo, todos los países de origen estan asociados a su historia colonial. La migración a España, por ejemplo, es mayoritariamente de América Latina, mientras que la migración a Reino Unido e Irlanda proviene de sus extintas colonias, así como tambien sucede entre Brasil y Portugal.

Migración neta del Banco Mundial (Banco Mundial, 2018)

El Banco Mundial es bien conocido por sus conjuntos de datos abiertos. El conjunto de datos de migración neta refleja la cantidad de personas que abandonan o ingresan al país en un año determinado. El conjunto de datos se descargó del Banco Mundial (2018) como archivo Excel en xls fromat. Los datos de todos los años y todos los países se encuentran almacenados en una sola hoja.

Preprocesamiento de los datos

In [128]:
# Origen de los datos
netm_df = pd.read_excel(
    "data/API_SM.POP.NETM_DS2_en_excel_v2_10182252.xls", sheet_name="Data")
In [131]:
# Datos
netm_df.head(5)
Out[131]:
Country Name Country Code Indicator Name Indicator Code 1960 1961 1962 1963 1964 1965 ... 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017
0 Aruba ABW Net migration SM.POP.NETM NaN NaN -4323.0 NaN NaN NaN ... NaN NaN NaN NaN 1253.0 NaN NaN NaN NaN 1004.0
1 Afghanistan AFG Net migration SM.POP.NETM NaN NaN -20000.0 NaN NaN NaN ... NaN NaN NaN NaN 448007.0 NaN NaN NaN NaN -299999.0
2 Angola AGO Net migration SM.POP.NETM NaN NaN -135000.0 NaN NaN NaN ... NaN NaN NaN NaN 87322.0 NaN NaN NaN NaN 0.0
3 Albania ALB Net migration SM.POP.NETM NaN NaN -99.0 NaN NaN NaN ... NaN NaN NaN NaN -93425.0 NaN NaN NaN NaN -40000.0
4 Andorra AND Net migration SM.POP.NETM NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

5 rows × 62 columns

In [132]:
# Preprocesamiento del DataSet
netm_df.index = netm_df["Country Name"]
netm_df = netm_df.drop(columns=["Indicator Name", "Indicator Code"])

# Se eliminan datos irrelevantes
netm_df = netm_df.dropna(axis=1, how='all')
In [133]:
# Observamos el DataSet
netm_df.head()
Out[133]:
Country Name Country Code 1962 1967 1972 1977 1982 1987 1992 1997 2002 2007 2012 2017
Country Name
Aruba Aruba ABW -4323.0 -4275.0 -3537.0 -5470.0 -1921.0 -5194.0 14218.0 6926.0 6263.0 -441.0 1253.0 1004.0
Afghanistan Afghanistan AFG -20000.0 -20000.0 -20000.0 -1092462.0 -3318301.0 -1484185.0 2327473.0 -379474.0 929118.0 -777497.0 448007.0 -299999.0
Angola Angola AGO -135000.0 -226155.0 -80955.0 11455.0 234148.0 -151982.0 142811.0 -126955.0 172594.0 85286.0 87322.0 0.0
Albania Albania ALB -99.0 -99.0 -99.0 -99.0 -99.0 -99.0 -443212.0 -179606.0 -176774.0 -217358.0 -93425.0 -40000.0
Andorra Andorra AND NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

Exploración de los datos

Choropleth

Un mapa coroplético nos brindará una visión espacial del conjunto de datos

In [134]:
# Cargamos los datos
g_df = gpd.read_file(
    "data/geo/ne_10m_admin_0_countries.shp")[['ADM0_A3', 'geometry']].to_crs('+proj=robin')
In [135]:
# Utilizamos un elemento clave para unir el codigo de nombre del país con el shapefile utilizando Geopandas
netm_df = pd.read_excel(
    "data/API_SM.POP.NETM_DS2_en_excel_v2_10182252.xls", sheet_name="Data")
netm_df = netm_df.drop(columns=["Indicator Name", "Indicator Code"])
netm_df = netm_df.dropna(axis=1, how='all')

def plot_map(year):
    """Plot choropleth representing net migration by country. 

    Keyword arguments:
    year -- year of interest
    netm_df -- World Bank net migration data set
    g_df -- shapefiles in geopandas data frame
    """
    year = str(year)
    
    if year not in list(netm_df.columns):
        raise ValueError("This year is unavailable. Please, try another one.")

    clusters = 9  # número de agrupaciones
    cmap = "OrRd"  # Color
    figsize = (16, 10)  # Medidas
    cols = ["Country Name", "Country Code", year]
    title = "Migración Neta en " + year  # Titulo del mapa

    # Conectamos al shapefile
    merged_df = g_df.merge(
        netm_df[["Country Code", year]], left_on="ADM0_A3", right_on="Country Code")

    # Graficamos
    ax = merged_df.dropna().plot(column=year, cmap=cmap,
                                 scheme='equal_interval', figsize=figsize, k=clusters, legend=True)

    # Eliminamos datos irrelevantes
    merged_df[merged_df.isna().any(axis=1)].plot(
        ax=ax, color='#fafafa', hatch='...')

    # Configuramos ejes
    ax.set_title(title, fontdict={'fontsize': 20}, loc='left')
    ax.set_axis_off()
    ax.set_xlim([-1.5e7, 1.7e7])
    ax.get_legend().set_bbox_to_anchor((.12, .4))
    ax.get_figure()
In [136]:
plot_map("2017")

Versión Interactiva

Modificando un poco el código es posible crear una visualización interactiva bajo el esquema de serie de tiempo para ver la evolución de los valores de Migración Neta anuales

In [137]:
from ipywidgets import interactive
years = netm_df.columns[2:]
step = 5
start = int(years[0])
end = int(years[-1])
interactive_plot = interactive(plot_map, year=(start, end, step))
output = interactive_plot.children[-1]
output.layout.height = '550px'
interactive_plot

Heatmap y Clúster Map

In [138]:
# Agregamos los datos para diseñar el Heatmap y el Clústermap 
netm_df = pd.read_excel(
    "data/API_SM.POP.NETM_DS2_en_excel_v2_10182252.xls", sheet_name="Data")
netm_df.index = netm_df["Country Name"]
netm_df = netm_df.drop(
    columns=["Country Name", "Country Code", "Indicator Name", "Indicator Code"])
netm_df.columns = [int(i) for i in list(netm_df.columns)]
netm_df = netm_df.dropna(axis=1, how='all')
netm_df = netm_df.dropna(axis=0, how='all')


def netmigration_chmap(countries, netm_df, flag):
    """Plot net migration values as a heatmap or clustermap

    Keyword arguemnts:
    countries -- list of countries to show
    netm_df -- data set with net migration values
    flag -- 1 if heatmap and 2 if clustermap
    """

    # Ajustamos medidas
    fig_size = plt.rcParams["figure.figsize"]
    fig_size[0] = 12
    fig_size[1] = 9
    plt.rcParams["figure.figsize"] = fig_size

    X = netm_df.loc[countries, :].fillna(0)
    min_max_scaler = preprocessing.MinMaxScaler(feature_range=(0, 1))
    X_train_minmax = min_max_scaler.fit_transform(X)
    xticks = X.columns.values
    yticks = X.index.values
    if flag == 1:
        sns.heatmap(X_train_minmax, xticklabels=xticks,
                    yticklabels=yticks)
    elif flag == 2:
        sns.clustermap(X_train_minmax, xticklabels=xticks,
                       yticklabels=yticks)
In [139]:
netmigration_chmap(America, netm_df, 1)
C:\Program Files (x86)\Microsoft Visual Studio\Shared\Anaconda3_64\lib\site-packages\pandas\core\indexing.py:1472: FutureWarning: 
Passing list-likes to .loc or [] with any missing label will raise
KeyError in the future, you can use .reindex() as an alternative.

See the documentation here:
https://pandas.pydata.org/pandas-docs/stable/indexing.html#deprecate-loc-reindex-listlike
  return self._getitem_tuple(key)

Parece que Chile ha dominado la recepcción del flujo de inmigración

Indice de Estado Frágil

El índice de estado frágil (FSI) es un informe anual realizado por el Fund for Peace del Fondo para la Paz. Este informe se basa en un conjunto de 12 indicadores que se utilizan para caracterizar la vulnerabilidad de los países al conflicto o colapso. Los indicadores se dividen en 4 categorías:

  • cohesión,
  • económico,
  • político,
  • sociales.

Todos los indicadores se nombran de manera que sean intuitivamente comprensibles. Los valores de los indicadores varían en la escala de 0 a 10, donde 0 es absolutamente estable y 10 es altamente vulnerable. Todos los indicadores se suman al puntaje FSI total que se utiliza para la clasificación de los países. Dependiendo de la puntuación los países se dividen en 4 categorías:

  • alerta,
  • advertencia,
  • estable,
  • sostenible.

Hay un proceso complejo detrás del cálculo de estos indicadores. Para más detalles y críticas ver el Fondo para la Paz (2018).

Nuestra hipótesis aquí es que estos indicadores pueden explicar la migración hacia o desde un determinado país, y por lo tanto, pueden usarse para nuestros modelos estadísticos. El conjunto de datos es un conjunto de países e indicadores correspondientes.

Pre-procesamiento

In [140]:
fsi_years = list(range(2006, 2018))  # Años disponibles
file_name = "fsi-"  # archivos
extension = ".xlsx"

fsi_dict = {}  # estructura de los DataSet

# Leemos los archivos en Pandas
for i in range(len(fsi_years)):
    path = "data/" + file_name + str(fsi_years[i]) + extension
    fsi_dict[fsi_years[i]] = pd.read_excel(path)

# Ajustamos los datos por país y año
for key, values in fsi_dict.items():
    fsi_dict[key].index = fsi_dict[key].Country
    fsi_dict[key] = fsi_dict[key].drop(columns=['Country', 'Year'])
In [141]:
# Resultado
fsi_dict[2010].head(5)
Out[141]:
Rank Total C1: Security Apparatus C2: Factionalized Elites C3: Group Grievance E1: Economy E2: Economic Inequality E3: Human Flight and Brain Drain P1: State Legitimacy P2: Public Services P3: Human Rights S1: Demographic Pressures S2: Refugees and IDPs X1: External Intervention
Country
Somalia 1st 114.3 10.0 10.0 9.7 9.6 8.0 8.3 10.0 9.6 9.9 9.6 10.0 9.6
Chad 2nd 113.3 9.9 9.8 9.8 8.5 9.3 8.3 9.9 9.6 9.6 9.4 9.5 9.7
Sudan 3rd 111.8 9.8 9.9 9.9 6.7 9.5 8.7 9.9 9.3 9.9 8.8 9.8 9.6
Zimbabwe 4th 110.2 9.2 9.5 8.8 9.6 9.4 9.7 9.6 9.4 9.5 9.4 8.6 7.5
Congo Democratic Republic 5th 109.9 9.8 8.9 8.6 8.7 9.5 8.0 8.8 9.0 9.4 9.9 9.6 9.7
In [142]:
def extract_temporal_fsi_data(country, fsi_dict, years=list(range(2006, 2018))):
    """Extract FSI values for a given time frame

    Keyword arguments
    country -- country of interest
    fsi_dict -- data set that contains all FSI data
    years -- years under consideration
    """
    d = {}
    for year in years:
        if country in fsi_dict[year].index:
            d[year] = fsi_dict[year].loc[country, "Total":].values
    return d


pprint(extract_temporal_fsi_data("Colombia", fsi_dict))
{2006: array([91.8, 9.0, 9.2, 7.4, 3.2, 8.5, 8.5, 8.7, 6.5, 7.6, 7.0, 9.1, 7.1],
      dtype=object),
 2007: array([89.7, 8.3, 8.5, 7.4, 3.8, 8.4, 8.4, 8.2, 6.0, 7.4, 6.8, 9.5, 7.0],
      dtype=object),
 2008: array([89.0, 8.0, 8.3, 7.4, 3.8, 8.4, 8.4, 7.9, 6.0, 7.2, 6.8, 9.2, 7.6],
      dtype=object),
 2009: array([89.2, 7.5, 8.0, 7.2, 4.3, 8.5, 8.5, 7.9, 6.0, 7.2, 6.9, 9.2, 8.0],
      dtype=object),
 2010: array([88.2, 7.7, 8.0, 7.2, 4.6, 8.3, 8.3, 7.7, 5.8, 6.9, 6.7, 9.0, 8.0],
      dtype=object),
 2011: array([87.0, 7.5, 8.0, 7.5, 4.1, 8.6, 7.9, 7.5, 5.6, 7.2, 6.7, 8.7, 7.7],
      dtype=object),
 2012: array([84.4, 7.0, 7.7, 7.2, 4.0, 8.4, 7.6, 7.4, 5.9, 7.0, 6.4, 8.4, 7.4],
      dtype=object),
 2013: array([83.8, 6.8, 7.7, 7.5, 3.8, 8.1, 7.3, 7.3, 6.1, 7.3, 6.5, 8.3, 7.1],
      dtype=object),
 2014: array([83.1, 7.0, 7.7, 7.7, 4.1, 7.8, 7.0, 7.4, 6.3, 7.1, 6.2, 8.0, 6.8],
      dtype=object),
 2015: array([82.5, 7.3, 7.6, 8.0, 3.8, 7.5, 6.7, 7.1, 6.0, 7.3, 6.9, 7.8, 6.5],
      dtype=object),
 2016: array([80.2, 7.0, 7.6, 7.0, 3.9, 7.8, 6.3, 6.5, 6.1, 7.2, 6.7, 7.9, 6.2],
      dtype=object),
 2017: array([78.9, 6.9, 7.6, 7.3, 4.2, 7.6, 6.2, 6.3, 5.9, 7.0, 6.2, 7.7, 6.0],
      dtype=object)}
In [143]:
def extract_total_fsi_country(country, fsi_dict, years=list(range(2006, 2018))):
    """Extract total FSI score for a given time frame

    Keyword arguments
    country -- country of interest
    fsi_dict -- data set that contains all FSI data
    years -- years under consideration
    """
    l = []
    for year in years:
        if country in fsi_dict[year].index:
            l.append(fsi_dict[year].loc[country, "Total"])
    return l


extract_total_fsi_country("Ecuador", fsi_dict)
Out[143]:
[81.2, 79.9, 80.3, 81.2, 81.7, 82.2, 80.1, 78.6, 77.3, 75.9, 75.6, 77.3]
In [144]:
def extract_total_fsi_countries(countries, fsi_dict, years=list(range(2006, 2018))):
    """Extract total FSI scores for specified countries in a given time frame

    Keyword arguments
    countries -- countries of interest
    fsi_dict -- data set that contains all FSI data
    years -- years under consideration
    """
    d = {}
    for country in countries:
        l = []
        for year in years:
            if country in fsi_dict[year].index:
                l.append(fsi_dict[year].loc[country, "Total"])
        d[country] = l
    
    # Check for the countries with no values
    res = {}
    for country, value in d.items():
        if len(value) != 0 :
            res[country] = value
    return res


pprint(extract_total_fsi_countries(["Colombia", "Ecuador"], fsi_dict))
{'Colombia': [91.8,
              89.7,
              89.0,
              89.2,
              88.2,
              87.0,
              84.4,
              83.8,
              83.1,
              82.5,
              80.2,
              78.9],
 'Ecuador': [81.2,
             79.9,
             80.3,
             81.2,
             81.7,
             82.2,
             80.1,
             78.6,
             77.3,
             75.9,
             75.6,
             77.3]}

Exploración de los datos

In [145]:
def fsi_lines_chart(countries, fsi_dict, years=list(range(2006, 2018)), figsize=(10, 8)):
    """

    Keyword arguments:
    """
    data = extract_total_fsi_countries(countries, fsi_dict)
    
    fig, ax = plt.subplots(figsize=figsize)
    title = "Total FSI Valor"
    ax.set_title(title)
    ax.set_ylabel("Valores FSI")
    ax.set_xticks(np.arange(len(years)))
    ax.set_xticklabels(years)
    ax.set_xlabel("Año")
   
    for country in data:
        ax.plot(data[country])
    loc = "upper right" 
    ax.legend(data.keys(), loc = loc)
In [146]:
fsi_lines_chart(countries["South America"] + countries["Caribbean"], fsi_dict)