Visualización

66968072043a4d9e90d717210d10ce83

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;
../../../_images/notebooks_Part2_00_Pandas_04_Visualizacion_3_0.png
[15]:
df.temperatura.plot();
../../../_images/notebooks_Part2_00_Pandas_04_Visualizacion_4_0.png

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'>
../../../_images/notebooks_Part2_00_Pandas_04_Visualizacion_8_1.png

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
../../../_images/notebooks_Part2_00_Pandas_04_Visualizacion_10_0.png
[20]:
df.temperatura.plot(kind="hist"); # Si los datos son numéricos un histograma es práctico
../../../_images/notebooks_Part2_00_Pandas_04_Visualizacion_11_0.png

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'>
../../../_images/notebooks_Part2_00_Pandas_04_Visualizacion_13_1.png

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ó
../../../_images/notebooks_Part2_00_Pandas_04_Visualizacion_15_0.png
[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]
../../../_images/notebooks_Part2_00_Pandas_04_Visualizacion_17_1.png

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()
../../../_images/notebooks_Part2_00_Pandas_04_Visualizacion_19_0.png

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()
../../../_images/notebooks_Part2_00_Pandas_04_Visualizacion_22_0.png

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()
../../../_images/notebooks_Part2_00_Pandas_04_Visualizacion_24_0.png

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()
../../../_images/notebooks_Part2_00_Pandas_04_Visualizacion_26_0.png

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 ?
../../../_images/notebooks_Part2_00_Pandas_04_Visualizacion_28_0.png

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')
../../../_images/notebooks_Part2_00_Pandas_04_Visualizacion_32_1.png
[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()
../../../_images/notebooks_Part2_00_Pandas_04_Visualizacion_33_0.png
[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()

../../../_images/notebooks_Part2_00_Pandas_04_Visualizacion_34_0.png
[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()
../../../_images/notebooks_Part2_00_Pandas_04_Visualizacion_35_0.png
[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()

../../../_images/notebooks_Part2_00_Pandas_04_Visualizacion_36_0.png

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.

Página web

[ ]:
%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()
../../../_images/notebooks_Part2_00_Pandas_04_Visualizacion_40_0.png
[39]:
fig, ax = plt.subplots(figsize=(10, 8))
sns.histplot(dfwho,x="LiteracyRate", hue="Continent")
plt.show()
../../../_images/notebooks_Part2_00_Pandas_04_Visualizacion_41_0.png
[40]:
fig, ax = plt.subplots(figsize=(10, 8))
sns.histplot(dfwho,x="TotalExpenditureHealth", hue="Continent", multiple="stack")
plt.show()
../../../_images/notebooks_Part2_00_Pandas_04_Visualizacion_42_0.png
[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()
../../../_images/notebooks_Part2_00_Pandas_04_Visualizacion_43_0.png
[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()
../../../_images/notebooks_Part2_00_Pandas_04_Visualizacion_44_0.png
[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()
../../../_images/notebooks_Part2_00_Pandas_04_Visualizacion_45_0.png
[47]:
#Usamos la libreria seaborn
sns.jointplot(x=x, y=y)

[47]:
<seaborn.axisgrid.JointGrid at 0x289168ca0>
../../../_images/notebooks_Part2_00_Pandas_04_Visualizacion_46_1.png

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.

[ ]:

License: CC BY 4.0 Isaac Lera and Gabriel Moya Universitat de les Illes Balears isaac.lera@uib.edu, gabriel.moya@uib.edu