Pandas


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 listse 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 indexcomo í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.

Deja una respuesta

Por favor, inicia sesión con uno de estos métodos para publicar tu comentario:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Salir /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Salir /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Salir /  Cambiar )

Conectando a %s

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.