Visualización con python

In [1]:
%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns

Cargamos los dataframes con los que haremos los ejemplos...

In [2]:
#Características de unos cuantos coches
mtcars=pd.read_csv('mtcars.csv')

Vamos a cargar un archivo con datos de coeficientes hidrodinámicos de unos cuantos yates medidos en canal de ensayos.

https://archive.ics.uci.edu/ml/datasets/Yacht+Hydrodynamics

No conozco bien las condiciones en las que se generó este conjunto de datos, pero espero que lo podamos entender un poco mejor gracias a las visualizaciones.

In [3]:
#Datos de coeficientes hidrodinámicos de unos cuantos yates
# medidos en canal de ensayos
yacht = pd.read_csv('yacht_hydrodynamics.csv')

Realizamos un análisis rápido:

  • yacht.head() muestra las primeras líneas
  • yacht.shape cuenta el número de registros, y el número de columnas (cuántos valores se han medido para cada registro)
  • yacht.info() muestra el tipo de datos de cada columna
In [4]:
print(yacht.head())
print('-----')
print(yacht.shape)
print('-----')
print(yacht.info())
   center  prismatic  LD ratio  BD ratio  LB ratio  Froude  resistence
0    -2.3      0.568      4.78      3.99      3.17   0.125        0.11
1    -2.3      0.568      4.78      3.99      3.17   0.150        0.27
2    -2.3      0.568      4.78      3.99      3.17   0.175        0.47
3    -2.3      0.568      4.78      3.99      3.17   0.200        0.78
4    -2.3      0.568      4.78      3.99      3.17   0.225        1.18
-----
(308, 7)
-----
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 308 entries, 0 to 307
Data columns (total 7 columns):
center        308 non-null float64
prismatic     308 non-null float64
LD ratio      308 non-null float64
BD ratio      308 non-null float64
LB ratio      308 non-null float64
Froude        308 non-null float64
resistence    308 non-null float64
dtypes: float64(7)
memory usage: 16.9 KB
None
In [5]:
mtcars.head()
Out[5]:
model mpg cyl disp hp drat wt qsec vs am gear carb
0 Mazda RX4 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4
1 Mazda RX4 Wag 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4
2 Datsun 710 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
3 Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
4 Hornet Sportabout 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2
In [6]:
mtcars.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 32 entries, 0 to 31
Data columns (total 12 columns):
model    32 non-null object
mpg      32 non-null float64
cyl      32 non-null int64
disp     32 non-null float64
hp       32 non-null int64
drat     32 non-null float64
wt       32 non-null float64
qsec     32 non-null float64
vs       32 non-null int64
am       32 non-null int64
gear     32 non-null int64
carb     32 non-null int64
dtypes: float64(5), int64(6), object(1)
memory usage: 3.1+ KB

Transformamos las columnas con variables categóricas ('vs','carb','am', 'cyl', 'gear') al tipo correcto

In [7]:
a_transformar=['vs','carb','am','cyl','gear']
In [8]:
for columna in a_transformar:
    mtcars[columna] = mtcars[columna].astype('category')
In [9]:
mtcars.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 32 entries, 0 to 31
Data columns (total 12 columns):
model    32 non-null object
mpg      32 non-null float64
cyl      32 non-null category
disp     32 non-null float64
hp       32 non-null int64
drat     32 non-null float64
wt       32 non-null float64
qsec     32 non-null float64
vs       32 non-null category
am       32 non-null category
gear     32 non-null category
carb     32 non-null category
dtypes: category(5), float64(5), int64(1), object(1)
memory usage: 2.6+ KB

Gráficas con pandas y matplotlib

Pandas (a través de matplotlib) incorpora una serie de funciones gráficas que pueden ser útiles cuando queremos realizar un gráfico sencillo. Si no se le da más instrucciones, visualiza una gráfica con la columna índice en el eje de abscisas y todas las demás columnas en las ordenadas. Este tipo de gráfica puede resultar útil para detectar fallos al importar, pero a menudo es poco iluminador, especialmente si las columnas se miden en unidades muy distintas.

In [21]:
yacht.plot(figsize=(8,8))
plt.show()

Los gráficos de barras se obtienen con la función plot.bar. Son útiles para visualizar los valores de cada una de las observaciones

In [22]:
#Al poner yacht.iloc[:10] dibujamos solo los 10 primeros registros
yacht.iloc[:10].plot.bar(figsize=(10,10))
Out[22]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fc462547a20>

Como puede observarse estas funciones gráficas sólo responden a las variables continuas. Esta es una primera (y obvia) limitación.


Ejercicio

  • Visualizar:
    • La serie de las variables mpg, wt, hp
    • Con una gráfica de barras, los valores de las columnas mpg, wt, hp de los 5 primeros coches
In [ ]:
 
In [ ]:
 
In [ ]:
 

Distribución de variables continuas 1-d:

Si queremos visualizar la distribución de una variable continua, normalmente usaremos alguna de estas funciones

  • ‘hist’ para histogramas.
  • ‘kde’ o 'density' para gráficas de densidad (la variante continua del histograma, recordad que vimos que los histogramas son muy sensibles a la distribución de los valores en cajas).
  • ‘box’ para boxplot.
In [27]:
yacht.plot.hist(y = ['resistence'], bins = 30)
plt.show()
## Pandas puede mostrar varias variables en el mismo histograma
/usr/local/lib/python3.5/dist-packages/pandas/plotting/_core.py:1714: UserWarning: Pandas doesn't allow columns to be created via a new attribute name - see https://pandas.pydata.org/pandas-docs/stable/indexing.html#attribute-access
  series.name = label
In [28]:
yacht.plot.density(y = 'resistence')
plt.show()

Las gráficas de cajas (boxplot) muestran una caja delimitada por el primer y el tercer cuartil, con una línea interior a la caja que marca la mediana. Los dos bigotes (whiskers) contienen al resto de los datos, excepto los datos extremos (outliers), que son las observaciones que distan del primer y tercer cuartil más de un RIC (rango intercuartílico, la longitud de la caja) y medio.

'Diagrama de caja' en la wikipedia

In [29]:
yacht.plot.box(y = 'resistence')
plt.show()

Puede ser útil, para empezar, dibujar un diagrama de las marginales de cada variable por separado.

In [46]:
fig, axes = plt.subplots(nrows=1, ncols=len(yacht.columns), figsize=(15,8))
for ax,col in zip(axes, yacht.columns):
    ax.boxplot(yacht[col])
    ax.set_title(col)

plt.legend()
plt.show()

Podemos hacer un histograma en vez de un boxplot:

In [55]:
fig, axes = plt.subplots(nrows=1, ncols=len(yacht.columns), figsize=(15,8))
for ax,col in zip(axes, yacht.columns):
    ax.hist(yacht[col], bins=20)
    ax.set_title(col)

plt.legend()
plt.show()

Distribución de variables continuas 2-d

Podemos visualizar el histograma en dos dimensiones por medio de un diagrama de dispersión (scatter plot)

In [30]:
yacht.plot.scatter(y = 'resistence', x = 'Froude')
plt.show()

La librería seaborn permite dibujar las relaciones entre cada par de variables. En la diagonal aparecen los histogramas.

In [56]:
sns.pairplot(yacht)
Out[56]:
<seaborn.axisgrid.PairGrid at 0x7fc44ee832e8>

Ejercicio

Sobre el dataset mtcars, visualizar:

  • La función de densidad de la variable hp
  • La relación que existe entre las variables mpg y wt
  • ¿Hay una relación clara entre transmisión manual (am=1) o automática (am=0), y consumo del vehículo?
In [ ]:
 
In [ ]:
 
In [ ]:
 

Series temporales con un patrón estacional

Vamos a hacer unas gráficas de la extensión del hielo en los océanos ártico y antártico, y luego os pediremos que hagáis un análisis similar para otra serie temporal con patrón estacional: el oleaje en una boya de la Bahía de Cádiz.

In [149]:
#Extensión del hielo en los polos
ice = pd.read_csv('seaice.csv')
ice.head()
Out[149]:
Year Month Day Extent Missing Source Data hemisphere
0 1978 10 26 10.231 0.0 ftp://sidads.colorado.edu/pub/DATASETS/nsidc00... north
1 1978 10 28 10.420 0.0 ftp://sidads.colorado.edu/pub/DATASETS/nsidc00... north
2 1978 10 30 10.557 0.0 ftp://sidads.colorado.edu/pub/DATASETS/nsidc00... north
3 1978 11 1 10.670 0.0 ftp://sidads.colorado.edu/pub/DATASETS/nsidc00... north
4 1978 11 3 10.777 0.0 ftp://sidads.colorado.edu/pub/DATASETS/nsidc00... north

Pregunta: mirando el output de describe(include='all'): ¿qué puedes decir sobre las columnas "Missing" y "hemisphere"?

