2.2. Tratamiento de valores duplicados#
Introducción#
La redundancia de datos se refiere a datos que se repiten en forma exacta, o parcialmente, pero tienen el mismo significado o representan la misma realidad. Además del problema de ocupar espacio innecesario, es muy probale que se generen inconsistencias debido a ella. Esto puede causar errores al realizar la analítica.
Diferencia entre redundancia e inconsistencia#
Vamos a ver un ejemplo de un dataframe que tenga redundacia o datos repetidos para ver las diferencias entre tener redundancias y/o inconsistencias.
Creamos un
dataframecon datos de clientes que compran en un kiosko:
import pandas as pd
# Dataset con datos redundantes
df_redundante = pd.DataFrame({
'id_cliente': [1, 2, 3, 3, 2, 3, 4],
'nombre': ['Susana', 'Oscar', 'Elena', 'Elena', 'Oscar', 'Elena', 'Lucila'],
'apellido': ['Horia','Acol', 'Morado', 'Morado','Acol','Morado', 'Nave'],
'ciudad': ['Neuquén', 'Rosario', 'Córdoba', 'Córdoba', 'Rosario', 'Córdoba', 'Plottier'],
'producto': ['Palitos de la Selva', 'Chupetín Pico Dulce', 'Rocklets', 'Alfajor Jorgito', 'Sugus', 'Tubby 4', 'Block'],
'precio': [100, 200, 150, 300, 120, 80, 230]
})
df_redundante
| id_cliente | nombre | apellido | ciudad | producto | precio | |
|---|---|---|---|---|---|---|
| 0 | 1 | Susana | Horia | Neuquén | Palitos de la Selva | 100 |
| 1 | 2 | Oscar | Acol | Rosario | Chupetín Pico Dulce | 200 |
| 2 | 3 | Elena | Morado | Córdoba | Rocklets | 150 |
| 3 | 3 | Elena | Morado | Córdoba | Alfajor Jorgito | 300 |
| 4 | 2 | Oscar | Acol | Rosario | Sugus | 120 |
| 5 | 3 | Elena | Morado | Córdoba | Tubby 4 | 80 |
| 6 | 4 | Lucila | Nave | Plottier | Block | 230 |
Podemos observar que hay mucha redundancia ya que las filas indexadas 1 y 4 son exactamente las mismas, y las filas 2, 3 y 5 también. Sin embargo esa redundancia hasta el momento no genera inconsistencias, ya que son iguales en todas las filas.
Entonces, cuándo hay incosistencias?
Vamos a agregar una fila al
dataframe df_redundanteque genere una inconsistencia:
# agrego la fila al final con .loc
df_redundante.loc[len(df_redundante)]=[4, 'Lucila', 'Nave', 'Neuquén', 'Flynn Paff', 123]
df_redundante
| id_cliente | nombre | apellido | ciudad | producto | precio | |
|---|---|---|---|---|---|---|
| 0 | 1 | Susana | Horia | Neuquén | Palitos de la Selva | 100 |
| 1 | 2 | Oscar | Acol | Rosario | Chupetín Pico Dulce | 200 |
| 2 | 3 | Elena | Morado | Córdoba | Rocklets | 150 |
| 3 | 3 | Elena | Morado | Córdoba | Alfajor Jorgito | 300 |
| 4 | 2 | Oscar | Acol | Rosario | Sugus | 120 |
| 5 | 3 | Elena | Morado | Córdoba | Tubby 4 | 80 |
| 6 | 4 | Lucila | Nave | Plottier | Block | 230 |
| 7 | 4 | Lucila | Nave | Neuquén | Flynn Paff | 123 |
Ahora si vemos como al agregar Lucila Nave en el índice 7 se genera una inconsistencia debido a la redundancia, Lucila vive en Plottier o en Neuquén?. Al mismo tiempo si usamos los datos asi para la analítica tenemos una misma persona que vive en dos lugares al mismo tiempo y esto puede ser contado 2 veces.
Duplicados en Pandas#
De esta forma, generalmente es necesario eliminar la redundancia, que en Pandas se traduce como eliminar elementos duplicados.
Manejo de Duplicados en Pandas
Pandas posee algunos métodos específicos para analizar duplicados (que también existen los mismos para NumPy). Son:
df.duplicated(): devuelveseriesbooleanas que indican las filas duplicadas. Posee como parámetro:subset = []: en donde se pueden indicar las columnas a identificarkeeppara determinar cuales duplicados marcar:keep = 'first': marca los duplicados comoTrue, excepto la primera ocurrencia. Es el defaultkeep ='last': marca los duplicados comoTrue, excepto la última ocurrenciakeep =False: marca todos los duplicados comoTrue
La información completa esta en pandas.DataFrame.duplicated
df.drop_duplicates(): devuelve undataframecon los valores duplicados eliminados. Tiene los mismos parámetrossubsetykeepy agrega elignore_index(predeterminadoFalse), el cual si esTruelos ejes (axis) se etiquetarán con 0, 1, …, n-1.
La información completa esta en pandas.DataFrame.drop_duplicates
Ejemplos de uso de df.duplicated()#
Utilizando el dataframe df_redundante vamos a probar el uso del método df.duplicated().
Volvemos a ver que tenia el
dataframe:
df_redundante
| id_cliente | nombre | apellido | ciudad | producto | precio | |
|---|---|---|---|---|---|---|
| 0 | 1 | Susana | Horia | Neuquén | Palitos de la Selva | 100 |
| 1 | 2 | Oscar | Acol | Rosario | Chupetín Pico Dulce | 200 |
| 2 | 3 | Elena | Morado | Córdoba | Rocklets | 150 |
| 3 | 3 | Elena | Morado | Córdoba | Alfajor Jorgito | 300 |
| 4 | 2 | Oscar | Acol | Rosario | Sugus | 120 |
| 5 | 3 | Elena | Morado | Córdoba | Tubby 4 | 80 |
| 6 | 4 | Lucila | Nave | Plottier | Block | 230 |
| 7 | 4 | Lucila | Nave | Neuquén | Flynn Paff | 123 |
Ahora aplicamos el método a todo
df_redundante:
# pregunto que filas estan duplicadas, y aquellas que lo estan, devuelva True
df_redundante.duplicated(keep = False)
0 False
1 False
2 False
3 False
4 False
5 False
6 False
7 False
dtype: bool
Como vemos ninguna fila estaba duplicada (devolvió todo False), esto es cierto ya que ninguna fila se repite por completo.
Vamos a probar con algunas columnas nada mas:
# pregunto si las columnas id_cliente y nombre tienen duplicados,
#y aquellas que lo estan, devuelva True
df_redundante.duplicated(['id_cliente', 'nombre'], keep = False)
0 False
1 True
2 True
3 True
4 True
5 True
6 True
7 True
dtype: bool
La única fila que no posee duplicados es la primera, indexada con 0 (Susana Horia).
Ahora podemos probar con el valor keep = 'first' para que marque True a los duplicados excepto la primera vez que aparece.
Lo hacemos para la columna ciudad:
# pregunto si la columna ciudad tiene duplicados,
#y marque True desde la 2da ocurrencia
df_redundante.duplicated(['ciudad'], keep = 'first')
0 False
1 False
2 False
3 True
4 True
5 True
6 False
7 True
dtype: bool
Otro método que podemos utilizar para ver valores duplicados es value_counts().
Método value_counts()
El método value_counts() cuenta la frecuencia de ocurrencias de cada valor en un dataframe o series. Algunos de sus parámetros son:
normalize=Trueque devuelve proporciones en lugar de la cantidadsort=Falsepara no ordenar los resultadosascending=Truepara ordenar de menor a mayordropna=Falsepara que incluya los NaN cuando cuenta
Vemos algunos ejemplos de
value_counts():
# en proporcion, cantidad de iguales para nombre y apellido
df_redundante[['nombre', 'apellido']].value_counts(normalize=True)
nombre apellido
Elena Morado 0.375
Lucila Nave 0.250
Oscar Acol 0.250
Susana Horia 0.125
Name: proportion, dtype: float64
# en cantidad, ordenado
df_redundante[['nombre', 'apellido']].value_counts(ascending=True)
nombre apellido
Susana Horia 1
Lucila Nave 2
Oscar Acol 2
Elena Morado 3
Name: count, dtype: int64
Ejemplos de uso de df.drop_duplicates()#
El método df.drop_duplicates() permite eliminar duplicados devolviendo un nuevo dataframe único.
Probamos el método en
df_redundante:
# aplicamos drop_duplicates para eliminar filas duplicadas
# en todo df_redundante
df_redundante.drop_duplicates()
| id_cliente | nombre | apellido | ciudad | producto | precio | |
|---|---|---|---|---|---|---|
| 0 | 1 | Susana | Horia | Neuquén | Palitos de la Selva | 100 |
| 1 | 2 | Oscar | Acol | Rosario | Chupetín Pico Dulce | 200 |
| 2 | 3 | Elena | Morado | Córdoba | Rocklets | 150 |
| 3 | 3 | Elena | Morado | Córdoba | Alfajor Jorgito | 300 |
| 4 | 2 | Oscar | Acol | Rosario | Sugus | 120 |
| 5 | 3 | Elena | Morado | Córdoba | Tubby 4 | 80 |
| 6 | 4 | Lucila | Nave | Plottier | Block | 230 |
| 7 | 4 | Lucila | Nave | Neuquén | Flynn Paff | 123 |
Como vimos no se modificó nada, porque ninguna fila estaba duplicada en forma completa.
Vamos a eliminar las filas que contengan duplicados en nombre y apellido:
# borrar filas, donde nombre y apellido sean redundantes
df_sin_redundantes = df_redundante.drop_duplicates(['nombre', 'apellido'])
df_sin_redundantes
| id_cliente | nombre | apellido | ciudad | producto | precio | |
|---|---|---|---|---|---|---|
| 0 | 1 | Susana | Horia | Neuquén | Palitos de la Selva | 100 |
| 1 | 2 | Oscar | Acol | Rosario | Chupetín Pico Dulce | 200 |
| 2 | 3 | Elena | Morado | Córdoba | Rocklets | 150 |
| 6 | 4 | Lucila | Nave | Plottier | Block | 230 |
Vamos a crear un nuevo
dataframeque contenga solo el nombre, apellido y ciudad dedf_redundante:
# creamos un nuevo df con solo nombre, apellido y ciudad
df_redundante_chico = df_redundante[['nombre', 'apellido', 'ciudad']]
df_redundante_chico
| nombre | apellido | ciudad | |
|---|---|---|---|
| 0 | Susana | Horia | Neuquén |
| 1 | Oscar | Acol | Rosario |
| 2 | Elena | Morado | Córdoba |
| 3 | Elena | Morado | Córdoba |
| 4 | Oscar | Acol | Rosario |
| 5 | Elena | Morado | Córdoba |
| 6 | Lucila | Nave | Plottier |
| 7 | Lucila | Nave | Neuquén |
y ahora si vamos a aplicar
.drop_duplicates()a todo el `dataframe:
df_redundante_chico.drop_duplicates()
| nombre | apellido | ciudad | |
|---|---|---|---|
| 0 | Susana | Horia | Neuquén |
| 1 | Oscar | Acol | Rosario |
| 2 | Elena | Morado | Córdoba |
| 6 | Lucila | Nave | Plottier |
| 7 | Lucila | Nave | Neuquén |