pandas es una biblioteca de análisis de datos en Python que nos provee de las estructuras de datos y herramientas para realizar análisis de manera rápida. Se articula sobre la biblioteca NumPy y nos permite enfrentarnos a situaciones en las que tenemos que manejar datos reales que requieren seguir un proceso de carga, limpieza, filtrado, reduccióń y análisis.
En esta clase veremos como cargar y guardar datos, las características de las pricipales estructuras de pandas y las aplicaremos a algunos problemas.
Se trata de una biblioteca muy extensa y que sigue evolucionando, por lo que lleva tiempo conocer todas las posibilidades que ofrece. La mejor forma de aprender pandas
es usándolo, por lo que ¡nos ahorraremos la introducción e iremos directos al grano!
In [1]:
# Importamos pandas
%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt
Trabajaremos sobre un fichero de datos metereológicos de la Consejeria Agricultura Pesca y Desarrollo Rural Andalucía.
In [2]:
from IPython.display import HTML
HTML('<iframe src="http://www.juntadeandalucia.es/agriculturaypesca/ifapa/ria/servlet/FrontController?action=Static&url=coordenadas.jsp&c_provincia=4&c_estacion=4" width="700" height="400"></iframe>')
Out[2]:
In [3]:
# Vemos qué pinta tiene el fichero
# (esto es un comando de la terminal, no de python
# y solo funcionará en Linux o MAC)
!head ../data/tabernas_meteo_data.txt
Vemos que los datos no están en formato CSV, sino que la delimitación son espacios. Si intentamos cargarlos con pandas no tendremos mucho éxito:
In [4]:
# Tratamos de cargarlo en pandas
pd.read_csv("../data/tabernas_meteo_data.txt").head(5)
Out[4]:
Tenemos que hacer los siguientes cambios:
La aproximación clásica a este tipo de problemas es hacer una lectura línea a línea en la que vayamos "parseando" los datos del fichero, de acuerdo al formato que esperamos recibir y nos protejamos cuando, no se cumpla la estructura.
Sin embargo, gracias a pandas
podremos reducir nuestro esfuerzo drásticamente, dando a la función read_csv
algunas indicaciones sobre el formato de nuestros datos
In [5]:
data = pd.read_csv(
"../data/tabernas_meteo_data.txt",
delim_whitespace=True, # delimitado por espacios en blanco
usecols=(0, 2, 3, 4, 5), # columnas que queremos usar
skiprows=2, # saltar las dos primeras líneas
names=['DATE', 'TMAX', 'TMIN', 'TMED', 'PRECIP'],
parse_dates=['DATE'],
# date_parser=lambda x: pd.datetime.strptime(x, '%d-%m-%y'), # Parseo manual
dayfirst=True, # ¡Importante
index_col=["DATE"] # Si queremos indexar por fechas
)
# Ordenando de más antigua a más moderna
data.sort_index(inplace=True)
# Mostrando sólo las primeras o las últimas líneas
data.head()
Out[5]:
Las fechas también se pueden parsear de manera manual con el argumento:
date_parser=lambda x: pd.datetime.strptime(x, '%d-%m-%y'), # Parseo manual
In [6]:
# Comprobamos los tipos de datos de la columnas
data.dtypes
Out[6]:
In [7]:
# Pedomos información general del dataset
data.info()
En una dataframe pueden convivir datos de tipo diferente en diferentes columnas: en nuestro caso, fechas (en el índice) y (flotantes en las columnas). El que un dato sea de tipo fecha y no un string u otra cosa, nos permite obtener información como el día de la semana de manera directa:
In [8]:
data.index.dayofweek
Out[8]:
Una vez hemos cargado los datos, estamos preparados para analizarlos utilizando toda la artillería de pandas
. Por ejemplo, puede que queramos una descripción estadística rápida:
In [9]:
# Descripción estadística
data.describe()
Out[9]:
Tenemos dos formas de acceder a las columnas: por nombre o por atributo (si no contienen espacios ni caracteres especiales).
In [10]:
# Accediendo como clave
data['TMAX'].head()
Out[10]:
In [11]:
# Accediendo como atributo
data.TMIN.head()
Out[11]:
In [12]:
# Accediendo a varias columnas a la vez
data[['TMAX', 'TMIN']].head()
Out[12]:
Del mismo modo que accedmos, podemos operar con ellos:
In [13]:
# Modificando valores de columnas
data[['TMAX', 'TMIN']] / 10
Out[13]:
e introducirlos en funciones:
In [14]:
# Aplicando una función a una columna entera (ej. media numpy)
import numpy as np
np.mean(data.TMAX)
Out[14]:
In [15]:
# Calculando la media con pandas
data.TMAX.mean()
Out[15]:
Para acceder a las filas tenemos dos métodos: .loc
(basado en etiquetas), .iloc
(basado en posiciones enteras) y (.ix
(que combina ambos).ix
ha desaparecido en la versión 0.20).
In [16]:
# Accediendo a una fila por índice
data.iloc[1]
Out[16]:
In [17]:
# Accediendo a una fila por etiqueta
data.loc["2016-09-02"]
Out[17]:
Puedo incluso hacer secciones basadas en fechas:
In [18]:
data.loc["2016-12-01":]
Out[18]:
También puedo indexar utilizando arrays de valores booleanos, por ejemplo procedentes de la comprobación de una condición:
In [19]:
# Comprobando que registros carecen de datos válidos
data.TMIN.isnull().head()
Out[19]:
In [20]:
# Accediendo a los registros que cumplen una condición
data.loc[data.TMIN.isnull()]
Out[20]:
In [21]:
# Valores de precipitación por encima de la media:
print(data.PRECIP.mean())
data[data.PRECIP > data.PRECIP.mean()]
Out[21]:
Por último, pandas proporciona métodos para calcular magnitudes como medias móviles usando el método rolling
:
In [22]:
# Calcular la media de la columna TMAX
data.TMAX.head(15)
Out[22]:
In [23]:
# Media trimensual centrada
data.TMAX.rolling(5, center=True).mean().head(15)
Out[23]:
In [24]:
# Agruparemos por año y día: creemos dos columnas nuevas
data['year'] = data.index.year
data['month'] = data.index.month
En muchas ocasiones queremos realizar agrupaciones de datos en base a determinados valores como son fechas, o etiquetas (por ejemplo, datos que pertenecen a un mismo ensayo o lugar)
Podemos agrupar nuestros datos utilizando groupby
:
In [25]:
# Creamos la agrupación
monthly = data.groupby(by=['year', 'month'])
In [26]:
# Podemos ver los grupos que se han creado
monthly.groups.keys()
Out[26]:
Con estos grupos podemos hacer hacer varias cosas:
In [27]:
# Accedemos a un grupo
monthly.get_group((2016,3)).head()
Out[27]:
In [28]:
# Hhacemos una agregación de los datos:
monthly_mean = monthly.mean()
monthly_mean.head(24)
Out[28]:
En ocasiones podemos querer ver nuestros datos de forma diferente o necesitamos organizarlos así para utilizar determinadas funciones de pandas
. Una necesidad típica es la de pivotar una tabla.
Imagina que queremos acceder a los mismos datos que en el caso anterior, pero que ahora queremos ver los años en las filas y para cada variable (TMAX, TMED...) los calores de cada mes en una columna. ¿Cómo lo harías?
In [29]:
# Dejar los años como índices y ver la media mensual en cada columna
monthly_mean.reset_index().pivot(index='year', columns='month')
Out[29]:
La línea anterior no es sencilla y no se escribe de una sola vez sin errores (sobre todo si estás empezando). Esto es una ejemplo de que pandas
es una librería potente, pero que lleva tiempo aprender. Pasarás muchas horas peleando contra problemas de este tipo, pero afortunadamente mucha gente lo ha pasado mal antes y su experiencia ha quedado plasmada en cientos de preguntas de stack overflow
y en la documentación de pandas
In [30]:
# Pintar la temperatura máx, min, med
data.plot(y=["TMAX", "TMIN", "TMED"])
plt.title('Temperaturas')
Out[30]:
In [31]:
data.loc[:, 'TMAX':'PRECIP'].plot.box()
Out[31]:
Pintando la temperatura máxima de las máximas, mínima de las mínimas, media de las medias para cada día del año de los años disponnibles
In [32]:
group_daily = data.groupby(['month', data.index.day])
daily_agg = group_daily.agg({'TMED': 'mean', 'TMAX': 'max', 'TMIN': 'min', 'PRECIP': 'mean'})
daily_agg.head()
Out[32]:
In [33]:
daily_agg.plot(y=['TMED', 'TMAX', 'TMIN'])
Out[33]:
In [34]:
# scatter_matrix
from pandas.tools.plotting import scatter_matrix
axes = scatter_matrix(data.loc[:, "TMAX":"TMED"])
Algunos enlaces: