6 - Visualitzacions
Pandas inclou una gran quantitat d’utilitats per a facilitar la representació gràfica d’un dataframe i de les seves sèries usant la llibreria Matplotlib. Veure documentació.
La visualització directa amb Pandas és simplement fantàstica, a causa del seu fàcil ús, per a interpretacions i informes varis. Encara que, no és prou versàtil per a controlar diversos “criteris tècnics”, en aquest cas cal complementar-la amb matplotlib. Els gràfics de Pandes són una abstracció d’aquesta llibreria.
D’altra banda, veurem alguns exemples de visualització amb una altra llibreria anomenada Seaborn.
Ara com ara, ens limitarem a parlar sobre els tipus bàsics de visualització i dels elements necessaris per facilitar la interpretació ((títols, tics als eixos, llegendes, selecció de colors, etc.)
Línies
Barres
Histogrames i *Boxplots
Scatter plots
Activitats d’escalfament.
[2]:
import pandas as pd
import numpy as np
# GENERARAMOS UNOS DATOS FICTICIOS!!!
data = {
'pais': ['Argentina', 'Brasil', 'Canadá', 'China', 'Alemania', 'India', 'Japón', 'México', 'Nigeria', 'Rusia'],
'continente': ['América del Sur', 'América del Sur', 'América del Norte', 'Asia', 'Europa', 'Asia', 'Asia', 'América del Norte', 'África', 'Europa'],
'poblacion': [45376763, 213823154, 38131104, 1395380000, 83149300, 1380004385, 126150000, 130222815, 206139587, 144096812],
'pib': [637.717, 2055.51, 1610.07, 16624.05, 4644.83, 2697.22, 4930.61, 1079.24, 514.049, 1630.03],
'contaminacion': [12.40, 9.69, 15.04, 7.57, 9.35, 17.70, 9.70, 14.87, 6.66, 11.14]
}
df = pd.DataFrame(data)
[3]:
#6.0 ¿Cual es la contaminación media por continente?
[3]:
contaminacion | |
---|---|
continente | |
América del Norte | 14.955000 |
América del Sur | 11.045000 |
Asia | 11.656667 |
Europa | 10.245000 |
África | 6.660000 |
[13]:
#6.1 ¿Que continente tiene mayor población?
[13]:
poblacion | |
---|---|
continente | |
Asia | 2901534385 |
América del Sur | 259199917 |
Europa | 227246112 |
África | 206139587 |
América del Norte | 168353919 |
[17]:
# Algunos de esos paises están la ONU
miembros_onu = ['Argentina', 'Brasil', 'China', 'Alemania', 'India', 'Japón' ] #Datos ficticios!
df['miembro_onu'] = df['pais'].apply(lambda x: 'Sí' if x in miembros_onu else 'No')
print(df)
pais continente poblacion pib contaminacion \
0 Argentina América del Sur 45376763 637.717 12.40
1 Brasil América del Sur 213823154 2055.510 9.69
2 Canadá América del Norte 38131104 1610.070 15.04
3 China Asia 1395380000 16624.050 7.57
4 Alemania Europa 83149300 4644.830 9.35
5 India Asia 1380004385 2697.220 17.70
6 Japón Asia 126150000 4930.610 9.70
7 México América del Norte 130222815 1079.240 14.87
8 Nigeria África 206139587 514.049 6.66
9 Rusia Europa 144096812 1630.030 11.14
miembro_onu
0 Sí
1 Sí
2 No
3 Sí
4 Sí
5 Sí
6 Sí
7 No
8 No
9 No
[20]:
##6.2 ¿Cual es el PIB medio por continente y por membresía en la ONU?
[20]:
pib | ||
---|---|---|
continente | miembro_onu | |
América del Norte | No | 1344.6550 |
América del Sur | Sí | 1346.6135 |
Asia | Sí | 8083.9600 |
Europa | No | 1630.0300 |
Sí | 4644.8300 | |
África | No | 514.0490 |
Plot method
[1]:
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),
"vent":np.random.choice(["N","S","E","W"],size=samples)})
df.head()
[1]:
temperatura | vent | |
---|---|---|
0 | 27 | E |
1 | 33 | S |
2 | 2 | N |
3 | -2 | E |
4 | -1 | W |
[2]:
df.plot()
[2]:
<Axes: >
[5]:
df.temperatura.plot() # Quines difèrencies a les dos gràfiques n'hi ha entre un dataframe i una sèrie?
[5]:
<AxesSubplot:>
[3]:
df.vent.plot()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[3], line 1
----> 1 df.vent.plot()
File ~/.pyenv/versions/my3110/lib/python3.11/site-packages/pandas/plotting/_core.py:1000, in PlotAccessor.__call__(self, *args, **kwargs)
997 label_name = label_kw or data.columns
998 data.columns = label_name
-> 1000 return plot_backend.plot(data, kind=kind, **kwargs)
File ~/.pyenv/versions/my3110/lib/python3.11/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/my3110/lib/python3.11/site-packages/pandas/plotting/_matplotlib/core.py:450, in MPLPlot.generate(self)
448 def generate(self) -> None:
449 self._args_adjust()
--> 450 self._compute_plot_data()
451 self._setup_subplots()
452 self._make_plot()
File ~/.pyenv/versions/my3110/lib/python3.11/site-packages/pandas/plotting/_matplotlib/core.py:635, in MPLPlot._compute_plot_data(self)
633 # no non-numeric frames or series allowed
634 if is_empty:
--> 635 raise TypeError("no numeric data to plot")
637 self.data = numeric_data.apply(self._convert_to_ndarray)
TypeError: no numeric data to plot
[4]:
df.groupby(["vent"]).size()
[4]:
vent
E 11
N 12
S 13
W 14
dtype: int64
[5]:
df.groupby(["vent"]).size().plot() #No té sentit el tipus .plot() per aquestes dades
[5]:
<Axes: xlabel='vent'>
[7]:
df.groupby(["vent"]).size().plot(kind="bar") #millor un de barres
[7]:
<Axes: xlabel='vent'>
Matplotlib
Pandas internament utilitza la llibreria on es defineixen els components d’un gràfic. Es diu Matplotlib
[18]:
import matplotlib.pyplot as plt # !
plt.plot(df.temperatura)
plt.title("Temperatura ")
plt.show()
[11]:
import matplotlib.pyplot as plt
fig, ax = plt.subplots() # Definició forma
ax.plot(df.temperatura) # Insercció de dades
plt.title("Temperatura ", fontsize=20) # Maquetació
plt.xlabel('Samples', fontsize=14)
plt.ylabel('Celsius')
plt.savefig('data/test.jpg',dpi=100) # guardat
plt.show() # Visualització
[20]:
# Vent sèrie
tipusVent = df.groupby(["vent"]).size()
print(tipusVent)
vent
E 11
N 12
S 13
W 14
dtype: int64
[21]:
x = tipusVent.index
y = tipusVent.values
fig, ax = plot.subplots() # Definició forma
ax.bar(x,y)
[21]:
<BarContainer object of 4 artists>
[22]:
fig, ax = plot.subplots() # Definició forma
ax.bar(x,y) # insercció de dades
plt.title("Intensitats del vent per direcció cardinal ", fontsize=18) # Maquetació
plt.xlabel('direcció', fontsize=14)
plt.ylabel('km/h')
plt.savefig('data/test.jpg',dpi=100) # Visualització i/o guardat
plt.show()
## Quines diferències trobes amb el bar.plot fet amb pandas?
[24]:
y
[24]:
array([11, 12, 13, 14])
[44]:
import numpy as np
import matplotlib.pyplot as plt
y = np.array([10,15,9,6])
# https://matplotlib.org/stable/gallery/pie_and_polar_charts/polar_bar.html
N = 4 #direccions
theta = np.linspace(0.0, 2 * np.pi, N, endpoint=False)
radii = y
width = np.pi / y
colors = ["red","blue","green","black"]
ax = plt.subplot(projection='polar')
ax.bar(theta, radii, width=width, bottom=0.0, color=colors, alpha=0.5)
plt.show()
# Una altra llibrería: https://plotly.com/python/polar-chart/
Tipus de visualitzacions
Barres
[45]:
df= pd.read_csv("data/WHO.csv")
co2 = df["Total_CO2_emissions"]
ticks_labels = df["Country"][6:10].values
ax = co2[6:10].plot(kind="bar") # un plot retorna el component AX
ax.set_xticklabels(ticks_labels)
plt.show()
[48]:
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()
# Com podem canviar les etiquetes de los eixos?
Histogrames
https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.hist.html
[57]:
co2 = df["Total_CO2_emissions"]
fig, ax = plot.subplots() # Definició forma
ax.hist(co2.values,bins=50) # insercció de dades
plt.show()
Boxplots
[65]:
co2 = df["Total_CO2_emissions"]
fig, ax = plt.subplots(ncols=2)
ax[0].boxplot(co2.values*1000)
ax[1].hist(co2)
plt.show()
# On estan els valors ?
[66]:
co2.head(10) # Quins valors tenim?
[66]:
0 692.50
1 3499.12
2 137535.56
3 NaN
4 8991.46
5 421.36
6 152711.86
7 4345.50
8 368858.53
9 73602.43
Name: Total_CO2_emissions, dtype: float64
[64]:
co2.dropna(inplace=True) ## VALORS DESCONEGUTS => NaN (NotAtNumber)
fig, ax = plt.subplots(ncols=2)
ax[0].boxplot(co2.values*1000)
ax[1].hist(co2)
plt.show()
Lineals
[75]:
import numpy as np
xs = np.random.randn(3, 10000)
print(xs.shape)
print(xs[:5])
(3, 10000)
[[-0.46183615 0.3178843 0.62683285 ... -1.27423201 1.67868375
-0.33164508]
[-0.53968937 -0.54150265 0.30693369 ... 0.57017049 0.38711088
-1.42256073]
[ 0.29354989 -0.34311475 -1.01271945 ... 0.49504346 0.83391768
-0.31889334]]
[76]:
bms = xs.cumsum(axis=1)
print(bms.shape)
print(bms[:5])
(3, 10000)
[[-4.61836153e-01 -1.43951857e-01 4.82880998e-01 ... 4.76108041e+01
4.92894879e+01 4.89578428e+01]
[-5.39689373e-01 -1.08119203e+00 -7.74258341e-01 ... 1.26704022e+02
1.27091133e+02 1.25668572e+02]
[ 2.93549889e-01 -4.95648581e-02 -1.06228431e+00 ... 8.45321035e+01
8.53660212e+01 8.50471279e+01]]
[79]:
fig, ax = plt.subplots()
for bm in bms:
ax.plot(bm)
plt.title("Random motion")
[79]:
Text(0.5, 1.0, 'Random motion')
[84]:
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()
[86]:
# 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()
[98]:
# 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 cal, depèn de la interpretació
plt.show()
[160]:
# 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()
Llibrería Seaborn
[12]:
%pip install seaborn
Requirement already satisfied: seaborn in /Users/isaac/.pyenv/versions/my3110/lib/python3.11/site-packages (0.13.1)
Requirement already satisfied: numpy!=1.24.0,>=1.20 in /Users/isaac/.pyenv/versions/my3110/lib/python3.11/site-packages (from seaborn) (1.23.5)
Requirement already satisfied: pandas>=1.2 in /Users/isaac/.pyenv/versions/my3110/lib/python3.11/site-packages (from seaborn) (1.5.3)
Requirement already satisfied: matplotlib!=3.6.1,>=3.4 in /Users/isaac/.pyenv/versions/my3110/lib/python3.11/site-packages (from seaborn) (3.8.1)
Requirement already satisfied: contourpy>=1.0.1 in /Users/isaac/.pyenv/versions/my3110/lib/python3.11/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (1.2.0)
Requirement already satisfied: cycler>=0.10 in /Users/isaac/.pyenv/versions/my3110/lib/python3.11/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (0.12.1)
Requirement already satisfied: fonttools>=4.22.0 in /Users/isaac/.pyenv/versions/my3110/lib/python3.11/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (4.44.0)
Requirement already satisfied: kiwisolver>=1.3.1 in /Users/isaac/.pyenv/versions/my3110/lib/python3.11/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (1.4.5)
Requirement already satisfied: packaging>=20.0 in /Users/isaac/.pyenv/versions/my3110/lib/python3.11/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (23.1)
Requirement already satisfied: pillow>=8 in /Users/isaac/.pyenv/versions/my3110/lib/python3.11/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (9.5.0)
Requirement already satisfied: pyparsing>=2.3.1 in /Users/isaac/.pyenv/versions/my3110/lib/python3.11/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (2.4.7)
Requirement already satisfied: python-dateutil>=2.7 in /Users/isaac/.pyenv/versions/my3110/lib/python3.11/site-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (2.8.2)
Requirement already satisfied: pytz>=2020.1 in /Users/isaac/.pyenv/versions/my3110/lib/python3.11/site-packages (from pandas>=1.2->seaborn) (2023.3)
Requirement already satisfied: six>=1.5 in /Users/isaac/.pyenv/versions/my3110/lib/python3.11/site-packages (from python-dateutil>=2.7->matplotlib!=3.6.1,>=3.4->seaborn) (1.16.0)
[notice] A new release of pip is available: 24.0 -> 24.2
[notice] To update, run: pip install --upgrade pip
Note: you may need to restart the kernel to use updated packages.
[13]:
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(dfwho.columns)
print(dfwho.shape)
print(dfwho.head())
Index(['Country', 'CountryID', 'Continent', 'LiteracyRate',
'TotalExpenditureHealth', 'CO2_emissions'],
dtype='object')
(202, 6)
Country CountryID Continent LiteracyRate TotalExpenditureHealth \
0 Afghanistan 1 Africa 28.0 5.4
1 Albania 2 Europa 98.7 6.2
2 Algeria 3 Africa 69.9 3.6
3 Andorra 4 Europa NaN 6.3
4 Angola 5 Africa 67.4 2.7
CO2_emissions
0 0.02
1 0.98
2 4.23
3 NaN
4 0.76
[149]:
# https://seaborn.pydata.org/generated/seaborn.histplot.html
fig, ax = plt.subplots(figsize=(10, 8))
sns.histplot(dfwho,x="LiteracyRate", kde=True)
plt.show()
[148]:
fig, ax = plt.subplots(figsize=(10, 8))
sns.histplot(dfwho,x="LiteracyRate", hue="Continent")
plt.show()
[162]:
fig, ax = plt.subplots(figsize=(10, 8))
sns.histplot(dfwho,x="TotalExpenditureHealth", hue="Continent", multiple="stack")
plt.show()
[161]:
# 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()
[159]:
# 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()
## Africa valors?
Activitats 5.1
Amb el fitxer who.csv,
Podries mostrar els 10 països més poblats (“Population (in thousands) total”)?
I els 10 menys poblats?
Nota: Hi ha paises on es desconeix la contaminació que generin (valors NaN) i afectaran la visualització.
[ ]:
#TODO
Representa en un gràfic de barres el creixement de població (Population annual growth rate (%)) dels països del top 10 més poblats.
[ ]:
#TODO
Representa amb un boxplot la contaminació (CO2) d’Europa (codi continent: 2)
[ ]:
#TODO
Amb el fitxer 4/data/data_groups.csv, visualitza la distribució de ciutats i punts de candidats
[ ]:
#TODO
Amb el fitxer 4/data/data_groups.csv, visualitza la distribució de ciutats, punts de candidats i sexe.
[164]:
#TODO
Isaac Lera and Gabriel Moya Universitat de les Illes Balears isaac.lera@uib.edu, gabriel.moya@uib.edu