Una gran parte del tiempo empleado en la tareas de análisis de datos se invierte en la preparación de los mismos. En la mayoría de las ocasiones, nuestras aplicaciones y desarrollos necesitan que los datos tengan un determinado formato. Sin embargo, los datos que se encuentran en ficheros o en bases de datos, no tienen por qué cumplir, a priori, nuestros requisitos, por lo que será necesario proceder a su limpieza, transformación, procesamiento, etc.
import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
%matplotlib inline
La operación pivot
permite construir un nuevo dataframe a partir de otro, de forma que los valores de una columna pasen a ser nombres de columna del nuevo dataframe. Esta operación se usa habitualmente cuando se trabaja con bases de datos relacionales y tiene la misma funcionalidad que en otros lenguajes de análisis de datos. Lo veremos más claro con un ejemplo.
El siguiente dataframe recoge la información de varios pedidos.
datos = np.array([[101, 'p1', 2],
[101, 'p2', 3],
[101, 'p3', 4],
[102, 'p3', 1],
[102, 'p2', 2],
[102, 'p4', 5],
[103, 'p2', 1],
[103, 'p1', 3],
[103, 'p4', 2]])
pedidos = pd.DataFrame(datos,
columns = ['Número_pedido', 'Producto', 'Cantidad'])
pedidos
Nótese que asociado a cada pedido tenemos varios productos, cada uno de ellos con la cantidad pedida.
El método pivot
de la clase Dataframe permite girar los datos y se invoca con al menos tres argumentos. El primer argumento index
tiene como valor el nombre de la columna cuyos valores contituirán las etiquetas del índice del nuevo dataframe. El segundo argumento columns
es el nombre de la columna cuyos valores constituirán las etiquetas del índice de las columnas del nuevo dataframe. Por último, el tercer argumento values
es el nombre de la columna cuyos valores pasan a ser los valores del nuevo Dataframe.
pedidos.pivot(index = 'Número_pedido', columns = 'Producto', values = 'Cantidad')
El método pivot
exige que no existan dos filas con los mismos valores para las columnas asociadas a los dos primeros argumentos (index
y columns
). En este ejemplo, las columnas Número_pedido
y Producto
forman lo que en bases de datos relacionales llamamos clave primaria, es decir, no existen dos filas con el mismo número de pedido y el mismo producto.
Si no se cumple la condicion, podemos usar el método pivot_table
de la clase DataFrame
. Su funcionamiento es similar a pivot
con la ventaja de que permite aplicar funciones de agregación.
datos = [[2001, 1, 'p1', 100],
[2001, 2, 'p1', 50],
[2001, 3, 'p2', 20],
[2004, 1, 'p3', 100],
[2004, 1, 'p1', 100],
[2004, 2, 'p1', 200],
[2007, 2, 'p1', 50],
[2007, 3, 'p1', 40]]
prod = pd.DataFrame(datos, columns = ['Ejercicio', 'Trimestre', 'Producto', 'Kg.'])
prod
prod.pivot_table(index = 'Ejercicio', columns = 'Producto', values = 'Kg.', aggfunc = sum)
En el ejemplo anterior, se aplica la función suma (sum
), para sumar la cantidad de producto producida en cada ejercicio.
El argumento fill_value
permite reemplazar los valores NaN
por un valor por defecto.
prod.pivot_table(index = 'Ejercicio', columns = 'Producto', values = 'Kg.',
aggfunc = sum, fill_value = 0)
La detección de datos duplicados en dataframes supone un gran esfuerzo cuando se habla de grandes cantidades de datos.
El método duplicated
de la clase DataFrame permite identificar filas repetidas. Como resultado se obtiene una serie cuyos valores son de tipo bool
, cada uno de los cuales está asociado a una fila. El valor True
indica que ya existe una fila anterior en el dataframe con los mismos valores (la primera aparición no se considera repetida). En caso contrario, el valor será False
.
Consideremos el siguiente dataframe con filas repetidas:
t = pd.DataFrame([['Africa', 11506],
['Africa', 11506],
['Antarctica', 5500],
['Antarctica', 5500],
['Africa', 11506],
['Banks', 23],
['Africa', 10000]], columns = ['Nombre', 'Millas cuadradas'])
t
t.duplicated()
El resultado del método duplicated
se puede utilizar como filtro para obtener un dataframe con las filas repetidas.
t[t.duplicated()]
Para eliminar las filas repetidas de un dataframe utilizamos el método drop_duplicates
de la clase DataFrame.
t.drop_duplicates()
También nos puede interesar eliminar filas si se repiten los valores de un subconjunto de columnas. En ese caso es necesario indicar dicho subconjunto de columnas en el argumento subset
.
t.drop_duplicates(subset = ['Nombre'])
Por defecto se mantiene la primera fila y se elimina el resto de filas repetidas. El argumento keep
con valor last
permite eliminar todas las filas repetidas excepto la última aparición.
t.drop_duplicates(subset = ['Nombre'], keep = 'last')
Al estar Pandas construída sobre la librería Numpy, las funciones universales definidas en Numpy se extienden para adaptarse a las nuevas estructuras de datos presentes en Pandas. Se trata de funciones que se aplican a cada uno de los valores de la estructura, ya sea un objeto de tipo ndarray
, Series
o DataFrame
.
datos = [ ( 8, 7, 6),
(2, 4, 16),
(10,20,30),
(4,2,5),
]
t = pd.DataFrame( datos,
columns = [ '2004', '2005', '2006'],
index = [ 'Estonia', 'Ireland', 'Greece', 'Spain'])
t
Por ejemplo, para aplicar la raiz cuadrada a cada valor de un dataframe, utilizamos la función np.sqrt
de Numpy.
np.sqrt(t)
Para calcular el cuadrado de cada uno de los vlores de un dataframe, utilizamos la función np.square
de Numpy.
np.square(t)
La aplicación de funciones a cada uno de los valores de una serie o un dataframe no se reduce a las funciones universales definidas en Numpy, si no que también es posible aplicar funciones definidas por el usuario.
En el caso de objetos de tipo Series
es necesario utilizar el método map
para aplicar funciones de usuario.
f1 = lambda x: x ** 2 - 2
s = pd.Series([2, 4, 6, 8 ], index = ['EE', 'IR', 'GR', 'ES'])
s
s.map(f1)
En el caso de objetos de tipo DataFrame
usaremos el método applymap
.
t.applymap(f1)
La siguiente función de usuario multiplica por 10 aquellos valores menos que 100.
f2 = lambda x: x * 10 if x < 100 else x
Se puede utilizar la aplicación de funciones para crear nuevas columnas en un dataframe en base a los datos de otra columna.
t['Nueva'] = t['2004'].map(f2)
t
El método apply
de la clase DataFrame
permite aplicar funciones de usuario tanto a filas como a columnas de un dataframe.
f3 = lambda x: x.sum() - x.min()
t = pd.DataFrame( [[10, 20, 30],
[40, 10, 60],
[70, 80, 10]],
index = ['P1', 'P2', 'P3'],
columns = ['A', 'B', 'C'])
t
t.apply(f3)
El resultado es una serie, donde las etiquetas del índice son las etiquetas del índice de las columnas del dataframe. Cada uno de los valores de la serie es el resultado de aplicar la función de usuario a cada columna. El argumento axis
permite aplicar la función a cada una de las filas.
t.apply(f3, axis = 1)
La función de usuario puede devolver una serie en lugar de un valor escalar. Esto permitirá la aplicación de varias funciones simultáneamente.
def f4(x):
return pd.Series([sum(x) , min(x), f3(x) ], index = ['Suma', 'Mínimo', 'f3'])
t.apply(f4, axis = 1)
Como podemos observar en el ejemplo anterior, el resultado es un dataframe con tantas columnas como valores haya en la serie devuelta por la función de usuario.