Introducción al Dataframe de Pandas con Python (I)
En esta entrada vamos a trabajar con Pandas, una estructura de datos fundamental para trabajar organizar los datasets en DataScience. Es una libreria muy completa, muy documentada en multitud de blogs y que veremos como nos facilita el trabajo. Empecemos!:
import pandas as pd import numpy as np
Hay varias maneras para crear un Pandas
, veamos aquí la forma más ‘estandar’.
Los datos los introducimos como vectores, en este caso 4 vectores de tres datos cada uno. Introducimos los nombre de los ídices correspondientes, y el nombre de cada una de las columnas :
datos=[[1,2,3,'hola'],[5,6,7,'adios'],[8,9,10,'bienvenido'],[11,12,13,'hasta luego'],[20,30,40,'hasta nunca']] panda=pd.DataFrame(datos,index=['primero','segundo','tercero','cuarto','quinto'],columns=list('ABCD'))
panda
A | B | C | D | |
---|---|---|---|---|
primero | 1 | 2 | 3 | hola |
segundo | 5 | 6 | 7 | adios |
tercero | 8 | 9 | 10 | bienvenido |
cuarto | 11 | 12 | 13 | hasta luego |
quinto | 20 | 30 | 40 | hasta nunca |
Eso es un dataframe
del la libreria pandas
. Se compone de una serie de filas, indexadas por un índice (index), de datos organizados por columnas. Los datos pueden ser, como hemos comprobado, de cualquier tipo. La clase pandas
nos permite multitud de operaciones : gestionar filas, columnas, columnas en funcion de otras etc..
Dimensiones del pandas
en formato (row x columns):
panda.shape
(5, 4)
Comprobemos el typo de cada una de las columnas :
panda.dtypes
A int64
B int64
C int64
D object
dtype: object
Lista de los índices, que podemos iterar como cualquier iterator
:
panda.index
Index(['primero', 'segundo', 'tercero', 'cuarto', 'quinto'], dtype='object')
for i in panda.index: print (i)
primero
segundo
tercero
cuarto
quinto
De igual manera podemos conseguir la lista de la columnas :
panda.columns
Index(['A', 'B', 'C', 'D'], dtype='object')
for i in panda.columns: print (i)
A
B
C
D
Como accedemos a una determinada fila del DataFrame
?. Veamos dos procedimientos : el primero utilizando el nº de orden de la fila dentro del DataFrame
, el segundo invocando directamente con el índice :
panda.iloc[0]
A 1
B 2
C 3
D hola
Name: primero, dtype: object
panda.loc['primero']
A 1
B 2
C 3
D hola
Name: primero, dtype: object
En el caso de que los índices sean secuenciales y empezando por 0, se podrán utilizar indistintamente los dos métodos anteriores. Pero es importante saber que se está haciendo y por qué para (imaginad el caso en el que se borra una fila intermedia!).
De igual manera que ocurre con las list
se puede llamar a la última fila con el índice -1
:
panda.iloc[-1]
A 20
B 30
C 40
D hasta nunca
Name: quinto, dtype: object
Podemos seleccionar una de las columnas :
panda['A']
primero 1
segundo 5
tercero 8
cuarto 11
quinto 20
Name: A, dtype: int64
..o varias, pasándolas como lista :
panda[['A','C']]
A | C | |
---|---|---|
primero | 1 | 3 |
segundo | 5 | 7 |
tercero | 8 | 10 |
cuarto | 11 | 13 |
quinto | 20 | 40 |
Una tarea muy frecuente en DataScience es la necesidad de introducir una columna adicional como función de una o varias columnas existentes. Veamos varias maneras de hacerlo :
En muchos casos de puede expresar simplemente como operaciones aritméticas de otras columnas
panda['A+B-C']=panda['A']+panda['B']-panda['C']
Que equivaldría a este método con apply
y lambda
:
panda['A+B-C lambda']=panda.apply(lambda x: (x['A']+x['B']-x['C']),axis=1)
Añadimos una columna con la longitud de la palabra de la columna D:
panda['long D']=panda.apply(lambda x:len(x['D']),axis=1)
O complicándolo un poco más : añadimos una columna con la longitud de la palabra más la columna A :
panda['long D +1']=panda.apply(lambda x:x['A']+len(x['D']),axis=1)
Por último vamos añadir una columna ‘booleana’ que nos indique si el valor en la columna C es par :
panda['C even']=panda['C'] % 2 == 0
panda
A | B | C | D | A+B-C | A+B-C lambda | long D | long D +1 | C even | |
---|---|---|---|---|---|---|---|---|---|
primero | 1 | 2 | 3 | hola | 0 | 0 | 4 | 5 | False |
segundo | 5 | 6 | 7 | adios | 4 | 4 | 5 | 10 | False |
tercero | 8 | 9 | 10 | bienvenido | 7 | 7 | 10 | 18 | True |
cuarto | 11 | 12 | 13 | hasta luego | 10 | 10 | 11 | 22 | False |
quinto | 20 | 30 | 40 | hasta nunca | 10 | 10 | 11 | 31 | True |
Podemos realizar algunos cálculos con los valores de las columnas :
panda['A'].sum()
45
O mejor aún, realizar unos cálculos estadísticos sobre todas las columnas que tengan valores numéricos. Esta función es muy útil al empezar a trabajar con un dataset desconocido,para tener una primera idea de los que tenemos entre manos :
panda.describe()
A | B | C | A+B-C | A+B-C lambda | long D | long D +1 | |
---|---|---|---|---|---|---|---|
count | 5.00000 | 5.000000 | 5.000000 | 5.000000 | 5.000000 | 5.000000 | 5.00000 |
mean | 9.00000 | 11.800000 | 14.600000 | 6.200000 | 6.200000 | 8.200000 | 17.20000 |
std | 7.17635 | 10.825895 | 14.673105 | 4.266146 | 4.266146 | 3.420526 | 10.18332 |
min | 1.00000 | 2.000000 | 3.000000 | 0.000000 | 0.000000 | 4.000000 | 5.00000 |
25% | 5.00000 | 6.000000 | 7.000000 | 4.000000 | 4.000000 | 5.000000 | 10.00000 |
50% | 8.00000 | 9.000000 | 10.000000 | 7.000000 | 7.000000 | 10.000000 | 18.00000 |
75% | 11.00000 | 12.000000 | 13.000000 | 10.000000 | 10.000000 | 11.000000 | 22.00000 |
max | 20.00000 | 30.000000 | 40.000000 | 10.000000 | 10.000000 | 11.000000 | 31.00000 |
Seleccionemos una serie de filas que cumplan con una condición :
panda[panda['A']>5]
A | B | C | D | A+B-C | A+B-C lambda | long D | long D +1 | C even | |
---|---|---|---|---|---|---|---|---|---|
tercero | 8 | 9 | 10 | bienvenido | 7 | 7 | 10 | 18 | True |
cuarto | 11 | 12 | 13 | hasta luego | 10 | 10 | 11 | 22 | False |
quinto | 20 | 30 | 40 | hasta nunca | 10 | 10 | 11 | 31 | True |
Podemos seleccionar una, o varias, columnas en particular en el resultado de la operación anterior :
panda[panda['A']>5]['B']
tercero 9
cuarto 12
quinto 30
Name: B, dtype: int64
Veamos un par de métodos para borrar columnas :
del panda['A']
panda.pop('B')
primero 2
segundo 6
tercero 9
cuarto 12
quinto 30
Name: B, dtype: int64
panda
C | D | A+B-C | A+B-C lambda | long D | long D +1 | C even | |
---|---|---|---|---|---|---|---|
primero | 3 | hola | 0 | 0 | 4 | 5 | False |
segundo | 7 | adios | 4 | 4 | 5 | 10 | False |
tercero | 10 | bienvenido | 7 | 7 | 10 | 18 | True |
cuarto | 13 | hasta luego | 10 | 10 | 11 | 22 | False |
quinto | 40 | hasta nunca | 10 | 10 | 11 | 31 | True |
Podemos ‘devolver’ el índice como una columna más del DataFrame ;
panda.reset_index()
index | C | D | A+B-C | A+B-C lambda | long D | long D +1 | C even | |
---|---|---|---|---|---|---|---|---|
0 | primero | 3 | hola | 0 | 0 | 4 | 5 | False |
1 | segundo | 7 | adios | 4 | 4 | 5 | 10 | False |
2 | tercero | 10 | bienvenido | 7 | 7 | 10 | 18 | True |
3 | cuarto | 13 | hasta luego | 10 | 10 | 11 | 22 | False |
4 | quinto | 40 | hasta nunca | 10 | 10 | 11 | 31 | True |
panda
C | D | A+B-C | A+B-C lambda | long D | long D +1 | C even | |
---|---|---|---|---|---|---|---|
primero | 3 | hola | 0 | 0 | 4 | 5 | False |
segundo | 7 | adios | 4 | 4 | 5 | 10 | False |
tercero | 10 | bienvenido | 7 | 7 | 10 | 18 | True |
cuarto | 13 | hasta luego | 10 | 10 | 11 | 22 | False |
quinto | 40 | hasta nunca | 10 | 10 | 11 | 31 | True |
Qué ha ocurrido?…no se ha llegado a realizar?. En realidad nos ha devuelto una copia del dataframe con el nuevo indice, pero no lo ha cambiado en el dataframe original. Para conseguirlo hay que añadir el parámetro inplace=True
.
panda.reset_index(inplace=True)
panda
index | C | D | A+B-C | A+B-C lambda | long D | long D +1 | C even | |
---|---|---|---|---|---|---|---|---|
0 | primero | 3 | hola | 0 | 0 | 4 | 5 | False |
1 | segundo | 7 | adios | 4 | 4 | 5 | 10 | False |
2 | tercero | 10 | bienvenido | 7 | 7 | 10 | 18 | True |
3 | cuarto | 13 | hasta luego | 10 | 10 | 11 | 22 | False |
4 | quinto | 40 | hasta nunca | 10 | 10 | 11 | 31 | True |
Podemos devolver la columna index
como índice, tal como estaba inicialmente (sin olvidar inplace
) :
panda.set_index('index',inplace=True)
panda
C | D | A+B-C | A+B-C lambda | long D | long D +1 | C even | |
---|---|---|---|---|---|---|---|
index | |||||||
primero | 3 | hola | 0 | 0 | 4 | 5 | False |
segundo | 7 | adios | 4 | 4 | 5 | 10 | False |
tercero | 10 | bienvenido | 7 | 7 | 10 | 18 | True |
cuarto | 13 | hasta luego | 10 | 10 | 11 | 22 | False |
quinto | 40 | hasta nunca | 10 | 10 | 11 | 31 | True |
O podríamos usar otra columna como índice, la C como ejemplo :
panda.set_index('C',inplace=True)
panda
D | A+B-C | A+B-C lambda | long D | long D +1 | C even | |
---|---|---|---|---|---|---|
C | ||||||
3 | hola | 0 | 0 | 4 | 5 | False |
7 | adios | 4 | 4 | 5 | 10 | False |
10 | bienvenido | 7 | 7 | 10 | 18 | True |
13 | hasta luego | 10 | 10 | 11 | 22 | False |
40 | hasta nunca | 10 | 10 | 11 | 31 | True |
En este caso, al no haber realizado previamente un reset_index
hemos perdido el Index original.
Si queremos acceder a la fila ’40’ :
panda.iloc[40]
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
in ()
----> 1 panda.iloc[40]
~/anaconda/envs/OpenData/lib/python3.6/site-packages/pandas/core/indexing.py in __getitem__(self, key)
1326 else:
1327 key = com._apply_if_callable(key, self.obj)
-> 1328 return self._getitem_axis(key, axis=0)
1329
1330 def _is_scalar_access(self, key):
~/anaconda/envs/OpenData/lib/python3.6/site-packages/pandas/core/indexing.py in _getitem_axis(self, key, axis)
1747
1748 # validate the location
-> 1749 self._is_valid_integer(key, axis)
1750
1751 return self._get_loc(key, axis=axis)
~/anaconda/envs/OpenData/lib/python3.6/site-packages/pandas/core/indexing.py in _is_valid_integer(self, key, axis)
1636 l = len(ax)
1637 if key >= l or key 1638 raise IndexError("single positional indexer is out-of-bounds")
1639 return True
1640
IndexError: single positional indexer is out-of-bounds
Qué ha ocurrido?..hemos utilizado el método iloc
, y realmente no hay un 40-ésimo elemento, sin embargo sí tenemos un índice con index=40 :
panda.loc[40]
D hasta nunca
A+B-C 10
A+B-C lambda 10
long D 11
long D +1 31
C even True
Name: 40, dtype: object
Eso es todo en esta introducción de pandas
. En otra entrada veremos como agrupar filas y columnas para trabajar de manera similar a las tablas dinámicas de excel, aunque con muchas más opciones.