In [150]:
ice.describe(include='all')
Out[150]:
Year Month Day Extent Missing Source Data hemisphere
count 23860.000000 23860.000000 23860.000000 23860.000000 23860.0 23860 23860
unique NaN NaN NaN NaN NaN 23860 2
top NaN NaN NaN NaN NaN ftp://sidads.colorado.edu/pub/DATASETS/nsidc00... south
freq NaN NaN NaN NaN NaN 1 11930
mean 1998.851635 6.550293 15.742917 11.603068 0.0 NaN NaN
std 9.951976 3.447684 8.802258 4.586068 0.0 NaN NaN
min 1978.000000 1.000000 1.000000 2.264000 0.0 NaN NaN
25% 1991.000000 4.000000 8.000000 7.747750 0.0 NaN NaN
50% 1999.000000 7.000000 16.000000 12.297000 0.0 NaN NaN
75% 2007.000000 10.000000 23.000000 15.197250 0.0 NaN NaN
max 2015.000000 12.000000 31.000000 20.201000 0.0 NaN NaN

Los datos contienen columnas para el año, mes y día. Puede ser interesante crear un campo Date que contenga toda la fecha.

In [147]:
ice['Date'] = pd.to_datetime(ice[['Year','Month','Day']])
ice.head()
Out[147]:
Year Month Day Extent Missing Source Data hemisphere Date
0 1978 10 26 10.231 0.0 ftp://sidads.colorado.edu/pub/DATASETS/nsidc00... north 1978-10-26
1 1978 10 28 10.420 0.0 ftp://sidads.colorado.edu/pub/DATASETS/nsidc00... north 1978-10-28
2 1978 10 30 10.557 0.0 ftp://sidads.colorado.edu/pub/DATASETS/nsidc00... north 1978-10-30
3 1978 11 1 10.670 0.0 ftp://sidads.colorado.edu/pub/DATASETS/nsidc00... north 1978-11-01
4 1978 11 3 10.777 0.0 ftp://sidads.colorado.edu/pub/DATASETS/nsidc00... north 1978-11-03

Parece conveniente separar los datos del hemisferio norte y del hemisferio sur.

In [151]:
icen = ice[ice.hemisphere=='north']
ices = ice[ice.hemisphere=='south']
In [152]:
plt.figure(figsize=(9,9))
icen.Extent.plot()
plt.figure(figsize=(9,9))
ices.Extent.plot()
Out[152]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fc44554b898>

Es difícil detectar la tendencia histórica cuando la variación inter-anual es tan fuerte, así que dibujamos los datos de un sólo mes:

In [153]:
plt.scatter(x=icen[icen.Month==1].Year, y=icen[icen.Month==1].Extent, s=3)
Out[153]:
<matplotlib.collections.PathCollection at 0x7fc44655d630>

Otra forma de representar los datos, usando Seaborn y boxplot

In [160]:
plt.figure(figsize=(9,9))
sns.boxplot(x="Year", y="Extent", data=icen[icen.Month==1])
sns.despine(offset=10, trim=True)

Los datos de hielo del hemisferio sur no son tan dramáticos:

In [154]:
plt.scatter(x=ices[ices.Month==6].Year, y=ices[ices.Month==6].Extent, s=3)
Out[154]:
<matplotlib.collections.PathCollection at 0x7fc446543c18>

En fin, parece que el hielo del Ártico disminuye en extensión (y en grosor), pero el del Antártico aumenta en extensión (y el balance global es negativo):

https://www.nasa.gov/content/goddard/nasa-study-shows-global-sea-ice-diminishing-despite-antarctic-gains

Ejercicio

Más abajo se cargan datos de cierta boya de la Bahía de Cádiz:

  • Investiga si la intensidad del oleaje y del viento son más fuertes en algunos meses, o a algunas horas. Si es así: ¿cuándo esperas tener mayor producción con un generador eólico, o hundimotriz?
    • Hm0 : Altura significante Espectral (m)
    • VelV : Velocidad Media del Viento (m/s)
In [159]:
simar=pd.read_csv('/home/OYE/SIMAR_6006052.csv')
simar.head()
Out[159]:
AA MM DD HH Hm0 Tm02 Tp DirM Hm0_V DirM_V Hm0_F1 Tm02_F1 DirM_F1 Hm0_F2 Tm02_F2 DirM_F2 VelV DirV
0 2005 12 13 15 0.3 10.9 12.1 242.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 2005 12 13 18 0.3 10.3 12.0 237.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2 2005 12 13 21 0.3 10.3 11.8 238.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
3 2005 12 14 0 0.3 10.7 11.6 241.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
4 2005 12 14 3 0.3 10.6 11.4 239.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
In [ ]:
 
In [ ]: