Python para Ciencias de la Tierra
Índice del curso
<< Capítulo 4: Paqueterías avanzadas: Análisis de Datos
4.2 Visualización de Datos: Matplotlib
Nota antes de comenzar: Puedes descargar el cuaderno de esta sesión, los archivos usados en los ejemplos y los ejercicios resueltos suscribiéndote a mi patreon: https://www.patreon.com/ciencias_tierra.
La visualización de datos permite al científico entender mejor el fenómeno que se está estudiando, pues una adecuada gráfica muestra patrones, tendencias, correlación de variables, entre otras características de los datos que no pueden verse con claridad en tablas. Por otro lado, los objetos visuales son una herramienta versátil para comunicar resultados.
Python no ofrece dentro de su biblioteca interna una herramienta para realizar gráficos de calidad superior, pero existe Matplotlib, una paquetería flexible y poderosa que se ha convertido en la estándar por excelencia de python para mostrar gráficos.
Matplotlib más que una paquetería, es una biblioteca. Su extensión cubre varios módulos enfocados en diversos rubros para la ejecución y diseño de objetos visuales 2D y 3D. El módulo que más se utiliza en el cómputo científico es pyplot, por lo que es frecuente importar sólo ese módulo, el cual ya tiene su alias estándar de importación: plt.
import matplotlib.pyplot as plt
El módulo plt se especializa en graficar series de datos de diversas características, sean listas, tuplas, diccionarios, dataframes, arreglos, entre otros. Una de sus características es que tiene una sintaxis basada en la sintaxis sencilla que utiliza el software comercial Matlab.
Gráficos básicos
Hay una variedad enorme de gráficos disponibles en Matplotlib, sin embargo sólo haremos un repaso por los más comunes. De acuerdo con las necesidades que surjan en sesiones posteriores nos adentraremos en otros gráficos.
Una característica que tienen en común los gráficos que muestro a continuación, es que trabajan con series de datos, por lo que el parámetro puede ser una lista, tupla, serie, o similar.
Gráfico de líneas
La gráfica más básica que ya viste cómo hacer en Pandas se hace al ejecutar la función plot(), la cual sólo requiere las coordenadas en X y Y como parámetros. Así mismo, se le pueden agregar características como título, y nombres al eje X y Y, con las funciones title(), xlabel() y ylabel(), respectivamente.
X = [x for x in range(50)]
Y = [x**2 for x in X]#Impresión de las series de datos
plt.plot(X, Y)#Asignación de títulos
plt.title('Gráfica de la función cuadrática.')
plt.xlabel('Eje X') #Título del eje X
plt.ylabel('Eje Y') #Título del eje Y#Ejecución del gráfico.
plt.show()
La función show() es a los gráficos lo que la función print() es para el texto, por lo que se recomienda escribirlo al terminar de escribir el código de impresión.
Se puede dar más estilo al texto de los título, como tamaño de fuente, grosor y color de las letras. Cambia la línea del título en el código superior por la siguiente línea, e imprime para ver el nuevo estilo.
plt.title('Gráfica de la función cuadrática.',
fontsize = 19, color = 'red', fontweight = 2)
Gráfico de barras
Es el gráfico que nos muestra la frecuencia con la que se repiten ciertos valores. Se ejecuta con la función bar().
minerales = ['Mica', 'Feldespato', 'Hornblenda', 'Cuarzo']
porcentajes = [42, 15, 20, 13]plt.bar(minerales, porcentajes)plt.title('Composición mineralógica de una roca', fontsize = 17, fontname = 'Tahoma')
plt.xlabel('Minerales')
plt.ylabel('Porcentaje')
plt.xticks(fontname = 'Verdana') #Edición del título del eje X
plt.yticks(fontname = 'Verdana') #Edición del título del eje Yplt.show()
Puedes notar que incluso se puede cambiar el tipo de letra del texto. Y si te lo preguntas, las características visuales del gráfico también. Prueba cambiando el código que tiene el tipo de gráfico a imprimir, por esta línea:
plt.bar(minerales, porcentajes, width = 0.5, align = 'center',
color = 'green', edgecolor = 'blue')
Histograma
Es una gráfica de barras, pero en lugar de admitir una variable cualitativa en el eje horizontal, admite una variable cuantitativa. Esto le permite tener un mayor número de barras, pues los intervalos se pueden definir de diferentes rangos. Por ello es el gráfico más útil para conocer la distribución de una variable numérica.
La función para esta impresión es hist(), y el parámetro que controla el número de barras a mostrar es bins. De forma preliminar sólo muestra una cantidad reducida de barras.
import random
random.seed(42)
edades = [random.randint(0,65) for x in range(1000)]plt.hist(edades, bins = 40)plt.title('Edad de la Población', fontsize = 17,
fontname = 'Tahoma')
plt.xlabel('Edad')
plt.ylabel('Cantidad de personas')plt.show()
Gráfico de caja
También llamado gráfico de bigotes, muestra la distribución de datos de una variable en cuartiles, resaltando los valores dentro de los cuartiles 25 y 75 (ésta es la caja) y los valores atípicos extremos (éstos son los bigotes). La función que permite esta impresión es boxplot(). Los conjuntos de datos se agregan como parámetro en forma de lista. Esto cambia la forma de insertarlos en la función xticks, donde también se deben indicar por medio de lista el nombre que recibe cada caja.
area_norte = [72, 68, 73, 65, 77, 73, 71, 69, 81, 76]
area_sur = [60, 65, 57, 68, 61, 63, 64, 59]plt.boxplot([area_norte, area_sur])plt.xticks([1, 2], ['Sector Norte', 'Sector Sur'])plt.title('Porcentaje de feldespatos hallados en muestras', fontsize = 15, fontname = 'Verdana')
plt.ylabel('Porcentaje', fontsize=13)
plt.xlabel('Muestras según su ubicació', fontsize=13)plt.show()
Gráfico de dispersión
Esta gráfica muestra tendencias, relaciones entre dos variables, y valores atípicos. La función para su impresión es scatter().
#Se usa la paquetería random
random.seed(13)cuarzo = [randint(20, 60) for x in range(20)]
feldespatos = [randint(20, 90) for x in range(20)]plt.scatter(cuarzo, feldespatos)plt.title('Relación Q-F de rocas muestreadas.', fontsize = 15)
plt.xlabel('Feldespatos (F)', fontsize = 13)
plt.ylabel('Cuarzo (Q)', fontsize = 13)plt.show( )
Ejemplo 4.2 El archivo Consumo_energía_global.csv contiene series de tiempo del consumo eléctrico en TWh anual, según la fuente de energía, que se ha registrado a nivel global desde 1800 hasta 2019. Obtener los siguientes gráficos:
- Dos gráficos de línea: uno que agrupe las fuentes renovables, y otro que agrupe las fuentes no renovables.
- Dos gráficos de áreas apiladas: uno que agrupe a las energías con consumo medio anual mayor a 3,000 TWh, y el otro que agrupe a las de consumo medio anual menor o igual a 3,000 TWh.
- Un gráfico de línea que muestre las series de tiempo del valor promedio de las energías renovables y del valor promedio de las energías no renovables. Cada línea debe tener su propio eje, siendo el principal el de las energías renovables, y el secundario el de las energías no renovables.
Exportar todos los gráficos en archivos jpg.
Como se trata de un archivo, usaremos Pandas para importarlo y verificar si la serie de datos está bien estructurada y no tiene valores que puedan causar conflicto.
import pandas as pd
import matplotlib.pyplot as pltconsumo_energia = pd.read_csv('Consumo_energia_global.csv')
Con la primera impresión de la tabla vemos que en principio todas las columnas son valores numéricos. Usamos el atributo dtypes para saber el tipo de dato por columna; y el método isnull(), el cual crea un dataframe de mismas dimensiones que el dataframe al que se aplica, pero con elementos booleanos, según haya (True) o no (False) valores nulos. El método sum() sólo suma los elementos con valor verdadero.
No hay valores nulos, y las columnas tienen el tipo de valor adecuado. Pero revisando la primera impresión del dataframe, se observa que en los primeros renglones hay un intervalo de tiempo de 10 años de uno a otro renglón, y en los útimos renglones el salto temporal es anual. Podríamos pensar que debemos escalar adecuadamente los intervalos de tiempo, sin embargo para fortuna nuestra, matplotlib grafica los datos con una escala automática que distribuye los datos adecuadamente.
También debes saber que la gráfica en los primeros períodos de tiempo no sería completamente veraz si se imprime sin distribuir el consumo de energía, pues muestra un valor acumulado de 10 años, lo que implica que mostrará una línea con un valor 10 veces superior al que debe tener. Lo conveniente es distribuir esa energía consumida según el período de tiempo que abarca el registro. Debes tener este tipo de consideraciones en situaciones posteriores donde trabajes con series de tiempo. Por esta ocasión se omite ese cambio para dar mayor importancia a la impresión de las gráficas.
1. Gráficos de línea.
Vemos qué columnas hay para agruparlas de acuerdo al tipo de fuente de energía con keys(). Se despliega la lista: ‘Wind (TWh)’, ‘Oil (TWh)’, ‘Nuclear (TWh)’, ‘Hydropower (TWh)’, ‘Traditional biomass (TWh)’, ‘Other renewables (TWh)’, ‘Biofuels (TWh)’, ‘Solar (TWh)’, ‘Coal (TWh)’, ‘Gas (TWh)’. Las agrupamos para después graficarlas:
- Energías renovables: Wind, Hydropower, Traditional biomass, Biofuels, Solar, Other renewables.
- Energías no renovables: Oil, Nuclear, Coal, Gas
La gráfica de línea es la más adecuada para graficar series de tiempo, y para apilar varias líneas sólo hay que ejecutar varias veces el comando plot(), según las series de datos que queramos agregar. Los textos que acompañarán a los gráficos serán en español. El código queda como sigue:
#Gráfica del consumo de energía por fuentes no renovables#Se define un tamaño diferente para la impresión y el gráfico.
plt.figure(figsize = (15,8))#Se agregan una por una las gráficas
plt.plot(consumo_energia.Year, consumo_energia['Oil (TWh)'],
color = 'black', label = 'Petróleo')
plt.plot(consumo_energia.Year, consumo_energia['Coal (TWh)'],
color = 'red', label = 'Carbón')
plt.plot(consumo_energia.Year, consumo_energia['Gas (TWh)'],
color = 'gray', label = 'Gas')
plt.plot(consumo_energia.Year, consumo_energia['Nuclear (TWh)'],
color = 'green', label = 'Nuclear')#Se muestra un cuadro de leyenda de lo que representa cada línea.
plt.legend(fontsize = 11)plt.title('Consumo de energía global por fuentes no renovables', fontsize = 17)
plt.xlabel('Años', fontsize = 13)
plt.ylabel('TWh', fontsize = 13)#Exportación del gráfico en formato jpg
plt.savefig("Consumo de Energía no Renovable.jpg")plt.show()
Orbservaciones sobre el código: figure crea un objeto que se puede interpretar como una ventana o cuadro en blanco, y permite definirle un tamaño. A continuación están las cuatro líneas de código encargadas de agregar las gráficas. Una en una se ejecutan y se añaden a la figura. Además del parámetro que les da color, tiene el parámetro label, el cual permite darle nombre a esa línea. Este nombre se habilita cuando se ejecuta la función legend() más abajo, la cual muestra el cuadro de leyenda, y admite parámetros de edición de texto y de ubicación dentro del cuadro. En automático se coloca en ‘la mejor posición posible’, pero puede cambiarse en diversas esquinas del gráfico. Por último la función savefig() se encarga de exportar la imagen en el formato que indiquemos al final del nombre del archivo. Es importante señalar que esta función debe estar dentro de la misma celda que ejecuta los gráficos, y antes del comando plt.show() para su correcta exportación.
En general, trabajarás el código generador de gráficos como si de un sólo script se tratara. No lo separes en diferentes celdas, o no tendrás los resultados esperados.
De forma muy similar y con pocos cambios, puedes agregar las series de tiempo de las energías no renovables para obtener su respectivo gráfico.
2. Gráficos de áreas apiladas
Antes de hacer el gráfico, se nos pide clasificar a las fuentes de energía por la cantidad que producen en promedio para su consumo global, de acuerdo con que sean menor o mayor que 3,000 TWh.
Podemos vernos tentados a utilizar el método mean() para obtener el valor promedio de cada columna, pero recordemos que el intervalo de tiempo entre los renglones no es el mismo en todo el período, por lo que debemos obtener el intervalo de tiempo total con la diferencia del máximo y mínimo años; y dividir la suma de cada columna entre ese intervalo.
Se hace el filtro y se muestran las energías que cumplen con registrar una aportación menor o igual a 3,000 TWh anual. Como son pocas columnas, es suficiente con imprimir los promedios anuales menores o iguales al valor indicado, junto con sus nombres.
Con base en este filtro, se obtienen los dos grupos:
- Fuente de energías que han generado en promedio más de 3,000 TWh al año para su consumo: Petróleo, Biomasa, Carbón y Gas.
- Fuente de energías que han generado en promedio menos de 3,000 TWh al año para su consumo: Eólica, Nuclear, Hidroeléctrica, Biocombustibles, Solar, Otras renovables.
Ya que hemos identificado qué columnas irán a cada gráfico de áreas, hacemos el código para su impresión. La función que genera estas gráficas es stackplot(), la cual admite como primer parámetro el eje X, en este caso la serie de tiempo, y como segundo parámetro las gráficas que se van a apilar, pueden listas o diccionarios. Si tienes un dataframe, debes convertir en diccionario las columnas que debes graficar. Se pueden agregar parámetros adicionales y opcionales como las etiquetas y los colores de las áreas. El código queda como sigue:
#Gráfico de áreas apiladas#Se crean las listas para dar características a cada área
etiquetas = ['Petróleo', 'Biomasa', 'Carbón', 'Gas']
colores = ['black', 'green', 'darkgrey', 'red']plt.figure(figsize = (15,8))#Se convierten en diccionario las columnas a graficar
energias_dict = dict(consumo_energia.iloc[:,[2,5,9,10]])#Generación del gráfico con las etiquetas y los colores ya definidos
plt.stackplot(consumo_energia.Year, energias_dict.values(),
labels = etiquetas,
colors = colores
)plt.title('Mayores fuentes de energía',
fontsize = 17)
plt.xlabel('Años', fontsize = 13)
plt.ylabel('Consumo anual (TWh)', fontsize = 13)
plt.legend(loc = 2, fontsize = 14)plt.savefig('Mayores fuentes de energía.jpg')plt.show()
Orbservaciones sobre el código: El orden de las etiquetas, el orden de las columnas, y el orden de los colores deben coincidir. Este principio se debe respetar cuando tienes varias gráficas en una figura. Una práctica común es que en la función que genera el gráfico, en este caso stackplot(), después de especificar los datos, se suele dejar un parámetro por renglón, y terminar con el paréntesis de cierre en otro renglón. Esto facilita la adición o eliminación de parámetros de acuerdo a las características del gráfico, además de mantener un orden.
La función legend() tiene un parámetro adicional: loc, el cual define la ubicación del cuadro de leyenda. Admite cadena o entero, de acuerdo al código que se puede encontrar fácilmente en su documentación.
De manera muy similar se realiza la otra gráfica que se pide, pero seleccionando las columnas de las fuentes de energía de menor aportación.
3. Gráfico con ejes principal y secundario
Ahora nos piden un valor promedio para las energías renovables y para las no renovables. Es decir, obtendremos dos promedios, uno para cada grupo de fuentes de energía. Graficaremos ambas nuevas series de datos, y a cada una le asignaremos un eje para que no sea difícil la interpretación de cada una, pues claramente las no renobables harían ver muy pequeña a la gráfica de las renovables.
Para obtener el promedio hay que sumar las columnas del dataframe y dividirlo entre el número de columnas sumadas. Pero utilizando métodos propios de un dataframe, sólo hay que seleccionar qué columnas queremos promediar, y usar mean() especificando que actue sobre las columnas, es decir, en el eje igual a 1.
media_no_renovables=consumo_energia.iloc[:,[2,3,9,10]].mean(axis=1)
media_renovables=consumo_energia.iloc[:,[1,4,5,6,7,8]].mean(axis=1)
Ya tenemos las dos series de datos, ahora podemos graficarlas.
La función estelar en el siguiente código es twinx(), la cual crea un objeto nuevo dentro de la figura inicial, permitiendo introducir una nueva gráfica para que tenga su independencia de la primera gráfica declarada.
#Gráfico con eje y gráfica secundariosplt.figure(figsize = (15,8))plt.title('Comparativa del aumento del consumo por fuente de energía
de 1800 a 2019', fontsize = 17,
weight = 'semibold', fontname = 'Tahoma')#GRÁFICO UNO ------------------------------------#
#Se guarda el objeto gráfico para que el comando legend pueda leerlo
plt_1 = plt.plot(consumo_energia.Year, media_renovables,
color = 'lime'
)plt.xlabel('Años', fontsize = 15)
plt.ylabel('Consumo promedio de fuentes renovables (TWh)',
fontsize = 15)
plt.tick_params(axis ='y', labelcolor = 'green')#GRÁFICO DOS ------------------------------------#
#Se agrega el segundo gráfico
ax2 = plt.twinx()#Se guarda el objeto gráfico
plt_2 = ax2.plot(consumo_energia.Year, media_no_renovables,
color = 'darkblue'
)ax2.set_ylabel('Consumo promedio de fuentes no renovables (TWh)',
fontsize = 15, rotation = 270, labelpad = 16)
ax2.tick_params(axis ='y', labelcolor = 'darkblue')#CUADRO DE LEYENDA ------------------------------#
#Se suman los dos objetos para crear una lista
plts = plt_1 + plt_2#lista de etiquetas
etiquetas_l = ['Energías Renovables', 'Energías no Renovables']
plt.legend(plts, etiquetas_l, fontsize = 15)plt.savefig('Comparativa de fuentes de energía.jpg')plt.show()
Orbservaciones sobre el código: La primera parte define las características generales de la figura: su tamáño y título. En la sección ‘Gráfica uno’ se definen las carcterísticas asociadas a la gráfica principal. Se guarda el objeto gráfico en la variable plt_1 porque el comando legend() más abajo necesita tener como referencia los objetos base para hacer el cuadro de leyenda. La función twinx() permite crear un nuevo objeto gráfico (ax2) como objeto independiente del principal, por ello se definen también sus características. En la sección ‘Cuadro de leyenda’ se suman ambos objetos gráficos para que legend() pueda relacionarlos adecuadamente.
Nota que hay nuevas funciones y parámetros agregados. En el título weight da un grosor al texto. La función tick_params() es una versión alterna de xticks() y yticks() vistos en gráficas arriba. El método set_ylabel() es alterno a ylabel(). Se usan estas formas alternas porque el objeto ax2 no tiene los métodos usados antes, pero tienen parámetros similares. En este caso se agregan rotation, el cual gira el texto, y labelpad, el cual controla la distancia entre el título de un eje con las etiquetas del eje.
Fin del ejemplo 4.2
Fig y Ax
En la última parte del anterior ejercicio notaste que aparecieron algunas funciones nuevas como tick_params() y set_ylabel(), así como una notación aparentemente sin sentido que no se había usado: ax2.
Es común que en la edición de gráficos en matplotlib se defina un objeto ax, el cual se refiere al concepto axes. No confundir con el término axis que ya hemos visto. Axes se refiere al objeto artístico contenido en una figura, es decir, al cuadro donde las gráficas se muestran y se editan junto con sus partes: ejes, marcas, etiquetas, entre otros. Una figura (figure) es el objeto completo, incluyendo el marco de la o las gráficas. Una figura puede contener uno o más Axes. Observa la siguiente imagen, donde se muestran las partes de una figura, y cómo contiene entre tantas cosas, al menos un Axes.
Ya usaste la función figure() para crear una figura con características particulares. Ahora usaremos el objeto axes de forma paralela para trabajar con más características para graficar, pues ya fuiste testigo que ese objeto ‘habilita’ funciones similares, pero también nuevas funciones.
Por convención, a la figura siempre se le llama fig, mientras que al axes siempre se le llama ax. La función de pyplot que crea estos dos objetos es subplots().
fig, ax = plt.subplots()
Hagamos una gráfica de barras agrupadas para que empieces a familiarizarte con estos dos nuevos e importantes conceptos. Para ello importaremos una paquetería de la cual hablaremos con el debido detalle en la siguiente sesión: numpy.
import numpy as np#Gráfico de barras agrupadas#Lista del nombre de cada grupo
etiquetas = ['CDMX', 'Colima', 'Puebla', 'Xalapa', 'Cancún']#Lista de valores en cada barra
precios_max = [25.99, 25.99, 25.99, 25.99, 24.99]
precios_min = [20.8, 20.04, 15.95, 15.95, 19.66]#Se crea un 'array' para dar orden a las etiquetas de los grupos
x = np.arange(len(etiquetas))
#Ancho de las barras
ancho = 0.3#Creación de los objetos figure y axes
fig, ax = plt.subplots(figsize = (15,8))ax.set_title('Precios de Gasolina en 5 ciudades de México (21/04/2022)', fontsize = 17)#Gráficación de cada una de las barras a agrupar
plt_1 = ax.bar(x - ancho/2,
precios_max,
width = ancho,
label = 'Precios máximos')
plt_2 = ax.bar(x + ancho/2,
precios_min,
width = ancho,
label = 'Precios mínimos')#Límites del eje Y
ax.set_ylim(0,30)plt.xticks(ticks = x, labels = etiquetas, fontsize = 12)
ax.set_xlabel('Ciudades', fontsize = 14)
ax.set_ylabel('Precio en pesos mexicanos', fontsize = 14)
ax.legend(fontsize = 12)#Se imprime el número sobre cada barra
ax.bar_label(plt_1, padding=3)
ax.bar_label(plt_2, padding=3)plt.savefig('Precios Gasolina.jpg')plt.show()
Orbservaciones sobre el código: La primera lista tiene los nombres en el orden en que se asignarán a los grupos de barras, mientras que las siguientes lists tienen los datos. El comando np.arange() crea un arreglo con el orden numérico para la asignación de las etiquetas de los grupos. El ancho es un valor definido menor o igual a 0.5. Se grafica cada serie de barras por separado con bar(). Al primer parámetro se agrega el arreglo creado arriba, restando y sumando el ancho para indicar que la primera barra esté a la izquierda y la segunda a la derecha, de forma que no sumen más de 1 para no traslapar las barras de un grupo con las de otro grupo. La impresión del valor sobre cada barra se ejecuta con bar_label(), con el parámetro padding, el cual define una distancia entre la bárra y el número superior impreso.
La siguienete tabla compara los comandos más utilizados para dar formato a las gráficas, según se usen los del objeto axes o los propios de plt.
Subgráficas
Hay varias maneras de crear múltiples gráficos en una misma figura. La función subplots() tiene entre sus utilidades la de agregar varios gráficos en una misma figura mediante la sintaxis:
plt.subplots(n_renglones, m_columnas)
Se agrega el gráfico a utilizar con el comando:
add_subplots(n_renglones, m_columnas, índice)
Y se define el gráfico a editar mediante el índice:
axes[índice]
Los parámetros n_renglones y m_columnas en subplots() crean una figura de NxM axes, mientras que en add_subplots() permiten elegir la ubicación de la gráfica añadida, con el parámetro índice recorriendo todas las subgráficas desde el cuadro superior izquierdo, de izquierda a derecha y de arriba a abajo hasta el último cuadro, el inferior derecho (Figura 4.27).
fig, ax = plt.subplots(2, 3)#Gráfico 1
ax = fig.add_subplot(2, 3, 1)
ax[0].plot(X, Y)#Gráfico 2
ax = fig.add_subplot (2, 3, 2)
ax[1].plot(X, Y)
.
.
.
#Gráfico 6
ax = fig.add_subplot (2, 3, 6)
ax[5].plot(X, Y)
Y todos los gráficos agregados a la figura son independientes entre sí, por lo que se deben especificar los datos y la edición de texto en cada uno.
Recuerda que el objeto ax se convierte en una serie de axes, por lo que debes especificar a cual de los axes añadirás la gráfica de acuerdo con el índice.
Ejemplo 4.3 Obtener tres gráficos en una misma figura de la temperatura promedio diaria registrada en tres ciudades diferentes durante el mes de marzo de 2022, a partir de los datos del Servicio Meteorológico Nacional de México.
Cada serie de tiempo está en un archivo diferente dentro de una misma carpeta. Decarga la carpeta comprimida temperaturas.rar, descomprime y agrégala a la carpeta donde está el cuaderno donde harás el ejercicio.
Nota: El siguiente ejemplo será realizado únicamente para uno de los archivos. Sin embargo el procedimiento bien puede implementarse a los demás, incluso automatizarse. Se deja al lector realizar las tablas adecuadas para graficar de los otros dos archivos.
Se importan las paqueterías que sabemos que sí usaremos: matplotlib.pyplot y pandas.
Como los archivos están en una carpeta dentro del mismo directorio donde estpa nuestro cuaderno, debemos especificar la dirección usando doble diagonal para acceder a una carpeta.
queretaro = pd.read_csv('temperaturas//temperatura_Queretaro.csv')
queretaro
Al imprimir el archivo notamos que hay una serie de renglones que son encabezado. No se aprecia cuántos renglones son de encabezados, por lo que se usa head() para buscar en dónde está el renglón de encabezado.
Se observa que está en el supuesto índice 7. E indico ‘supuesto’ porque en realidad hay que contar el índice de encabezado que tomó erróneamente Pandas, por lo que el verdadero renglón con el encabezado es el de índice 8.
Para solucionar esto bien podemos abrir el archivo en una hoja de cálculo y borrarlos, pero read_csv ofrece un parámetro para indicar qué renglón es el encabezado: header.
path = 'temperaturas//temperatura_Queretaro.csv'
queretaro = pd.read_csv(path, header = 8)
queretaro
Observa que se agrega una cadena llamada path. Ésta es la dirección donde está el archivo a leer. Cuando un archivo está en un directorio diferente es común guardar su dirección en una variable para no extender demasiado la línea que lee el archivo. La cadena se puede llamar direccion, pero es muy común usar el término en inglés.
Se utilizan los métodos isnull() y sum() y el atributo dtypes ya vistos en ejemplos previos para saber si hay valores nulos en la tabla y qué tipo de variable es cada columna. No es sorpresa que la variable temporal sea una cadena.
A partir de este punto se piensa en el procedimiento a seguir antes de hacer la gráfica, pues se nos pide obtener la temperatura promedio por día, es decir, sólo se graficarán 31 registros, pero tenemos más de 1,000 registros. Hay que hacer una transformación de datos.
La verdad, para obtener la lista de pasos a hacer para resolver un problema requiere de pensar por minutos, incluso horas, sobre todo cuando se está iniciando en la programación. Por lo que no debes sesgarte por lo que aquí veas. No es un proceso rápido ni un proceso inalcanzable ni el único camino para solucionar el problema. La experiencia te hará cada vez mejor.
Los pasos resumidos a realizar para cada archivo son:
- Crear una nueva columna que sólo tenga el día calendario, al cabo sabemos que todas las mediciones son de marzo del mismo año.
- Agrupar las mediciones por el día calendario.
- Obtener la media por día.
- Guardar dataframe resumido.
- Repetir los pasos 1 al 4 según cuantos archivos haya.
- Graficar las tres series de tiempo.
Una alternativa para extraer el día de cada registro es mediante los métodos de las cadenas, pues ya has visto suficientes como para hacerlo sin mucho problema. Pero para el tratado de fechas, es recomendable usar una paquetería especializada para ello: datetime. Es un paquete interno de Python. No es muy grande, pero para este ejercicio sólo usaremos un módulo de esta paquetería, la cual también se llama datetime.
from datetime import datetime
Con esta paquetería el código para crear y añadir la nueva tabla es:
dias = list()for i in queretaro.index:
dia = queretaro.loc[i,'Fecha Local'].split(' ')[0]
dia = datetime.strptime(dia, '%d/%m/%Y')
dia = dia.day
dias.append(dia)
queretaro.insert(1, 'Día', dias)
queretaro
Se crea una lista para después insertarla al dataframe que ya tenemos. El atributo index crea una serie iterable de datos con los índices del dataframe. El módulo datetime tiene la función strptime() para convertir una cadena a un objeto de fecha en un formato, en este caso ‘%d/%m/%Y’. Al no requerirse la fecha, se omiten las horas. Se extrae el día calendario con el atributo day.
Como lo has hecho en las últimas dos sesiones, se crea una nueva tabla a partir del agrupamiento de acuerdo a la media de las fechas.
df_queretaro = queretaro.groupby('Día').mean()
Este procedimiento se repite para los otros dos archivos, los cuales una vez procesados se pueden graficar como sigue.
En este ejemplo las otras dos tablas resultantes se llaman df_toluca y df_allende para las ciudades de Toluca, Estado de México y San Miguel de Allende, Guanajuato, respectivamente. Descuida, todos los encabezados están en el renglón con índice igual a 8.
Como se indicó antes de iniciar este ejemplo y lo viste en el ejemplo anterior, debes llevar un orden y separar el código en secciones según el gráfico que se añade. El código es el siguiente:
#Características generales de toda la figura
fig, ax = plt.subplots(3, 1, figsize = (13,8))fig.suptitle("Temperaturas registradas en 3 ciudades de México en marzo 2022", y = 0.93, fontsize = 18)
fig.supxlabel('Día calendario de marzo', y = 0.05)
#Gráfico para la ciudad de San Miguel de Allende
ax[0].plot(df_allende.index, df_allende.iloc[:,0],
color = 'green', linestyle = '-.')ax[0].set_title('San Miguel de Allende, Guanajuato', y = 0.8)
ax[0].set_ylabel('Temperatura (°C)', family = 'Verdana')ax[0].set_ylim(10, 30)
ax[0].grid()
#Gráfico para la ciudad de Querétaro
ax[1].plot(df_queretaro.index, df_queretaro.iloc[:,0],
color = 'red', linestyle = '--')ax[1].set_title('Querétaro, Querétaro', y = 0.8)
ax[1].set_ylabel('Temperatura (°C)', family = 'Verdana')ax[1].set_ylim(10, 30)
ax[1].grid()
#Gráfico para la ciudad de Toluca
ax[2].plot(df_toluca.index, df_toluca.iloc[:,0],
color = 'gray', linestyle = '-', marker = 'o')ax[2].set_ylabel('Temperatura (°C)', family = 'Verdana')
ax[2].set_title('Toluca, Estado de México', y = 0.8)ax[2].set_ylim(5, 25)
ax[2].grid()plt.savefig('temperaturas//Gráfico de Temperaturas.jpg')plt.show()
Orbservaciones sobre el código: La primera parte da características generales de la figura: tamaño, título general (suptitle) y título general del eje X (supxlabel), pues se entiende al tener todas la misma escala. El índice del objeto ax es el que dirige a qué gráfico agregas o modificas. A cada gráfico se agregó título con una posición interna (y = 0.8) al gráfico para no empalmarce con los gráficos superiores. Se dió un color y estilo diferente a cada línea con los parámetros color y linestyle respectivamente. Pueden abreviarse como c y como ls respectivamente. A la tercera gráfica se le agregó un marcador con marker = ‘o’. Cada eje Y se limitó según los valores de cada gráfico. En los tres gráficos se activó la maya con grid(). Por último se guardó la imagen en la misma carpeta de las temperaturas.
Fin del ejemplo 4.3
Gráficos interactivos
Matplotlib ofrece la posibilidad de abrir un gráfico en una ventana externa con una interfaz de usuario, o GUI, la cual tiene botones para interactuar con la imagen. Para activar esta modalidad primero debes instalar una paquetería complemento de Matplotlib: ipympl.
Dirígete a la carpeta de Anaconda en el botón de inicio, y abre Anaconda prompt. No debes tener ningún programa de Anaconda abierto. En la terminal de comandos escribe:
conda install -c conda-forge ipympl
Luego de un momento se mostrarán los paquetes que se instalarán, preguntándote si deseas proceder. Indica que sí. La instalación terminará en cuanto la terminal ya admita ingresar comandos. Cierra Anaconda prompt escribiendo exit, y abre jupyter.
En jupyter, luego de importar matplotlib.pyplot, habilitas el GUI ejecutando el comando mágico %matplotlib widget. A partir de ahora, cada gráfica que imprimas en ese cuaderno tendrá una barra de botones para su interacción.
Si deseas regresar al modo de impresión gráfico ordinario, deshabilitas esta funcionalidad con el comando mágico %matplotlib inline. Los gráficos impresos en modo interactivo se congelarán en la última vista fijada.
Colores de las gráficas
Por si te has preguntado cuáles son los colores disponibles para colorear las gráficas y en realidad cualquier objeto visual en Matplotlib, te gustará saber que su gama de opciones es tan diversa que cumplirá gran parte de tus necesidades.
En la Figura 4.33 no está toda la lista, pero sólo es para que te des una idea de su variedad de colores y paletas de colores, las cuales pronto implementarás en aplicaciones 2-D. La lista completa distribuída en varias imágenes la puedes obtener dando clic aquí para su descarga. Todas las imágenes fueron obtenidas de la página oficial de Matplotlib, y tenerlas en una carpeta facilitará su acceso.
Existe otra paquetería muy utilizada en la visualización de datos llamada Seaborn. Ésta la utilizaremos en menor medida, pues Matplotlib ya tiene funcionalidades e integración con Seaborn, pero cuando se utilice se dará su debida descripción previa. Matplotlib es por excelencia y mayoría de votos la mejor y más variada paquetería de visualización de datos.
Puedes descargar el cuaderno de esta sesión, los archivos usados en los ejemplos y los ejercicios resueltos suscribiéndote a mi patreon: https://www.patreon.com/ciencias_tierra.
Ejercicios
4.4 - Del archivo del ejemplo 4.2, Consumo_energía_global.csv, sobre el consumo de energía global, obtener una tabla que resuma la cantidad promedio de consumo global por fuente de energía en 4 períodos de 50 años y un período menor (2001–2019). La tabla debe contener una columna de período y una columna para cada tipo de energía.
Hacer una gráfica de barras agrupadas que resuma todo el consumo global por tipo de fuente y por cada período. Cada grupo es un período, y cada barra uno de los 10 tipos de energías.
Exportar la imagen en formato jpg.
Siguiente sesión
>> Capítulo 4: Paqueterías avanzadas: Análisis numérico
Para saber más
Página oficial de la documentación de Matplotlib con cientos de ejemplos: