Ejercicio

Los ficheros emisiones-2016.csv, emisiones-2017.csv, emisiones-2018.csv y emisiones-2019.csv, contienen datos sobre las emisiones contaminates en la ciudad de Madrid en los años 2016, 2017, 2018 y 2019 respectivamente. Escribir un programa con los siguientes requisitos:

  1. Generar un DataFrame con los datos de los cuatro ficheros.
  2. Filtrar las columnas del DataFrame para quedarse con las columnas ESTACION, MAGNITUD, AÑO, MES y las correspondientes a los días D01, D02, etc.
  3. Reestructurar el DataFrame para que los valores de los contaminantes de las columnas de los días aparezcan en una única columna.
  4. Añadir una columna con la fecha a partir de la concatenación del año, el mes y el día (usar el módulo datetime).
  5. Eliminar las filas con fechas no válidas (utilizar la función isnat del módulo numpy) y ordenar el DataFrame por estaciones, contaminantes y fecha.
  6. Mostrar por pantalla las estaciones y los contaminantes disponibles en el DataFrame.
  7. Crear una función que reciba una estación, un contaminante y un rango de fechas y devuelva una serie con las emisiones del contaminante dado en la estación y rango de fechas dado.
  8. Mostrar un resumen descriptivo (mímino, máximo, media, etc) para cada contaminante.
  9. Mostrar un resumen descriptivo para cada contaminente por distritos.
  10. Crear una función que reciba una estación y un contaminante y devuelva un resumen descriptivo de las emisiones del contaminante indicado en la estadión indicada.
  11. Crear una función que devuelva las emisiones medias mensuales de un contaminante y un año dados para todos las estaciones.
  12. Crear un función que reciba una estación de medición y devuelva un DataFrame con las medias mensuales de los distintos tipos de contaminantes.

Solución


In [30]:
import pandas as pd
import numpy as np
import datetime as dt

# Generar un DataFrame con los datos de los cuatro ficheros
import pandas as pd 

emisiones_2016 = pd.read_csv('emisiones-2016.csv', sep = ';')
emisiones_2017 = pd.read_csv('emisiones-2017.csv', sep = ';')
emisiones_2018 = pd.read_csv('emisiones-2018.csv', sep = ';')
emisiones_2019 = pd.read_csv('emisiones-2019.csv', sep = ';')
emisiones = pd.concat([emisiones_2016, emisiones_2017, emisiones_2018, emisiones_2019])
emisiones


Out[30]:
PROVINCIA MUNICIPIO ESTACION MAGNITUD PUNTO_MUESTREO ANO MES D01 V01 D02 ... D27 V27 D28 V28 D29 V29 D30 V30 D31 V31
0 28 79 4 1 28079004_1_38 2016 1 8.0 V 7.0 ... 9.0 V 7.0 V 8.0 V 9.0 V 9.0 V
1 28 79 4 1 28079004_1_38 2016 2 12.0 V 13.0 ... 7.0 V 8.0 V 9.0 V 0.0 N 0.0 N
2 28 79 4 1 28079004_1_38 2016 3 11.0 V 10.0 ... 8.0 V 7.0 V 8.0 V 10.0 V 8.0 V
3 28 79 4 1 28079004_1_38 2016 4 8.0 V 9.0 ... 9.0 V 8.0 V 8.0 V 8.0 V 0.0 N
4 28 79 4 1 28079004_1_38 2016 5 7.0 V 8.0 ... 7.0 V 7.0 V 7.0 V 7.0 V 7.0 V
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
1831 28 79 60 14 28079060_14_6 2019 8 94.0 V 104.0 ... 88.0 V 90.0 V 99.0 V 108.0 V 98.0 V
1832 28 79 60 14 28079060_14_6 2019 9 88.0 V 82.0 ... 54.0 V 68.0 V 70.0 V 55.0 V 0.0 N
1833 28 79 60 14 28079060_14_6 2019 10 44.0 V 75.0 ... 28.0 V 33.0 V 16.0 V 19.0 V 47.0 V
1834 28 79 60 14 28079060_14_6 2019 11 41.0 V 55.0 ... 55.0 V 52.0 V 47.0 V 56.0 V 0.0 N
1835 28 79 60 14 28079060_14_6 2019 12 47.0 V 53.0 ... 17.0 V 13.0 V 14.0 V 5.0 V 4.0 V

7266 rows × 69 columns


In [31]:
# Filtrar las columnas del DataFrame para quedarse con las columnas ESTACION, MAGNITUD, AÑO, MES y las correspondientes a los días D01, D02, etc. 
columnas = ['ESTACION', 'MAGNITUD', 'ANO', 'MES']
columnas.extend([col for col in emisiones if col.startswith('D')])
emisiones = emisiones[columnas]
emisiones


Out[31]:
ESTACION MAGNITUD ANO MES D01 D02 D03 D04 D05 D06 ... D22 D23 D24 D25 D26 D27 D28 D29 D30 D31
0 4 1 2016 1 8.0 7.0 6.0 6.0 7.0 6.0 ... 10.0 11.0 11.0 13.0 12.0 9.0 7.0 8.0 9.0 9.0
1 4 1 2016 2 12.0 13.0 9.0 9.0 11.0 9.0 ... 11.0 10.0 9.0 8.0 7.0 7.0 8.0 9.0 0.0 0.0
2 4 1 2016 3 11.0 10.0 9.0 9.0 7.0 8.0 ... 8.0 8.0 9.0 9.0 9.0 8.0 7.0 8.0 10.0 8.0
3 4 1 2016 4 8.0 9.0 9.0 8.0 8.0 9.0 ... 8.0 8.0 8.0 8.0 9.0 9.0 8.0 8.0 8.0 0.0
4 4 1 2016 5 7.0 8.0 9.0 9.0 8.0 8.0 ... 7.0 7.0 8.0 7.0 7.0 7.0 7.0 7.0 7.0 7.0
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
1831 60 14 2019 8 94.0 104.0 106.0 99.0 77.0 82.0 ... 86.0 97.0 104.0 96.0 85.0 88.0 90.0 99.0 108.0 98.0
1832 60 14 2019 9 88.0 82.0 80.0 92.0 79.0 75.0 ... 59.0 46.0 49.0 69.0 57.0 54.0 68.0 70.0 55.0 0.0
1833 60 14 2019 10 44.0 75.0 44.0 54.0 65.0 68.0 ... 37.0 39.0 45.0 21.0 23.0 28.0 33.0 16.0 19.0 47.0
1834 60 14 2019 11 41.0 55.0 79.0 65.0 64.0 51.0 ... 38.0 75.0 69.0 45.0 35.0 55.0 52.0 47.0 56.0 0.0
1835 60 14 2019 12 47.0 53.0 45.0 44.0 49.0 41.0 ... 68.0 36.0 9.0 11.0 10.0 17.0 13.0 14.0 5.0 4.0

7266 rows × 35 columns


In [32]:
# Reestructurar el DataFrame para que los valores de los contaminantes de las columnas de los días aparezcan en una única columna.
emisiones = emisiones.melt(id_vars=['ESTACION', 'MAGNITUD', 'ANO', 'MES'], var_name='DIA', value_name='VALOR')
emisiones


Out[32]:
ESTACION MAGNITUD ANO MES DIA VALOR
0 4 1 2016 1 D01 8.0
1 4 1 2016 2 D01 12.0
2 4 1 2016 3 D01 11.0
3 4 1 2016 4 D01 8.0
4 4 1 2016 5 D01 7.0
... ... ... ... ... ... ...
225241 60 14 2019 8 D31 98.0
225242 60 14 2019 9 D31 0.0
225243 60 14 2019 10 D31 47.0
225244 60 14 2019 11 D31 0.0
225245 60 14 2019 12 D31 4.0

225246 rows × 6 columns


In [33]:
# Crear una nueva columna con las fechas a partir del año, mes y día
# Primero eliminamos el caracter D del comienzo de la columna de los días
emisiones['DIA'] = emisiones.DIA.str.strip('D')
# Concatenamos las columnas del año, mes y día
emisiones['FECHA'] = emisiones.ANO.apply(str) + '/' + emisiones.MES.apply(str) + '/' + emisiones.DIA.apply(str)
# Convertimos la nueva columna al tipo fecha
emisiones['FECHA'] = pd.to_datetime(emisiones.FECHA, format='%Y/%m/%d', infer_datetime_format=True, errors='coerce')
emisiones


Out[33]:
ESTACION MAGNITUD ANO MES DIA VALOR FECHA
0 4 1 2016 1 01 8.0 2016-01-01
1 4 1 2016 2 01 12.0 2016-02-01
2 4 1 2016 3 01 11.0 2016-03-01
3 4 1 2016 4 01 8.0 2016-04-01
4 4 1 2016 5 01 7.0 2016-05-01
... ... ... ... ... ... ... ...
225241 60 14 2019 8 31 98.0 2019-08-31
225242 60 14 2019 9 31 0.0 NaT
225243 60 14 2019 10 31 47.0 2019-10-31
225244 60 14 2019 11 31 0.0 NaT
225245 60 14 2019 12 31 4.0 2019-12-31

225246 rows × 7 columns


In [34]:
# Eliminar las filas con fechas no válidas
emisiones = emisiones.drop(emisiones[np.isnat(emisiones.FECHA)].index)
# Ordenar el el dataframe por estación, magnitud y fecha
emisiones.sort_values(['ESTACION', 'MAGNITUD', 'FECHA'])


Out[34]:
ESTACION MAGNITUD ANO MES DIA VALOR FECHA
0 4 1 2016 1 01 8.0 2016-01-01
7266 4 1 2016 1 02 7.0 2016-01-02
14532 4 1 2016 1 03 6.0 2016-01-03
21798 4 1 2016 1 04 6.0 2016-01-04
29064 4 1 2016 1 05 7.0 2016-01-05
... ... ... ... ... ... ... ...
196181 60 14 2019 12 27 17.0 2019-12-27
203447 60 14 2019 12 28 13.0 2019-12-28
210713 60 14 2019 12 29 14.0 2019-12-29
217979 60 14 2019 12 30 5.0 2019-12-30
225245 60 14 2019 12 31 4.0 2019-12-31

221158 rows × 7 columns


In [35]:
# Mostrar las estaciones disponibles
print('Estaciones:', emisiones.ESTACION.unique())
# Mostrar los contaminantes disponibles
print('Contaminantes:', emisiones.MAGNITUD.unique())


Estaciones: [ 4  8 11 16 17 18 24 27 35 36 38 39 40 47 48 49 50 54 55 56 57 58 59 60]
Contaminantes: [ 1  6  7  8 12  9 10 14 20 30 35 42 43 44]

In [41]:
# Función que devuelve las emisiones de un contaminante dado en una estación y rango de fechas dado.
def evolucion(estacion, contaminante, desde, hasta):
    return emisiones[(emisiones.ESTACION == estacion) & (emisiones.MAGNITUD == contaminante) & (emisiones.FECHA >= desde) & (emisiones.FECHA <= hasta)].sort_values('FECHA').VALOR
evolucion(56, 8, dt.datetime.strptime('2018/10/25', '%Y/%m/%d'), dt.datetime.strptime('2019/02/12', '%Y/%m/%d'))


Out[41]:
179559    89.0
186825    87.0
194091    46.0
201357    38.0
208623    29.0
          ... 
57841     82.0
65107     64.0
72373     26.0
79639     59.0
86905     71.0
Name: VALOR, Length: 111, dtype: float64

In [36]:
# Resumen descriptivo por contaminantes
emisiones.groupby('MAGNITUD').VALOR.describe()


Out[36]:
count mean std min 25% 50% 75% max
MAGNITUD
1 14610.0 7.428953 7.012504 0.00 4.00 7.00 10.00 610.00
6 14610.0 0.350233 0.215935 0.00 0.20 0.30 0.40 14.90
7 35064.0 20.446412 135.123509 0.00 4.00 9.00 23.00 24742.00
8 35064.0 37.677618 20.118050 0.00 22.00 35.00 50.00 148.00
9 8948.0 10.087729 10.643591 0.00 6.00 9.00 13.00 850.00
10 17897.0 18.772923 35.723619 0.00 10.00 16.00 24.00 4481.00
12 35064.0 67.959417 61.443940 0.00 29.00 48.00 84.00 1005.00
14 20454.0 49.941772 24.753120 0.00 31.00 52.00 69.00 336.00
20 8766.0 2.364944 4.236706 0.00 0.80 1.60 2.80 195.00
30 8766.0 0.531371 0.538180 0.00 0.20 0.40 0.70 15.10
35 8766.0 0.479751 1.183618 0.00 0.10 0.20 0.50 35.70
42 4383.0 1.400897 0.251836 -0.01 1.25 1.38 1.54 3.09
43 4383.0 1.292923 0.230898 -0.14 1.17 1.28 1.43 2.77
44 4383.0 0.108941 0.068776 0.00 0.06 0.10 0.14 1.31

In [37]:
# Resumen descriptivo por contaminantes y distritos
emisiones.groupby(['ESTACION', 'MAGNITUD']).VALOR.describe()


Out[37]:
count mean std min 25% 50% 75% max
ESTACION MAGNITUD
4 1 1461.0 7.329911 16.379050 1.0 4.0 7.0 9.0 610.0
6 1461.0 0.411499 0.172902 0.1 0.3 0.4 0.5 1.3
7 1461.0 31.939767 37.667968 0.0 8.0 16.0 42.0 239.0
8 1461.0 44.398357 17.766063 0.0 31.0 43.0 55.0 105.0
12 1461.0 93.341547 72.436531 0.0 44.0 69.0 119.0 467.0
... ... ... ... ... ... ... ... ... ...
60 7 1461.0 12.326489 19.593109 1.0 2.0 4.0 12.0 151.0
8 1461.0 31.125941 18.101896 3.0 18.0 27.0 41.0 101.0
10 1461.0 17.033539 12.205022 1.0 9.0 14.0 21.0 215.0
12 1461.0 50.023956 45.933843 6.0 22.0 33.0 60.0 328.0
14 1461.0 60.718001 26.309952 4.0 42.0 65.0 81.0 119.0

153 rows × 8 columns


In [38]:
# Función que devuelve un resumen descriptivo de la emisiones en un contaminante dado en un estación dada
def resumen(estacion, contaminante):
    return emisiones[(emisiones.ESTACION == estacion) & (emisiones.MAGNITUD == contaminante)].VALOR.describe()

# Resumen de Dióxido de Nitrógeno en Plaza Elíptica
print('Resumen Dióxido de Nitrógeno en Plaza Elíptica:\n', resumen(56, 8),'\n', sep='')
# Resumen de Dióxido de Nitrógeno en Plaza del Carmen
print('Resumen Dióxido de Nitrógeno en Plaza del Carmen:\n', resumen(35, 8), sep='')


Resumen Dióxido de Nitrógeno en Plaza Elíptica:
count    1461.000000
mean       55.113621
std        21.911483
min         0.000000
25%        39.000000
50%        53.000000
75%        69.000000
max       142.000000
Name: VALOR, dtype: float64

Resumen Dióxido de Nitrógeno en Plaza del Carmen:
count    1461.000000
mean       43.260096
std        16.384656
min         0.000000
25%        32.000000
50%        43.000000
75%        54.000000
max        96.000000
Name: VALOR, dtype: float64

In [20]:
# Función que devuelve una serie con las emisiones medias mensuales de un contaminante y un mes año para todos las estaciones
def evolucion_mensual(contaminante, año):
    return emisiones[(emisiones.MAGNITUD == contaminante) & (emisiones.ANO == año)].groupby(['ESTACION', 'MES']).VALOR.mean().unstack('MES')

# Evolución del dióxido de nitrógeno en 2019
evolucion_mensual(8, 2019)


Out[20]:
MES 1 2 3 4 5 6 7 8 9 10 11 12
ESTACION
4 61.129032 56.000000 44.677419 34.870968 33.451613 35.516129 36.838710 34.064516 34.258065 33.451613 21.096774 40.806452
8 74.516129 69.612903 57.064516 46.903226 43.032258 40.580645 36.387097 24.612903 43.774194 55.000000 40.677419 49.870968
11 58.516129 53.806452 37.709677 31.032258 26.064516 30.709677 33.451613 28.322581 35.774194 48.451613 30.774194 47.612903
16 54.387097 51.387097 35.451613 27.032258 20.354839 23.870968 25.258065 24.548387 30.258065 41.935484 28.548387 40.677419
17 65.967742 63.354839 43.161290 28.451613 22.548387 24.322581 27.161290 29.322581 35.741935 47.774194 29.580645 44.516129
18 55.161290 49.516129 35.645161 23.516129 19.967742 20.354839 22.322581 23.419355 25.000000 41.225806 24.580645 39.193548
24 37.516129 34.741935 21.645161 12.677419 10.806452 11.612903 13.322581 13.903226 19.935484 27.000000 11.774194 26.258065
27 47.580645 43.548387 35.709677 26.677419 22.903226 25.387097 27.032258 31.677419 34.806452 49.000000 29.161290 45.774194
35 54.548387 46.838710 31.903226 21.000000 22.548387 25.193548 35.322581 33.096774 36.322581 45.193548 29.161290 41.483871
36 58.870968 53.000000 37.645161 28.387097 21.354839 24.000000 25.677419 25.935484 30.483871 43.838710 31.258065 43.354839
38 62.193548 53.612903 38.032258 28.387097 25.935484 24.548387 22.096774 21.161290 35.838710 47.064516 28.258065 45.870968
39 63.354839 56.193548 37.483871 24.741935 19.483871 24.387097 25.483871 24.870968 29.483871 47.290323 26.258065 42.774194
40 58.967742 51.806452 36.387097 27.516129 21.967742 23.258065 26.419355 26.548387 33.612903 44.580645 29.967742 41.709677
47 53.548387 47.483871 36.322581 22.838710 21.354839 18.354839 23.903226 26.806452 32.580645 43.645161 25.870968 39.193548
48 55.709677 51.645161 34.677419 25.612903 17.967742 23.838710 24.903226 20.870968 26.451613 43.967742 32.580645 43.000000
49 45.580645 37.967742 25.870968 16.806452 14.354839 13.451613 16.129032 16.322581 18.806452 31.838710 21.483871 33.935484
50 57.741935 50.774194 36.741935 31.419355 24.000000 29.258065 28.967742 26.258065 31.193548 44.709677 30.741935 41.709677
54 60.774194 53.580645 38.354839 27.000000 24.645161 21.354839 23.806452 26.225806 33.161290 46.967742 31.838710 45.967742
55 63.548387 55.774194 44.161290 30.225806 25.419355 25.774194 26.064516 28.225806 31.774194 45.000000 26.935484 44.064516
56 77.483871 74.451613 61.064516 45.387097 44.032258 39.774194 45.838710 44.580645 50.419355 57.225806 34.000000 51.903226
57 53.967742 47.258065 31.290323 24.193548 17.354839 20.096774 21.903226 21.677419 25.064516 37.806452 25.161290 38.193548
58 24.774194 21.741935 15.483871 10.806452 9.483871 11.870968 12.322581 12.935484 14.935484 21.645161 11.516129 18.935484
59 39.064516 37.000000 25.322581 19.032258 13.741935 16.290323 14.225806 14.774194 22.129032 35.290323 22.645161 32.387097
60 42.032258 40.419355 25.129032 20.354839 14.580645 17.645161 22.000000 16.096774 18.838710 20.806452 15.225806 42.870968

In [81]:
# Mostrar la edad media de las mujeres que viajaban en cada clase.
print(titanic.groupby(['Pclass','Sex'])['Age'].mean().unstack()['female'])


Pclass
1    34.611765
2    28.722973
3    21.750000
Name: female, dtype: float64

In [85]:
# Añadir una nueva columna booleana para ver si el pasajero era menor de edad o no.
titanic['Young'] = titanic['Age'] < 18

In [91]:
# Mostrar el porcentaje de menores y mayores de edad que sobrevivieron en cada clase.
print(titanic.groupby(['Pclass', 'Young'])['Survived'].value_counts(normalize = True) * 100)


Pclass  Young  Survived
1       False  1           61.274510
               0           38.725490
        True   1           91.666667
               0            8.333333
2       False  0           59.006211
               1           40.993789
        True   1           91.304348
               0            8.695652
3       False  0           78.208232
               1           21.791768
        True   0           62.820513
               1           37.179487
Name: Survived, dtype: float64

In [ ]: