Visualización
Pandas incluye una gran cantidad de utilidades para facilitar la representación gráfica de un dataframe y de sus series usando la libreria Matplotlib. Ver documentación.
pyplot
es una capa sobre Matplotlib que dota a la librería de una sintaxis similar a la de MATLAB.
La visualización directa con Pandas es fantástica, debido a su fácil uso, para interpretaciones e informes varios. Aunque, no es lo suficientemente versàtil para controlar diversos “criterios”, en este caso deberiamos utilizar directamente la libreria matplotlib. Los gràficos de pandas són una abstracción de esta libreria.
Por otro lado veremos algunos ejemplos de visualización con otra libreria llamada Seaborn.
Por ahora, nos limitaremos a hablar sobre los tipos básicos de visualización
Lineas
Barras
Histogramas y Boxplots
Scatter plots
Función Plot de Pandas
En este bloque ilustraremos los ejemplos de visualización de sus series mediante la función plot. Ver documentación
[13]:
import numpy as np
import pandas as pd
# Generació de dades
np.random.seed(1)
samples = 50
df = pd.DataFrame({
"temperatura":np.random.randint(low=-10,high=50,size=samples),
"pluja":np.random.randint(low= 0,high=50,size=samples),
"vent":np.random.choice(["N","S","E","W"],size=samples)})
df.head()
[13]:
temperatura | pluja | vent | |
---|---|---|---|
0 | 27 | 42 | W |
1 | 33 | 8 | N |
2 | 2 | 30 | E |
3 | -2 | 7 | E |
4 | -1 | 3 | E |
[14]:
df.plot(); # que passa si llevem el;
[15]:
df.temperatura.plot();
La función plot
és muy versátil pero no funciona simpre, hay que ser consciente del tipo de datos que mostramos:
[16]:
df.vent.plot()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/Users/isaac/Projects/TxADM_notebooks/notebooks/Part2/00_Pandas/03_Visualizacion.ipynb Cell 7 line <cell line: 1>()
----> <a href='vscode-notebook-cell:/Users/isaac/Projects/TxADM_notebooks/notebooks/Part2/00_Pandas/03_Visualizacion.ipynb#Y100sZmlsZQ%3D%3D?line=0'>1</a> df.vent.plot()
File ~/.pyenv/versions/3.9.7/envs/my397/lib/python3.9/site-packages/pandas/plotting/_core.py:972, in PlotAccessor.__call__(self, *args, **kwargs)
969 label_name = label_kw or data.columns
970 data.columns = label_name
--> 972 return plot_backend.plot(data, kind=kind, **kwargs)
File ~/.pyenv/versions/3.9.7/envs/my397/lib/python3.9/site-packages/pandas/plotting/_matplotlib/__init__.py:71, in plot(data, kind, **kwargs)
69 kwargs["ax"] = getattr(ax, "left_ax", ax)
70 plot_obj = PLOT_CLASSES[kind](data, **kwargs)
---> 71 plot_obj.generate()
72 plot_obj.draw()
73 return plot_obj.result
File ~/.pyenv/versions/3.9.7/envs/my397/lib/python3.9/site-packages/pandas/plotting/_matplotlib/core.py:327, in MPLPlot.generate(self)
325 def generate(self):
326 self._args_adjust()
--> 327 self._compute_plot_data()
328 self._setup_subplots()
329 self._make_plot()
File ~/.pyenv/versions/3.9.7/envs/my397/lib/python3.9/site-packages/pandas/plotting/_matplotlib/core.py:506, in MPLPlot._compute_plot_data(self)
504 # no non-numeric frames or series allowed
505 if is_empty:
--> 506 raise TypeError("no numeric data to plot")
508 self.data = numeric_data.apply(self._convert_to_ndarray)
TypeError: no numeric data to plot
[17]:
df.groupby(["vent"]).count()
[17]:
temperatura | pluja | |
---|---|---|
vent | ||
E | 9 | 9 |
N | 16 | 16 |
S | 12 | 12 |
W | 13 | 13 |
[18]:
df[["vent","temperatura"]].groupby(["vent"]).count().plot()
[18]:
<AxesSubplot:xlabel='vent'>
El parametro kind
Este parametro sirve para controlar el tipo de gráfico que mostramos, entre las opciones se dispone de: - bar
: trama de barres verticals - barh
: trama de barres horitzontals - hist
: histograma - box
: trama de caixa - area
: parcel·la de superfície - pie
: trama de pastís - scatter
: gràfic de dispersió (només DataFrame) - hexbin
: trama hexbin (només DataFrame)
[19]:
df.groupby(["vent"]).size().plot(kind="bar"); #mejor uno de barras
[20]:
df.temperatura.plot(kind="hist"); # Si los datos son numéricos un histograma es práctico
Además del tipo de gráfico, la función plot
nos permite definir otras características del gráfico mediante varios parámetros:
title
: permite definir el título del gráfico mediante una cadena de texto.legend
: un booleano que indica si el gráfico debe incluir una leyenda o no.xlabel
: etiqueta del eje x.ylabel
: etiqueta del eje y.colormap
: mapa de colores para el dibujo. documentación
Esta función tiene algunos parámetros adicionales que se pueden consultar en su documentación.
Veamos cómo podemos agregar información adicional a nuestro gráfico:
[21]:
df.groupby(["vent"]).size().plot(kind="bar", title="Procedencia del vent", ylabel="Nombre de dies", colormap="summer")
[21]:
<AxesSubplot:title={'center':'Procedencia del vent'}, xlabel='vent', ylabel='Nombre de dies'>
Matplotlib
Como mencionamos en la introducción, Pandas
utiliza internamente la biblioteca en la que se definen los componentes de un gráfico. Se llama Matplotlib
y permite realizar visualizaciones avanzadas.
Al utilizar Matplotlib
directamente, tenemos un mayor control sobre las opciones de visualización. También podemos guardar nuestros gráficos.
[22]:
import matplotlib.pyplot as plt
fig, ax = plt.subplots() # Definició forma
ax.plot(df.temperatura) # Inserció de dades
plt.title("Temperatura ", fontsize=20) # Maquetació
plt.xlabel('Samples', fontsize=14)
plt.ylabel('Celsius')
plt.savefig('data/test.png',dpi=100) # guardat
plt.show() # Visualització
[23]:
tipusVent = df.groupby(["vent"]).size()
print(tipusVent)
vent
E 9
N 16
S 12
W 13
dtype: int64
[24]:
x = tipusVent.index
y = tipusVent.values
print("x: ", tipusVent.index)
print("y: ", tipusVent.values)
fig, ax = plt.subplots() # Definició forma
ax.bar(x, y, color=['#5cb85c','#5bc0de','#d9534f','#59484a']) # Colors en hexadecimal
plt.title("Informació de vent");
plt.xlabel('Direccions', fontsize=14);
plt.ylabel('Dies', fontsize=14);
x: Index(['E', 'N', 'S', 'W'], dtype='object', name='vent')
y: [ 9 16 12 13]
Una vez que nos familiarizamos con la dinámica de la biblioteca y comprendemos las diferentes funciones, tenemos la capacidad de crear gráficos avanzados:
Este gráfico se ha extraído del siguiente tutorial enlace.
[25]:
import numpy as np
import matplotlib.pyplot as plt
N = 4 #direccions
theta = np.linspace(0.0, 2 * np.pi, N, endpoint=False)
width = np.pi / y
colors = ["red","blue","green","black"] # també podem definir els colors amb un string
ax = plt.subplot(projection='polar')
ax.bar(theta, y, width=width, bottom=0.0, color=colors, alpha=0.5)
plt.title("Direccions dels vents \n")
plt.show()
Tipos de Visualizaciones
A continuación, revisaremos varios tipos de visualizaciones para que tengan algunos ejemplos a su disposición.
Existe una galería de ejemplos muy útil que pueden encontrar en el siguiente enlace.
Barras
Los diagramas de barras son muy útiles. Ya hemos visto cómo dibujarlos directamente desde un dataframe y también utilizando la biblioteca matplotlib
.
[26]:
df = pd.read_csv("data/WHO.csv")
co2 = df["Total_CO2_emissions"]
ticks_labels = df["Country"][1:10].values
ax = co2[1:10].plot(kind="bar") # un plot retorna el component ax, combinam les dues lliberies
ax.set_xticklabels(ticks_labels)
plt.show()
Vamos a crear una segunda versión, seleccionando los datos que deseamos mostrar:
[27]:
co2 = df["Total_CO2_emissions"].sort_values(ascending=False).head(10)
dateSelect = df.loc[co2.index] # Noms
co2.index = dateSelect["Country"]
ax = co2.plot(kind="bar")
ax.set_ylabel("count")
plt.title("Principals països emissors de C02")
plt.show()
Histograma
Un histograma es una representación gráfica que muestra la distribución de datos numéricos en forma de barras verticales, donde cada barra representa la frecuencia o cantidad de veces que ocurren valores dentro de un rango específico.
[28]:
co2 = df["Total_CO2_emissions"]
#co2.dropna(inplace=True) ## NaN (NotAtNumber)
fig, ax = plt.subplots() # Definició forma
ax.hist(co2.values,bins=10) # la definició dels bins és molt rellevant en aquest tipus de gràfic ()
plt.show()
Boxplots
Un diagrama de caja, también conocido como boxplot en inglés, es una representación gráfica utilizada para mostrar la distribución y estadísticas clave de un conjunto de datos. Esta representación consiste en un rectángulo que abarca el primer cuartil (Q1) y el tercer cuartil (Q3) de los datos, con una línea en el centro que indica la mediana. Las “whiskers” (o flechas) se extienden desde el rectángulo hasta los valores máximos y mínimos dentro de un rango específico, mientras que los puntos fuera de este rango se identifican como valores atípicos. Esto permite visualizar la dispersión, simetría y posibles valores atípicos en un conjunto de datos de manera concisa.
[29]:
co2 = df["Total_CO2_emissions"]
co2.dropna(inplace=True)
fig, ax = plt.subplots(ncols=2) # Podem definir múltiples plots en el mateix gràfic
ax[0].boxplot(co2.values*1000)
ax[1].hist(co2)
plt.show()
# On estan els valors ?
Gráficos de Líneas
Vamos a explorar cómo construir una visualización completa con diferentes propiedades, y aprovecharemos para crear un gráfico de líneas utilizando la función plot
.
[30]:
import numpy as np
xs = np.random.randn(3, 10000)
print(xs.shape)
(3, 10000)
[31]:
bms = xs.cumsum(axis=1)
print(bms.shape)
print(bms[:5])
(3, 10000)
[[-7.54397941e-01 4.98470214e-01 1.01140003e+00 ... 9.14682941e+01
9.03213906e+01 8.92281628e+01]
[ 2.12748670e-01 1.21726451e+00 3.15673183e-01 ... 1.17470082e+02
1.14698694e+02 1.16181446e+02]
[ 1.01381882e+00 1.42041385e-02 -5.38972764e-01 ... -1.61459873e+02
-1.59856643e+02 -1.60684727e+02]]
[32]:
fig, ax = plt.subplots()
for bm in bms:
ax.plot(bm)
plt.title("Random motion")
[32]:
Text(0.5, 1.0, 'Random motion')
[33]:
labels = ["MarketX","IB35","S&P500"]
fig, ax = plt.subplots()
for e,bm in enumerate(bms):
ax.plot(bm, label=labels[e])
plt.title("Random market motion")
plt.legend()
plt.show()
[34]:
# https://matplotlib.org/stable/gallery/lines_bars_and_markers/linestyles.html
labels = ["MarketX","IB35","S&P500"]
lstyle = ["solid","dashdot","dotted"]
fig, ax = plt.subplots()
for e,bm in enumerate(bms):
ax.plot(bm, label=labels[e],linestyle=lstyle[e])
plt.title("Random market motion")
plt.legend()
plt.show()
[35]:
# https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.vlines.html
# https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.grid.html
labels = ["MarketX","IB35","S&P500"]
lstyle = ["solid","dashdot","dotted"]
fig, ax = plt.subplots(figsize=(18,10)) #diferent mida
for e,bm in enumerate(bms):
ax.plot(bm, label=labels[e],linestyle=lstyle[e])
ax.vlines(x=4000,ymin=-50,ymax=50,color="red",linewidth=5)
plt.title("Random market motion")
plt.legend(prop={'size': 20}) # Alerta! Prova de llevar aquesta "prop" (propietat)
plt.grid() # grid # Alerta! El Grid moltes vegades no és necessari
plt.show()
[36]:
# Podem millorar certes visualitzacions de sèries fent filtratge de les dades
def moving_average(a, n=3) :
ret = np.cumsum(a, dtype=float)
ret[n:] = ret[n:] - ret[:-n]
return ret[n - 1:] / n
xs = np.random.randn(10000)
bms = xs.cumsum()
bmsMA = moving_average(bms,100)
fig, ax = plt.subplots(figsize=(18,10)) #diferent mida
ax.plot(bms, label="Original",color="gray")
ax.plot(bmsMA, label="Moving average",color="red",linewidth=3)
plt.title("Random market motion")
plt.legend(prop={'size': 20})
plt.show()
Biblioteca Seaborn
Seaborn es una biblioteca de visualización de datos para Python que se utiliza para crear gráficos estadísticos atractivos e informativos. Esta biblioteca simplifica la creación de gráficos como histogramas, diagramas de caja, gráficos de dispersión y muchos otros, y ofrece una interfaz fácil de usar que permite personalizar la apariencia de los gráficos.
[ ]:
%pip install seaborn
[37]:
import seaborn as sns
dfwho = pd.read_csv("data/WHO.csv", usecols=[0,1,2,185,67,4])
dfwho.columns =['Country', 'CountryID', 'Continent', 'LiteracyRate', 'TotalExpenditureHealth',
'CO2_emissions']
continents = {1:"Africa",2:"Europa",3:"Africa",4:"North America",7:"Asia",6:"Asia",5:"South America"}
dfwho.replace({"Continent": continents},inplace=True)
print(list(dfwho.columns))
print("Dimensions del dataframe:", dfwho.shape)
['Country', 'CountryID', 'Continent', 'LiteracyRate', 'TotalExpenditureHealth', 'CO2_emissions']
Dimensions del dataframe: (202, 6)
[38]:
# https://seaborn.pydata.org/generated/seaborn.histplot.html
fig, ax = plt.subplots(figsize=(10, 8)) # Establim el marc de dibuix
sns.histplot(dfwho, x="LiteracyRate")
plt.show()
[39]:
fig, ax = plt.subplots(figsize=(10, 8))
sns.histplot(dfwho,x="LiteracyRate", hue="Continent")
plt.show()
[40]:
fig, ax = plt.subplots(figsize=(10, 8))
sns.histplot(dfwho,x="TotalExpenditureHealth", hue="Continent", multiple="stack")
plt.show()
[41]:
# https://seaborn.pydata.org/generated/seaborn.violinplot.html
fig, ax = plt.subplots(figsize=(10, 8))
sns.violinplot(x=dfwho["TotalExpenditureHealth"],y=dfwho.Continent)
plt.show()
[42]:
# https://seaborn.pydata.org/generated/seaborn.rugplot.html
import seaborn as sns; sns.set_theme()
fig, ax = plt.subplots(figsize=(10, 8))
sns.scatterplot(data=dfwho, x="CO2_emissions", y="LiteracyRate",hue="Continent")
sns.rugplot(data=dfwho, x="CO2_emissions", y="LiteracyRate",hue="Continent")
plt.show()
[46]:
# Otro escenario
x = np.random.normal(size=1000)
y = np.random.normal(size=1000)
plt.scatter(x, y, c='firebrick', alpha=0.2)
plt.title("Ejemplo de scatter")
plt.show()
[47]:
#Usamos la libreria seaborn
sns.jointplot(x=x, y=y)
[47]:
<seaborn.axisgrid.JointGrid at 0x289168ca0>
Ejercicios
A) Utilizando el archivo “who.csv”, - ¿Podrías mostrar un gráfico con los 10 países más poblados (“Población (en miles) total”)? - ¿Y otro con los 10 menos poblados?
Nota: Ten en cuenta que los valores “NaN” pueden afectar la visualización.
[ ]:
B) Representa en un gráfico de barras el crecimiento de población (Population annual growth rate (%)) de los paises del top 10 más poblados.
[ ]:
C) Representa con un boxplot la contaminació (CO2) (``CO2_emissions``) de Europa (codi continent: 2)
[ ]:
D) Con el fichero ``data_groups.csv``, visualiza la distribución de ciudades y puntos de candidatos.
[ ]:
Isaac Lera and Gabriel Moya Universitat de les Illes Balears isaac.lera@uib.edu, gabriel.moya@uib.edu