Imports, funciones y globales

In [128]:
import seaborn as sns
import pandas as pd
import geopandas as gpd
import matplotlib.pyplot as plt
from bokeh.palettes import viridis
import datetime
import calendar

print(pd.__name__, pd.__version__)
print(gpd.__name__, gpd.__version__)
print(sns.__name__, sns.__version__)

%matplotlib inline
plt.style.use('fivethirtyeight')

dir_datos_uber = 'D:/datos/UBER'
idx = pd.IndexSlice
def linea_division_meses(ax, no_semana, no_dia):
    semanas = ax.yaxis.get_data_interval().max()
    semanas = 26
    ax.axhline(y=no_semana-1, xmin=(no_dia)/7, xmax=1, c='k', ls='--')
    ax.axhline(y=no_semana, xmin=0, xmax=(no_dia)/7, c='k', ls='--')
    if no_dia!=7:
        ax.axvline(x=no_dia,  ymin=1-(no_semana-1)/semanas, ymax=1-(no_semana)/semanas, c='k', ls='--')
    return ax

puntos = {"Aeropuerto": 4946,
        "Santa Fe": 2922,
        "Bellas Artes": 4743,
        "Central del norte": 3220,
        "Taxqueña": 2806,
        "Indios verdes": 3126,
        "Pantitlán": 4962,
        "El Rosario": 2674,
        "Tlahuac": 4113,
        "Ciudad Azteca": 982,
        "Cuatro caminos": 1384,
        "Tacubaya": 4917,
        "Barranca del muerto": 3959,
        "Metro Constitucion de 1917": 3509,
        "Metro universidad": 2766,
        "Metro La Paz": 1842,
        "Metro Martín Carrera": 3037,
        "Metro Politécnico": 3086}

ageb_to_nombre = {v:k for k, v in puntos.items()}
pandas 0.24.2
geopandas 0.5.0
seaborn 0.9.0

Datos

In [17]:
dir_uber_diario = f'{dir_datos_uber}/diario_2019_T2'
df = pd.read_csv(f'{dir_uber_diario}/uber_diario_puntos_ciudad_20190101_20190630.csv', dtype={'dstId': str}).query('periodo=="ALL_DAY"')
agebs = gpd.read_file(f'{dir_datos_uber}/mexico_city_agebs.json')
agebs.set_index('MOVEMENT_ID', inplace=True)
colonias = gpd.read_file('datos/coloniascdmx.geojson')\
    .loc[lambda x: x['geometry'].notnull()]
In [18]:
df['fecha'] = pd.to_datetime(df['fecha'], format='%Y-%m-%d')
df['tiempo'] = df['meanTravelTimeSec'].div(60).round(2)
df['n_semana'] = df['fecha'].dt.weekofyear
df['n_dia_sem'] = df['fecha'].dt.dayofweek
df['dia'] = df['fecha'].dt.day_name(locale='Spanish')
df.sort_values(['fecha', 'sourceid', 'dstId'], inplace=True)
df.set_index(['fecha', 'sourceid', 'dstId'], inplace=True)

# Límites de las zonas
N0, N1 = 19.45, 19.6
S0, S1 = 19.15, 19.28
P0, P1 = -99.3, -99.2
O0, O1= -99.1, -98.95

# Crea DF para cada zona
norte = agebs.cx[P1:O0, N0:N1]
sur = agebs.cx[P1:O0, S0:S1]
poniente = agebs.cx[P0:P1, S1:N0]
oriente = agebs.cx[O0:O1, S1:N0]
centro = agebs.cx[P1:O0, S1:N0]

# Crea variable de la zona
df.loc[idx[:, :, norte.index], 'zona'] = 'Norte'
df.loc[idx[:, :, sur.index], 'zona'] = 'Sur'
df.loc[idx[:, :, poniente.index], 'zona'] = 'Poniente'
df.loc[idx[:, :, oriente.index], 'zona'] = 'Oriente'
df.loc[idx[:, :, centro.index], 'zona'] = 'Centro'

df.head()
Out[18]:
Unnamed: 0 max meanTravelTimeSec min periodo tiempo n_semana n_dia_sem dia zona
fecha sourceid dstId
2019-01-01 982 100 17.0 2021.0 1495.0 1105.0 ALL_DAY 24.92 1 1 Martes NaN
1001 261.0 935.0 582.0 361.0 ALL_DAY 9.70 1 1 Martes NaN
1002 262.0 1343.0 963.0 690.0 ALL_DAY 16.05 1 1 Martes NaN
1003 263.0 1287.0 830.0 535.0 ALL_DAY 13.83 1 1 Martes NaN
1004 264.0 1369.0 945.0 652.0 ALL_DAY 15.75 1 1 Martes NaN

Gráficas

In [138]:
dicc_dias = {0: 'Lu', 1: 'Ma', 2: "Mi", 3: "Ju", 4: "Vi", 5:"Sa" , 6: "Do"}
meses = ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic']
fecha_inicio = '20190101'
fecha_fin = '20190630'

def crea_grafica_evolucion(ageb_origen):
    fig = plt.figure()
    gs = plt.GridSpec(20, 30)

    ax1 = fig.add_subplot(gs[0:9, 0:9])
    ax2 = fig.add_subplot(gs[11:20, 0:9])
    ax3 = fig.add_subplot(gs[0:20, 10:20])
    ax4 = fig.add_subplot(gs[0:9, 20:30])
    ax5 = fig.add_subplot(gs[11:20, 20:30])

    fig.subplots_adjust(wspace=110)
    fig.set_size_inches(20, 10)
    df_origen = df.xs(ageb_origen, axis=0, level='sourceid')

    # Diario
    agrup_fecha = df_origen.groupby('fecha')[['tiempo']].mean()
    agrup_fecha.unstack()['tiempo'].plot(ax=ax1)
    agrup_fecha.rolling(7).mean().plot(ax=ax1)
    ax1.legend(['Diario', 'promedio semanal'])
    ax1.set_ylabel('minutos')
    ax1.set_ylim(0, 40)
    ax1.set_title('Tiempo promedio a todos los destinos', fontsize=14)
    ax1.set_xlabel('')

    # Dia de la semana 
    agrup_dia = df_origen.groupby(['n_dia_sem', 'dia'])[['tiempo']].mean()
    agrup_dia.reset_index('n_dia_sem').plot.bar(y='tiempo', ax=ax2, legend=False)
    ax2.set_ylim(0, 40)
    ax2.set_ylabel('minutos')
    ax2.set_xlabel('')
    ax2.set_title('Tiempo promedio por día de la semana', fontsize=14)

    # Calendario
    rango_meses = pd.date_range(start=fecha_inicio, end=fecha_fin, freq='m')
    n_meses = len(rango_meses)
    heat_data = df_origen.groupby(['n_semana', 'n_dia_sem'])[['tiempo']].mean().unstack('n_semana')['tiempo'].T\
        .rename(columns=dicc_dias)
    ax = sns.heatmap(heat_data, linewidths=.5, cmap='viridis_r', vmin=20, vmax=35, ax=ax3)
    ax.set_yticklabels(ax.get_yticklabels(), rotation=0)

    semanas = []
    for date in rango_meses:
        isocal = date.isocalendar()
        ax = linea_division_meses(ax, no_semana=isocal[1], no_dia=isocal[2])
        semanas.append(isocal[1])

    ax.set_title('Tiempo promedio a todos los destinos (minutos)', fontsize=14, y=1.03)
    ax.xaxis.set_ticks_position('top')
    ax.set_ylabel('')
    ax.set_xlabel('')
    ax_twin = ax.twinx()
    ax_twin.yaxis.grid(False)
    ax_twin.set_ylim(0, max(semanas))
    ax_twin.set_yticks(pd.np.arange(2, max(semanas), 4.5))

    ax_twin.set_yticklabels(reversed(meses[0:n_meses]))
    ax_twin.yaxis.set_ticks_position('left')
    ax_twin.yaxis.set_label_position('left')
    ax_twin.spines['left'].set_position(('outward', 25))
    # Tiempo diario por zona
    df_origen.groupby(['fecha', 'zona'])['tiempo'].mean().unstack('zona').rolling(7).mean().plot(ax=ax4)
    ax4.set_ylim(0, 60)
    ax4.set_title('Tiempo promedio, por zona de destino', fontsize=14)
    ax4.set_xlabel('')
    ax4.legend(ncol=3, loc=(0, 0.05))
    ax4.set_ylabel('minutos (media movil 7 días)', fontsize=12)

    # Mapa de zonas
    colonias.boundary.plot(color='k', ax=ax5, lw=0.2)
    norte.boundary.plot(color='C1', ax=ax5, label='Norte', lw=0.2, zorder=-1)
    sur.boundary.plot(color='C4', ax=ax5, label='Sur', lw=0.2, zorder=-1)
    poniente.boundary.plot(color='C3', ax=ax5, label='Poniente', lw=0.2, zorder=-1)
    oriente.boundary.plot(color='C2', ax=ax5, label='Oriente', lw=0.2, zorder=-1)
    centro.boundary.plot(color='C0', ax=ax5, label='Centro', lw=0.2, zorder=-1)
    agebs.loc[[str(ageb_origen)]].plot(color='red', ax=ax5, zorder=10)
    ax5.legend(bbox_to_anchor=(1.4, 0.7))
    ax5.set_axis_off()
    
    #Titulo y guardado
    zona_origen = ageb_to_nombre[ageb_origen]
    fig.text(x=0, y=-0.1, s='Elaborado por @jjsantoso con datos de Uber Movements', fontdict={'size': 9}, transform=ax.transAxes)
    fig.suptitle(f'Todos los viajes de UBER con origen en {zona_origen} (Movement ID {ageb_origen}), 2019-1T y 2T', fontsize=18)
    figname = f'graficas/evolucion_diaria_{zona_origen}_2019T1T2.png'
    fig.savefig(figname, dpi=300, bbox_inches='tight')
    print('Guardada en:', figname)
In [140]:
for origen in ageb_to_nombre:
    crea_grafica_evolucion(origen)
Guardada en: graficas/evolucion_diaria_Aeropuerto_2019T1T2.png
Guardada en: graficas/evolucion_diaria_Santa Fe_2019T1T2.png
Guardada en: graficas/evolucion_diaria_Bellas Artes_2019T1T2.png
Guardada en: graficas/evolucion_diaria_Central del norte_2019T1T2.png
Guardada en: graficas/evolucion_diaria_Taxqueña_2019T1T2.png
Guardada en: graficas/evolucion_diaria_Indios verdes_2019T1T2.png
Guardada en: graficas/evolucion_diaria_Pantitlán_2019T1T2.png
Guardada en: graficas/evolucion_diaria_El Rosario_2019T1T2.png
Guardada en: graficas/evolucion_diaria_Tlahuac_2019T1T2.png
Guardada en: graficas/evolucion_diaria_Ciudad Azteca_2019T1T2.png
Guardada en: graficas/evolucion_diaria_Cuatro caminos_2019T1T2.png
Guardada en: graficas/evolucion_diaria_Tacubaya_2019T1T2.png
Guardada en: graficas/evolucion_diaria_Barranca del muerto_2019T1T2.png
Guardada en: graficas/evolucion_diaria_Metro Constitucion de 1917_2019T1T2.png
Guardada en: graficas/evolucion_diaria_Metro universidad_2019T1T2.png
Guardada en: graficas/evolucion_diaria_Metro La Paz_2019T1T2.png
Guardada en: graficas/evolucion_diaria_Metro Martín Carrera_2019T1T2.png
Guardada en: graficas/evolucion_diaria_Metro Politécnico_2019T1T2.png