🌐 Update translations via Co-op Translator

pull/688/head
leestott 2 months ago committed by GitHub
parent 57c2be2a87
commit 462a17f4b4

@ -8,13 +8,13 @@
"source": [
"# Preparación de Datos\n",
"\n",
"[Fuente original del Notebook de *Data Science: Introduction to Machine Learning for Data Science Python and Machine Learning Studio por Lee Stott*](https://github.com/leestott/intro-Datascience/blob/master/Course%20Materials/4-Cleaning_and_Manipulating-Reference.ipynb)\n",
"[Fuente original del cuaderno de *Data Science: Introduction to Machine Learning for Data Science Python and Machine Learning Studio por Lee Stott*](https://github.com/leestott/intro-Datascience/blob/master/Course%20Materials/4-Cleaning_and_Manipulating-Reference.ipynb)\n",
"\n",
"## Explorando información de `DataFrame`\n",
"\n",
"> **Objetivo de aprendizaje:** Al final de esta subsección, deberías sentirte cómodo encontrando información general sobre los datos almacenados en pandas DataFrames.\n",
"> **Objetivo de aprendizaje:** Al final de esta subsección, deberías sentirte cómodo encontrando información general sobre los datos almacenados en DataFrames de pandas.\n",
"\n",
"Una vez que hayas cargado tus datos en pandas, lo más probable es que estén en un `DataFrame`. Sin embargo, si el conjunto de datos en tu `DataFrame` tiene 60,000 filas y 400 columnas, ¿cómo puedes empezar a entender con qué estás trabajando? Afortunadamente, pandas proporciona herramientas convenientes para observar rápidamente información general sobre un `DataFrame`, además de las primeras y últimas filas.\n",
"Una vez que hayas cargado tus datos en pandas, lo más probable es que estén en un `DataFrame`. Sin embargo, si el conjunto de datos en tu `DataFrame` tiene 60,000 filas y 400 columnas, ¿cómo puedes siquiera empezar a entender con qué estás trabajando? Afortunadamente, pandas proporciona herramientas convenientes para observar rápidamente información general sobre un `DataFrame`, además de las primeras y últimas filas.\n",
"\n",
"Para explorar esta funcionalidad, importaremos la biblioteca Python scikit-learn y utilizaremos un conjunto de datos icónico que todo científico de datos ha visto cientos de veces: el conjunto de datos *Iris* del biólogo británico Ronald Fisher, utilizado en su artículo de 1936 \"El uso de mediciones múltiples en problemas taxonómicos\":\n"
]
@ -78,9 +78,9 @@
"id": "smE7AGzOhxk2"
},
"source": [
"Entonces, estamos trabajando con 150 filas y 4 columnas de datos. Cada fila representa un punto de datos y cada columna representa una característica asociada al marco de datos. Básicamente, hay 150 puntos de datos que contienen 4 características cada uno.\n",
"Entonces, estamos trabajando con 150 filas y 4 columnas de datos. Cada fila representa un punto de datos y cada columna representa una característica única asociada con el marco de datos. Básicamente, hay 150 puntos de datos que contienen 4 características cada uno.\n",
"\n",
"`shape` aquí es un atributo del marco de datos y no una función, por lo que no termina con un par de paréntesis.\n"
"`shape` aquí es un atributo del marco de datos y no una función, por eso no termina con un par de paréntesis.\n"
]
},
{
@ -90,7 +90,7 @@
},
"source": [
"### `DataFrame.columns`\n",
"Pasemos ahora a las 4 columnas de datos. ¿Qué representa exactamente cada una de ellas? El atributo `columns` nos dará el nombre de las columnas en el dataframe.\n"
"Ahora pasemos a las 4 columnas de datos. ¿Qué representa exactamente cada una de ellas? El atributo `columns` nos dará el nombre de las columnas en el dataframe.\n"
]
},
{
@ -181,8 +181,8 @@
},
"source": [
"A partir de aquí, podemos hacer algunas observaciones: \n",
"1. El tipo de dato de cada columna: En este conjunto de datos, toda la información está almacenada como números de punto flotante de 64 bits. \n",
"2. Número de valores no nulos: Manejar valores nulos es un paso importante en la preparación de datos. Esto se abordará más adelante en el cuaderno. \n"
"1. El tipo de dato de cada columna: En este conjunto de datos, todos los datos están almacenados como números de punto flotante de 64 bits. \n",
"2. Número de valores no nulos: Manejar los valores nulos es un paso importante en la preparación de datos. Esto se abordará más adelante en el cuaderno. \n"
]
},
{
@ -192,7 +192,7 @@
},
"source": [
"### DataFrame.describe()\n",
"Supongamos que tenemos muchos datos numéricos en nuestro conjunto de datos. Los cálculos estadísticos univariados, como la media, la mediana, los cuartiles, etc., se pueden realizar en cada una de las columnas de forma individual. La función `DataFrame.describe()` nos proporciona un resumen estadístico de las columnas numéricas de un conjunto de datos.\n"
"Supongamos que tenemos muchos datos numéricos en nuestro conjunto de datos. Los cálculos estadísticos univariados, como la media, la mediana, los cuartiles, etc., se pueden realizar en cada una de las columnas individualmente. La función `DataFrame.describe()` nos proporciona un resumen estadístico de las columnas numéricas de un conjunto de datos.\n"
]
},
{
@ -452,7 +452,7 @@
"source": [
"### Ejercicio:\n",
"\n",
"A partir del ejemplo dado anteriormente, queda claro que, por defecto, `DataFrame.head` devuelve las primeras cinco filas de un `DataFrame`. En la celda de código a continuación, ¿puedes encontrar una forma de mostrar más de cinco filas?\n"
"Del ejemplo dado anteriormente, queda claro que, por defecto, `DataFrame.head` devuelve las primeras cinco filas de un `DataFrame`. En la celda de código a continuación, ¿puedes encontrar una manera de mostrar más de cinco filas?\n"
]
},
{
@ -584,7 +584,7 @@
"source": [
"En la práctica, es útil poder examinar fácilmente las primeras filas o las últimas filas de un `DataFrame`, especialmente cuando estás buscando valores atípicos en conjuntos de datos ordenados.\n",
"\n",
"Todas las funciones y atributos mostrados anteriormente con la ayuda de ejemplos de código nos ayudan a obtener una visión general y una sensación del conjunto de datos.\n",
"Todas las funciones y atributos mostrados anteriormente con la ayuda de ejemplos de código nos ayudan a obtener una visión general y una idea del contenido de los datos.\n",
"\n",
"> **Conclusión:** Incluso solo mirando los metadatos sobre la información en un DataFrame o los primeros y últimos valores en uno, puedes obtener una idea inmediata sobre el tamaño, la forma y el contenido de los datos con los que estás trabajando.\n"
]
@ -595,18 +595,18 @@
"id": "TvurZyLSDxq_"
},
"source": [
"### Datos Faltantes\n",
"Vamos a profundizar en los datos faltantes. Los datos faltantes ocurren cuando no se almacena un valor en algunas de las columnas.\n",
"### Datos faltantes\n",
"Vamos a profundizar en los datos faltantes. Los datos faltantes ocurren cuando no se almacena ningún valor en algunas de las columnas.\n",
"\n",
"Tomemos un ejemplo: supongamos que alguien está consciente de su peso y no completa el campo de peso en una encuesta. Entonces, el valor del peso para esa persona estará ausente.\n",
"Tomemos un ejemplo: supongamos que alguien está preocupado por su peso y no completa el campo de peso en una encuesta. Entonces, el valor de peso para esa persona en particular estará ausente.\n",
"\n",
"La mayoría de las veces, en conjuntos de datos del mundo real, ocurren valores faltantes.\n",
"\n",
"**Cómo Pandas maneja los datos faltantes**\n",
"\n",
"Pandas maneja los valores faltantes de dos maneras. La primera, que ya has visto en secciones anteriores, es `NaN`, o Not a Number (No es un número). Este es en realidad un valor especial que forma parte de la especificación de punto flotante IEEE y se utiliza únicamente para indicar valores de punto flotante faltantes.\n",
"Pandas maneja los valores faltantes de dos maneras. La primera ya la has visto en secciones anteriores: `NaN`, o Not a Number (No es un número). Este es, de hecho, un valor especial que forma parte de la especificación de punto flotante IEEE y se utiliza únicamente para indicar valores faltantes de tipo flotante.\n",
"\n",
"Para los valores faltantes que no son de tipo flotante, pandas utiliza el objeto `None` de Python. Aunque pueda parecer confuso encontrarse con dos tipos diferentes de valores que esencialmente indican lo mismo, hay razones programáticas sólidas para esta elección de diseño y, en la práctica, seguir este enfoque permite a pandas ofrecer un buen compromiso en la gran mayoría de los casos. No obstante, tanto `None` como `NaN` tienen restricciones que debes tener en cuenta en relación con cómo pueden ser utilizados.\n"
"Para valores faltantes que no son de tipo flotante, pandas utiliza el objeto `None` de Python. Aunque pueda parecer confuso encontrarse con dos tipos diferentes de valores que esencialmente indican lo mismo, hay razones programáticas sólidas para esta elección de diseño y, en la práctica, seguir este enfoque permite a pandas ofrecer un buen equilibrio para la gran mayoría de los casos. No obstante, tanto `None` como `NaN` tienen restricciones que debes tener en cuenta con respecto a cómo pueden ser utilizados.\n"
]
},
{
@ -616,7 +616,7 @@
},
"source": [
"### `None`: datos faltantes no flotantes\n",
"Debido a que `None` proviene de Python, no puede ser utilizado en arrays de NumPy y pandas que no tengan el tipo de dato `'object'`. Recuerda, los arrays de NumPy (y las estructuras de datos en pandas) solo pueden contener un tipo de dato. Esto es lo que les da su enorme poder para trabajar con datos y cálculos a gran escala, pero también limita su flexibilidad. Dichos arrays tienen que convertir los datos al \"denominador común más bajo\", es decir, al tipo de dato que abarque todo en el array. Cuando `None` está en el array, significa que estás trabajando con objetos de Python.\n",
"Dado que `None` proviene de Python, no puede ser utilizado en arrays de NumPy y pandas que no tengan el tipo de dato `'object'`. Recuerda que los arrays de NumPy (y las estructuras de datos en pandas) solo pueden contener un tipo de dato. Esto es lo que les da su enorme potencia para trabajar con datos y cálculos a gran escala, pero también limita su flexibilidad. Dichos arrays tienen que convertir su tipo al \"mínimo común denominador\", es decir, al tipo de dato que abarque todo en el array. Cuando `None` está en el array, significa que estás trabajando con objetos de Python.\n",
"\n",
"Para ver esto en acción, considera el siguiente ejemplo de array (nota el `dtype` que tiene):\n"
]
@ -657,7 +657,7 @@
"id": "pdlgPNbhgRr7"
},
"source": [
"La realidad de los tipos de datos promovidos conlleva dos efectos secundarios. Primero, las operaciones se realizarán a nivel de código interpretado de Python en lugar de código compilado de NumPy. Básicamente, esto significa que cualquier operación que involucre `Series` o `DataFrames` con `None` será más lenta. Aunque probablemente no notarías esta disminución de rendimiento, en conjuntos de datos grandes podría convertirse en un problema.\n",
"La realidad de los tipos de datos promovidos conlleva dos efectos secundarios. Primero, las operaciones se realizarán a nivel de código interpretado de Python en lugar de código compilado de NumPy. Básicamente, esto significa que cualquier operación que involucre `Series` o `DataFrames` con `None` será más lenta. Aunque probablemente no notarías este impacto en el rendimiento, en conjuntos de datos grandes podría convertirse en un problema.\n",
"\n",
"El segundo efecto secundario deriva del primero. Debido a que `None` esencialmente arrastra a las `Series` o `DataFrames` de vuelta al mundo de Python estándar, usar agregaciones de NumPy/pandas como `sum()` o `min()` en arreglos que contienen un valor ``None`` generalmente producirá un error:\n"
]
@ -697,7 +697,9 @@
"metadata": {
"id": "LcEwO8UogRr9"
},
"source": []
"source": [
"**Conclusión clave**: La suma (y otras operaciones) entre enteros y valores `None` no está definida, lo que puede limitar lo que puedes hacer con conjuntos de datos que los contienen.\n"
]
},
{
"cell_type": "markdown",
@ -828,7 +830,9 @@
"metadata": {
"id": "_iDvIRC8gRsA"
},
"source": []
"source": [
"Recuerda: `NaN` es solo para valores de punto flotante faltantes; no hay un equivalente de `NaN` para enteros, cadenas o valores booleanos.\n"
]
},
{
"cell_type": "markdown",
@ -902,14 +906,14 @@
"id": "WjMQwltNgRsB"
},
"source": [
"En el proceso de convertir tipos de datos para establecer homogeneidad en los `Series` y `DataFrame`s, pandas cambiará sin problemas los valores faltantes entre `None` y `NaN`. Debido a esta característica de diseño, puede ser útil pensar en `None` y `NaN` como dos variantes diferentes de \"nulo\" en pandas. De hecho, algunos de los métodos principales que usarás para manejar valores faltantes en pandas reflejan esta idea en sus nombres:\n",
"En el proceso de convertir tipos de datos para establecer homogeneidad en los datos de `Series` y `DataFrame`s, pandas cambiará sin problema los valores faltantes entre `None` y `NaN`. Debido a esta característica de diseño, puede ser útil pensar en `None` y `NaN` como dos variantes diferentes de \"nulo\" en pandas. De hecho, algunos de los métodos principales que usarás para manejar valores faltantes en pandas reflejan esta idea en sus nombres:\n",
"\n",
"- `isnull()`: Genera una máscara booleana que indica valores faltantes\n",
"- `notnull()`: Opuesto a `isnull()`\n",
"- `dropna()`: Devuelve una versión filtrada de los datos\n",
"- `fillna()`: Devuelve una copia de los datos con los valores faltantes rellenados o imputados\n",
"- `isnull()`: Genera una máscara booleana que indica valores faltantes.\n",
"- `notnull()`: Opuesto a `isnull()`.\n",
"- `dropna()`: Devuelve una versión filtrada de los datos.\n",
"- `fillna()`: Devuelve una copia de los datos con los valores faltantes rellenados o imputados.\n",
"\n",
"Estos son métodos importantes que debes dominar y con los que debes familiarizarte, así que repasémoslos en detalle.\n"
"Estos son métodos importantes que debes dominar y con los que debes sentirte cómodo, así que vamos a repasarlos en detalle.\n"
]
},
{
@ -920,8 +924,7 @@
"source": [
"### Detectar valores nulos\n",
"\n",
"Ahora que hemos entendido la importancia de los valores faltantes, necesitamos detectarlos en nuestro conjunto de datos antes de manejarlos. \n",
"Tanto `isnull()` como `notnull()` son tus métodos principales para detectar datos nulos. Ambos devuelven máscaras booleanas sobre tus datos.\n"
"Ahora que hemos entendido la importancia de los valores faltantes, necesitamos detectarlos en nuestro conjunto de datos antes de manejarlos. Tanto `isnull()` como `notnull()` son tus métodos principales para detectar datos nulos. Ambos devuelven máscaras booleanas sobre tus datos.\n"
]
},
{
@ -974,7 +977,7 @@
"id": "PaSZ0SQygRsC"
},
"source": [
"Mira detenidamente el contenido. ¿Hay algo que te sorprenda? Aunque `0` es un nulo aritmético, sigue siendo un número entero perfectamente válido, y pandas lo trata como tal. `''` es un poco más sutil. Aunque lo usamos en la Sección 1 para representar un valor de cadena vacío, sigue siendo un objeto de cadena y no una representación de nulo según pandas.\n",
"Observa detenidamente el resultado. ¿Hay algo que te sorprenda? Aunque `0` es un valor nulo en términos aritméticos, sigue siendo un número entero válido y pandas lo trata como tal. `''` es un caso un poco más sutil. Aunque lo usamos en la Sección 1 para representar un valor de cadena vacío, sigue siendo un objeto de tipo cadena y no una representación de un valor nulo según pandas.\n",
"\n",
"Ahora, vamos a darle la vuelta y usar estos métodos de una manera más parecida a cómo los usarás en la práctica. Puedes usar máscaras booleanas directamente como un índice de ``Series`` o ``DataFrame``, lo cual puede ser útil cuando intentas trabajar con valores faltantes (o presentes) de forma aislada.\n",
"\n",
@ -1071,7 +1074,7 @@
"\n",
"La cantidad de datos que pasamos a nuestro modelo tiene un efecto directo en su rendimiento. Eliminar valores nulos significa que estamos reduciendo el número de puntos de datos y, por lo tanto, el tamaño del conjunto de datos. Por eso, es recomendable eliminar filas con valores nulos cuando el conjunto de datos es bastante grande.\n",
"\n",
"Otro caso podría ser que una fila o columna específica tenga muchos valores faltantes. En ese caso, podrían eliminarse porque no aportarían mucho valor a nuestro análisis, ya que la mayor parte de los datos están ausentes para esa fila/columna.\n",
"Otro caso podría ser que una fila o columna específica tenga muchos valores faltantes. En ese caso, podrían eliminarse porque no aportarían mucho valor a nuestro análisis, ya que la mayoría de los datos están ausentes para esa fila/columna.\n",
"\n",
"Más allá de identificar valores faltantes, pandas ofrece una forma conveniente de eliminar valores nulos de `Series` y `DataFrame`s. Para ver esto en acción, volvamos a `example3`. La función `DataFrame.dropna()` ayuda a eliminar las filas con valores nulos.\n"
]
@ -1112,9 +1115,9 @@
"id": "hil2cr64gRsD"
},
"source": [
"Ten en cuenta que esto debería verse como tu salida de `example3[example3.notnull()]`. La diferencia aquí es que, en lugar de simplemente indexar los valores enmascarados, `dropna` ha eliminado esos valores faltantes del `Series` `example3`.\n",
"Ten en cuenta que esto debería parecerse a tu salida de `example3[example3.notnull()]`. La diferencia aquí es que, en lugar de simplemente indexar los valores enmascarados, `dropna` ha eliminado esos valores faltantes del `Series` `example3`.\n",
"\n",
"Debido a que los DataFrames tienen dos dimensiones, ofrecen más opciones para eliminar datos.\n"
"Dado que los DataFrames tienen dos dimensiones, ofrecen más opciones para eliminar datos.\n"
]
},
{
@ -1206,7 +1209,7 @@
"source": [
"(¿Notaste que pandas convirtió dos de las columnas a flotantes para acomodar los `NaN`?)\n",
"\n",
"No puedes eliminar un solo valor de un `DataFrame`, por lo que tienes que eliminar filas o columnas completas. Dependiendo de lo que estés haciendo, podrías querer hacer una cosa u otra, y por eso pandas te da opciones para ambas. Dado que en ciencia de datos las columnas generalmente representan variables y las filas representan observaciones, es más probable que elimines filas de datos; la configuración predeterminada de `dropna()` es eliminar todas las filas que contengan cualquier valor nulo:\n"
"No puedes eliminar un único valor de un `DataFrame`, por lo que tienes que eliminar filas o columnas completas. Dependiendo de lo que estés haciendo, podrías querer hacer una u otra cosa, y por eso pandas te da opciones para ambas. Debido a que en ciencia de datos las columnas generalmente representan variables y las filas representan observaciones, es más probable que elimines filas de datos; la configuración predeterminada de `dropna()` es eliminar todas las filas que contienen cualquier valor nulo:\n"
]
},
{
@ -1358,9 +1361,9 @@
"id": "KWXiKTfMgRsF"
},
"source": [
"Ten en cuenta que esto puede eliminar una gran cantidad de datos que podrías querer conservar, especialmente en conjuntos de datos más pequeños. ¿Qué pasa si solo quieres eliminar filas o columnas que contienen varios o incluso todos los valores nulos? Puedes especificar esas configuraciones en `dropna` con los parámetros `how` y `thresh`.\n",
"Ten en cuenta que esto puede eliminar muchos datos que podrías querer conservar, especialmente en conjuntos de datos más pequeños. ¿Qué pasa si solo quieres eliminar filas o columnas que contienen varios o incluso todos los valores nulos? Puedes especificar esas configuraciones en `dropna` utilizando los parámetros `how` y `thresh`.\n",
"\n",
"Por defecto, `how='any'` (si deseas comprobarlo por ti mismo o ver qué otros parámetros tiene el método, ejecuta `example4.dropna?` en una celda de código). Alternativamente, podrías especificar `how='all'` para eliminar únicamente las filas o columnas que contienen todos los valores nulos. Ampliemos nuestro ejemplo de `DataFrame` para ver esto en acción en el próximo ejercicio.\n"
"Por defecto, `how='any'` (si deseas verificarlo por ti mismo o ver qué otros parámetros tiene el método, ejecuta `example4.dropna?` en una celda de código). Alternativamente, podrías especificar `how='all'` para eliminar únicamente las filas o columnas que contienen todos los valores nulos. Vamos a ampliar nuestro ejemplo de `DataFrame` para ver esto en acción en el próximo ejercicio.\n"
]
},
{
@ -1454,9 +1457,9 @@
"source": [
"> Puntos clave: \n",
"1. Eliminar valores nulos es una buena idea solo si el conjunto de datos es lo suficientemente grande. \n",
"2. Se pueden eliminar filas o columnas completas si la mayor parte de sus datos están ausentes. \n",
"2. Se pueden eliminar filas o columnas completas si la mayoría de sus datos están ausentes. \n",
"3. El método `DataFrame.dropna(axis=)` ayuda a eliminar valores nulos. El argumento `axis` indica si se deben eliminar filas o columnas. \n",
"4. También se puede usar el argumento `how`. Por defecto, está configurado como `any`. Por lo tanto, elimina solo aquellas filas/columnas que contienen algún valor nulo. Se puede configurar como `all` para especificar que eliminaremos solo aquellas filas/columnas donde todos los valores sean nulos. \n"
"4. También se puede usar el argumento `how`. Por defecto está configurado como `any`. Por lo tanto, elimina solo aquellas filas/columnas que contienen algún valor nulo. Se puede configurar como `all` para especificar que eliminaremos solo aquellas filas/columnas donde todos los valores sean nulos. \n"
]
},
{
@ -1488,7 +1491,7 @@
"id": "38kwAihWgRsG"
},
"source": [
"El parámetro `thresh` te da un control más detallado: estableces el número de valores *no nulos* que una fila o columna necesita tener para ser conservada:\n"
"El parámetro `thresh` te ofrece un control más detallado: defines la cantidad de valores *no nulos* que una fila o columna necesita tener para ser conservada:\n"
]
},
{
@ -1563,7 +1566,7 @@
"id": "fmSFnzZegRsG"
},
"source": [
"Aquí, la primera y última fila se han eliminado, porque contienen solo dos valores no nulos.\n"
"Aquí, se han eliminado la primera y la última fila, porque contienen solo dos valores no nulos.\n"
]
},
{
@ -1574,9 +1577,9 @@
"source": [
"### Rellenar valores nulos\n",
"\n",
"A veces tiene sentido completar los valores faltantes con otros que podrían ser válidos. Existen varias técnicas para rellenar valores nulos. La primera es usar Conocimiento del Dominio (conocimiento del tema en el que se basa el conjunto de datos) para aproximar de alguna manera los valores faltantes.\n",
"A veces tiene sentido completar los valores faltantes con aquellos que podrían ser válidos. Hay algunas técnicas para rellenar valores nulos. La primera es usar Conocimiento del Dominio (conocimiento del tema en el que se basa el conjunto de datos) para aproximar de alguna manera los valores faltantes.\n",
"\n",
"Podrías usar `isnull` para hacer esto directamente, pero puede ser tedioso, especialmente si tienes muchos valores que rellenar. Dado que esta es una tarea tan común en ciencia de datos, pandas proporciona `fillna`, que devuelve una copia del `Series` o `DataFrame` con los valores faltantes reemplazados por uno de tu elección. Vamos a crear otro ejemplo de `Series` para ver cómo funciona esto en la práctica.\n"
"Puedes usar `isnull` para hacer esto directamente, pero puede ser laborioso, especialmente si tienes muchos valores que rellenar. Debido a que esta es una tarea tan común en ciencia de datos, pandas proporciona `fillna`, que devuelve una copia del `Series` o `DataFrame` con los valores faltantes reemplazados por uno de tu elección. Vamos a crear otro ejemplo de `Series` para ver cómo funciona esto en la práctica.\n"
]
},
{
@ -1585,12 +1588,12 @@
"id": "CE8S7louLezV"
},
"source": [
"### Datos Categóricos (No numéricos)\n",
"### Datos categóricos (no numéricos)\n",
"Primero, consideremos los datos no numéricos. En los conjuntos de datos, tenemos columnas con datos categóricos. Por ejemplo, Género, Verdadero o Falso, etc.\n",
"\n",
"En la mayoría de estos casos, reemplazamos los valores faltantes con la `moda` de la columna. Supongamos que tenemos 100 puntos de datos, 90 han respondido Verdadero, 8 han respondido Falso y 2 no han respondido. Entonces, podemos completar los 2 con Verdadero, considerando toda la columna.\n",
"En la mayoría de estos casos, reemplazamos los valores faltantes con la `moda` de la columna. Supongamos que tenemos 100 puntos de datos, de los cuales 90 han indicado Verdadero, 8 han indicado Falso y 2 no han respondido. Entonces, podemos completar los 2 con Verdadero, considerando toda la columna.\n",
"\n",
"Nuevamente, aquí podemos usar conocimiento del dominio. Consideremos un ejemplo de cómo completar con la moda.\n"
"Nuevamente, aquí podemos usar conocimiento del dominio. Veamos un ejemplo de cómo completar con la moda.\n"
]
},
{
@ -1695,7 +1698,9 @@
"metadata": {
"id": "MLAoMQOfNPlA"
},
"source": []
"source": [
"Ahora, primero encontremos la moda antes de llenar el valor `None` con la moda.\n"
]
},
{
"cell_type": "code",
@ -1730,7 +1735,9 @@
"metadata": {
"id": "6iNz_zG_OKrx"
},
"source": []
"source": [
"Entonces, reemplazaremos None con True\n"
]
},
{
"cell_type": "code",
@ -1850,14 +1857,14 @@
},
"source": [
"### Datos Numéricos\n",
"Ahora, pasando a los datos numéricos. Aquí, tenemos dos formas comunes de reemplazar valores faltantes:\n",
"Ahora, pasando a los datos numéricos. Aquí tenemos dos formas comunes de reemplazar valores faltantes:\n",
"\n",
"1. Reemplazar con la mediana de la fila \n",
"2. Reemplazar con el promedio de la fila \n",
"\n",
"Reemplazamos con la mediana en caso de datos sesgados con valores atípicos. Esto se debe a que la mediana es robusta frente a los valores atípicos.\n",
"Reemplazamos con la mediana en caso de datos sesgados con valores atípicos. Esto se debe a que la mediana es resistente a los valores atípicos.\n",
"\n",
"Cuando los datos están normalizados, podemos usar el promedio, ya que en ese caso, el promedio y la mediana estarían bastante cerca.\n",
"Cuando los datos están normalizados, podemos usar el promedio, ya que en ese caso, el promedio y la mediana serían bastante cercanos.\n",
"\n",
"Primero, tomemos una columna que esté distribuida normalmente y rellenemos el valor faltante con el promedio de la columna.\n"
]
@ -1999,7 +2006,9 @@
"metadata": {
"id": "oBSRGxKRS39K"
},
"source": []
"source": [
"Rellenar con la media\n"
]
},
{
"cell_type": "code",
@ -2108,7 +2117,7 @@
"id": "jIvF13a1i00Z"
},
"source": [
"Ahora intentemos con otro dataframe, y esta vez reemplazaremos los valores None con la mediana de la columna.\n"
"Ahora intentemos otro dataframe, y esta vez reemplazaremos los valores None con la mediana de la columna.\n"
]
},
{
@ -2248,7 +2257,9 @@
"metadata": {
"id": "z9PLF75Jj_1s"
},
"source": []
"source": [
"Rellenar con la mediana\n"
]
},
{
"cell_type": "code",
@ -2432,10 +2443,10 @@
},
"source": [
"> Puntos clave:\n",
"1. Completar los valores faltantes debe hacerse cuando hay poca información o cuando existe una estrategia para llenar los datos faltantes.\n",
"2. El conocimiento del dominio puede utilizarse para completar los valores faltantes aproximándolos.\n",
"3. Para datos categóricos, generalmente, los valores faltantes se sustituyen con la moda de la columna.\n",
"4. Para datos numéricos, los valores faltantes suelen completarse con la media (para conjuntos de datos normalizados) o la mediana de las columnas.\n"
"1. Completar los valores faltantes debe hacerse cuando hay poca información o existe una estrategia para llenar los datos faltantes.\n",
"2. El conocimiento del dominio puede utilizarse para aproximar y completar los valores faltantes.\n",
"3. En el caso de datos categóricos, generalmente los valores faltantes se sustituyen por la moda de la columna.\n",
"4. Para datos numéricos, los valores faltantes suelen completarse con la media (en conjuntos de datos normalizados) o la mediana de las columnas.\n"
]
},
{
@ -2465,7 +2476,9 @@
"metadata": {
"id": "kq3hw1kLgRsI"
},
"source": []
"source": [
"Puedes **rellenar hacia adelante** los valores nulos, lo que significa usar el último valor válido para llenar un nulo:\n"
]
},
{
"cell_type": "code",
@ -2720,7 +2733,7 @@
"id": "ZeMc-I1EgRsI"
},
"source": [
"Ten en cuenta que cuando no se dispone de un valor previo para completar hacia adelante, el valor nulo permanece.\n"
"Nota que cuando no se dispone de un valor previo para completar hacia adelante, el valor nulo permanece.\n"
]
},
{
@ -2753,7 +2766,7 @@
"id": "YHgy0lIrgRsJ"
},
"source": [
"Puedes ser creativo con cómo usas `fillna`. Por ejemplo, veamos nuevamente `example4`, pero esta vez llenemos los valores faltantes con el promedio de todos los valores en el `DataFrame`:\n"
"Puedes ser creativo con el uso de `fillna`. Por ejemplo, veamos nuevamente `example4`, pero esta vez llenemos los valores faltantes con el promedio de todos los valores en el `DataFrame`:\n"
]
},
{
@ -2844,9 +2857,9 @@
"id": "zpMvCkLSgRsJ"
},
"source": [
"Ten en cuenta que la columna 3 aún no tiene valores: la dirección predeterminada es completar los valores fila por fila.\n",
"Observa que la columna 3 sigue sin valores: la dirección predeterminada es llenar los valores fila por fila.\n",
"\n",
"> **Conclusión:** Hay múltiples maneras de tratar los valores faltantes en tus conjuntos de datos. La estrategia específica que utilices (eliminarlos, reemplazarlos, o incluso cómo los reemplazas) debe estar dictada por las particularidades de esos datos. Desarrollarás un mejor sentido de cómo manejar los valores faltantes cuanto más trabajes e interactúes con conjuntos de datos.\n"
"> **Conclusión:** Hay múltiples formas de tratar los valores faltantes en tus conjuntos de datos. La estrategia específica que utilices (eliminarlos, reemplazarlos, o incluso cómo los reemplazas) debe estar dictada por las particularidades de esos datos. Desarrollarás un mejor sentido de cómo manejar los valores faltantes cuanto más trabajes e interactúes con conjuntos de datos.\n"
]
},
{
@ -2870,7 +2883,7 @@
"source": [
"**CODIFICACIÓN DE ETIQUETAS**\n",
"\n",
"La codificación de etiquetas consiste básicamente en convertir cada categoría en un número. Por ejemplo, supongamos que tenemos un conjunto de datos de pasajeros de aerolíneas y hay una columna que contiene su clase entre las siguientes ['clase ejecutiva', 'clase económica', 'primera clase']. Si se realiza la codificación de etiquetas en esto, se transformaría en [0,1,2]. Veamos un ejemplo mediante código. Como estaremos aprendiendo `scikit-learn` en los próximos cuadernos, no lo utilizaremos aquí.\n"
"La codificación de etiquetas consiste básicamente en convertir cada categoría en un número. Por ejemplo, supongamos que tenemos un conjunto de datos de pasajeros de aerolíneas y hay una columna que contiene su clase entre las siguientes ['clase ejecutiva', 'clase económica', 'primera clase']. Si se realiza la codificación de etiquetas en esto, se transformaría en [0,1,2]. Veamos un ejemplo con código. Como estaremos aprendiendo `scikit-learn` en los próximos cuadernos, no lo utilizaremos aquí.\n"
]
},
{
@ -2978,7 +2991,7 @@
"id": "IDHnkwTYov-h"
},
"source": [
"Para realizar la codificación de etiquetas en la primera columna, primero debemos describir un mapeo de cada clase a un número, antes de reemplazar\n"
"Para realizar la codificación de etiquetas en la primera columna, primero debemos describir un mapeo de cada clase a un número, antes de reemplazar.\n"
]
},
{
@ -3080,8 +3093,8 @@
"id": "ftnF-TyapOPt"
},
"source": [
"Como podemos ver, el resultado coincide con lo que esperábamos. Entonces, ¿cuándo usamos la codificación de etiquetas? La codificación de etiquetas se utiliza en uno o ambos de los siguientes casos:\n",
"1. Cuando el número de categorías es grande\n",
"Como podemos observar, el resultado coincide con lo que esperábamos. Entonces, ¿cuándo usamos la codificación de etiquetas? La codificación de etiquetas se utiliza en uno o ambos de los siguientes casos:\n",
"1. Cuando el número de categorías es grande.\n",
"2. Cuando las categorías tienen un orden.\n"
]
},
@ -3091,11 +3104,11 @@
"id": "eQPAPVwsqWT7"
},
"source": [
"**CODIFICACIÓN ONE HOT**\n",
"**ONE HOT ENCODING**\n",
"\n",
"Otro tipo de codificación es la Codificación One Hot. En este tipo de codificación, cada categoría de la columna se agrega como una columna separada y cada punto de datos recibirá un 0 o un 1 dependiendo de si contiene esa categoría. Por lo tanto, si hay n categorías diferentes, se añadirán n columnas al dataframe.\n",
"Otro tipo de codificación es One Hot Encoding. En este tipo de codificación, cada categoría de la columna se agrega como una columna separada y cada dato recibirá un 0 o un 1 dependiendo de si contiene esa categoría. Por lo tanto, si hay n categorías diferentes, se añadirán n columnas al dataframe.\n",
"\n",
"Por ejemplo, tomemos el mismo ejemplo de clases de avión. Las categorías eran: ['business class', 'economy class', 'first class']. Entonces, si realizamos la codificación one hot, se agregarán las siguientes tres columnas al conjunto de datos: ['class_business class', 'class_economy class', 'class_first class'].\n"
"Por ejemplo, tomemos el mismo ejemplo de clases de avión. Las categorías eran: ['business class', 'economy class', 'first class']. Entonces, si realizamos One Hot Encoding, se agregarán las siguientes tres columnas al conjunto de datos: ['class_business class', 'class_economy class', 'class_first class'].\n"
]
},
{
@ -3328,7 +3341,7 @@
"id": "_zXRLOjXujdA"
},
"source": [
"Cada columna codificada en caliente contiene 0 o 1, lo que especifica si esa categoría existe para ese punto de datos.\n"
"Cada columna codificada en formato one-hot contiene 0 o 1, lo que especifica si esa categoría existe para ese punto de datos.\n"
]
},
{
@ -3349,9 +3362,9 @@
"id": "XnUmci_4uvyu"
},
"source": [
"> Puntos clave: \n",
"1. La codificación se utiliza para convertir datos no numéricos en datos numéricos. \n",
"2. Existen dos tipos de codificación: Codificación de etiquetas (Label encoding) y Codificación One Hot (One Hot encoding), ambas pueden realizarse según las necesidades del conjunto de datos. \n"
"> Puntos clave:\n",
"1. La codificación se realiza para convertir datos no numéricos en datos numéricos.\n",
"2. Hay dos tipos de codificación: codificación de etiquetas y codificación One Hot, ambas pueden realizarse según las necesidades del conjunto de datos.\n"
]
},
{
@ -3362,7 +3375,7 @@
"source": [
"## Eliminando datos duplicados\n",
"\n",
"> **Objetivo de aprendizaje:** Al final de esta subsección, deberías sentirte cómodo identificando y eliminando valores duplicados de DataFrames.\n",
"> **Objetivo de aprendizaje:** Al final de esta subsección, deberías sentirte cómodo identificando y eliminando valores duplicados de los DataFrames.\n",
"\n",
"Además de los datos faltantes, a menudo encontrarás datos duplicados en conjuntos de datos del mundo real. Afortunadamente, pandas ofrece una forma sencilla de detectar y eliminar entradas duplicadas.\n"
]
@ -3375,7 +3388,7 @@
"source": [
"### Identificando duplicados: `duplicated`\n",
"\n",
"Puedes identificar fácilmente valores duplicados utilizando el método `duplicated` en pandas, el cual devuelve una máscara booleana que indica si una entrada en un `DataFrame` es un duplicado de una anterior. Vamos a crear otro ejemplo de `DataFrame` para ver esto en acción.\n"
"Puedes identificar fácilmente valores duplicados utilizando el método `duplicated` en pandas, que devuelve una máscara booleana indicando si un registro en un `DataFrame` es un duplicado de uno anterior. Vamos a crear otro ejemplo de `DataFrame` para ver esto en acción.\n"
]
},
{
@ -3664,7 +3677,525 @@
"metadata": {
"id": "GvX4og1EgRsL"
},
"source": []
"source": [
"> **Conclusión:** Eliminar datos duplicados es una parte esencial de casi todos los proyectos de ciencia de datos. Los datos duplicados pueden alterar los resultados de tus análisis y proporcionarte resultados inexactos.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Verificación de calidad de datos en el mundo real\n",
"\n",
"> **Objetivo de aprendizaje:** Al finalizar esta sección, deberías sentirte cómodo detectando y corrigiendo problemas comunes de calidad de datos en el mundo real, incluyendo valores categóricos inconsistentes, valores numéricos anormales (valores atípicos) y entidades duplicadas con variaciones.\n",
"\n",
"Aunque los valores faltantes y los duplicados exactos son problemas comunes, los conjuntos de datos del mundo real suelen contener problemas más sutiles:\n",
"\n",
"1. **Valores categóricos inconsistentes**: La misma categoría escrita de manera diferente (por ejemplo, \"USA\", \"U.S.A\", \"United States\").\n",
"2. **Valores numéricos anormales**: Valores extremos que indican errores de entrada de datos (por ejemplo, edad = 999).\n",
"3. **Filas casi duplicadas**: Registros que representan la misma entidad con ligeras variaciones.\n",
"\n",
"Exploremos técnicas para detectar y manejar estos problemas.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Creando un Conjunto de Datos \"Sucio\" de Ejemplo\n",
"\n",
"Primero, vamos a crear un conjunto de datos de ejemplo que contenga los tipos de problemas que comúnmente encontramos en datos del mundo real:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import numpy as np\n",
"\n",
"# Create a sample dataset with quality issues\n",
"dirty_data = pd.DataFrame({\n",
" 'customer_id': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],\n",
" 'name': ['John Smith', 'Jane Doe', 'John Smith', 'Bob Johnson', \n",
" 'Alice Williams', 'Charlie Brown', 'John Smith', 'Eva Martinez',\n",
" 'Bob Johnson', 'Diana Prince', 'Frank Castle', 'Alice Williams'],\n",
" 'age': [25, 32, 25, 45, 28, 199, 25, 31, 45, 27, -5, 28],\n",
" 'country': ['USA', 'UK', 'U.S.A', 'Canada', 'USA', 'United Kingdom',\n",
" 'United States', 'Mexico', 'canada', 'USA', 'UK', 'usa'],\n",
" 'purchase_amount': [100.50, 250.00, 105.00, 320.00, 180.00, 90.00,\n",
" 102.00, 275.00, 325.00, 195.00, 410.00, 185.00]\n",
"})\n",
"\n",
"print(\"Sample 'Dirty' Dataset:\")\n",
"print(dirty_data)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 1. Detectar valores categóricos inconsistentes\n",
"\n",
"Observa que la columna `country` tiene múltiples representaciones para los mismos países. Vamos a identificar estas inconsistencias:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Check unique values in the country column\n",
"print(\"Unique country values:\")\n",
"print(dirty_data['country'].unique())\n",
"print(f\"\\nTotal unique values: {dirty_data['country'].nunique()}\")\n",
"\n",
"# Count occurrences of each variation\n",
"print(\"\\nValue counts:\")\n",
"print(dirty_data['country'].value_counts())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Estandarización de Valores Categóricos\n",
"\n",
"Podemos crear un mapeo para estandarizar estos valores. Un enfoque sencillo es convertirlos a minúsculas y crear un diccionario de mapeo:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Create a standardization mapping\n",
"country_mapping = {\n",
" 'usa': 'USA',\n",
" 'u.s.a': 'USA',\n",
" 'united states': 'USA',\n",
" 'uk': 'UK',\n",
" 'united kingdom': 'UK',\n",
" 'canada': 'Canada',\n",
" 'mexico': 'Mexico'\n",
"}\n",
"\n",
"# Standardize the country column\n",
"dirty_data['country_clean'] = dirty_data['country'].str.lower().map(country_mapping)\n",
"\n",
"print(\"Before standardization:\")\n",
"print(dirty_data['country'].value_counts())\n",
"print(\"\\nAfter standardization:\")\n",
"print(dirty_data[['country_clean']].value_counts())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Alternativa: Usar coincidencia difusa**\n",
"\n",
"Para casos más complejos, podemos usar la coincidencia de cadenas difusa con la biblioteca `rapidfuzz` para detectar automáticamente cadenas similares:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"try:\n",
" from rapidfuzz import process, fuzz\n",
"except ImportError:\n",
" print(\"rapidfuzz is not installed. Please install it with 'pip install rapidfuzz' to use fuzzy matching.\")\n",
" process = None\n",
" fuzz = None\n",
"\n",
"# Get unique countries\n",
"unique_countries = dirty_data['country'].unique()\n",
"\n",
"# For each country, find similar matches\n",
"if process is not None and fuzz is not None:\n",
" print(\"Finding similar country names (similarity > 70%):\")\n",
" for country in unique_countries:\n",
" matches = process.extract(country, unique_countries, scorer=fuzz.ratio, limit=3)\n",
" # Filter matches with similarity > 70 and not identical\n",
" similar = [m for m in matches if m[1] > 70 and m[0] != country]\n",
" if similar:\n",
" print(f\"\\n'{country}' is similar to:\")\n",
" for match, score, _ in similar:\n",
" print(f\" - '{match}' (similarity: {score}%)\")\n",
"else:\n",
" print(\"Skipping fuzzy matching because rapidfuzz is not available.\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 2. Detectar valores numéricos anormales (Outliers)\n",
"\n",
"Al observar la columna `age`, encontramos algunos valores sospechosos como 199 y -5. Utilicemos métodos estadísticos para detectar estos outliers.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Display basic statistics\n",
"print(\"Age column statistics:\")\n",
"print(dirty_data['age'].describe())\n",
"\n",
"# Identify impossible values using domain knowledge\n",
"print(\"\\nRows with impossible age values (< 0 or > 120):\")\n",
"impossible_ages = dirty_data[(dirty_data['age'] < 0) | (dirty_data['age'] > 120)]\n",
"print(impossible_ages[['customer_id', 'name', 'age']])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Usando el método IQR (Rango Intercuartílico)\n",
"\n",
"El método IQR es una técnica estadística robusta para la detección de valores atípicos que es menos sensible a valores extremos:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Calculate IQR for age (excluding impossible values)\n",
"valid_ages = dirty_data[(dirty_data['age'] >= 0) & (dirty_data['age'] <= 120)]['age']\n",
"\n",
"Q1 = valid_ages.quantile(0.25)\n",
"Q3 = valid_ages.quantile(0.75)\n",
"IQR = Q3 - Q1\n",
"\n",
"# Define outlier bounds\n",
"lower_bound = Q1 - 1.5 * IQR\n",
"upper_bound = Q3 + 1.5 * IQR\n",
"\n",
"print(f\"IQR-based outlier bounds for age: [{lower_bound:.2f}, {upper_bound:.2f}]\")\n",
"\n",
"# Identify outliers\n",
"age_outliers = dirty_data[(dirty_data['age'] < lower_bound) | (dirty_data['age'] > upper_bound)]\n",
"print(f\"\\nRows with age outliers:\")\n",
"print(age_outliers[['customer_id', 'name', 'age']])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Usando el Método Z-Score\n",
"\n",
"El método Z-score identifica valores atípicos basándose en desviaciones estándar respecto a la media:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"try:\n",
" from scipy import stats\n",
"except ImportError:\n",
" print(\"scipy is required for Z-score calculation. Please install it with 'pip install scipy' and rerun this cell.\")\n",
"else:\n",
" # Calculate Z-scores for age, handling NaN values\n",
" age_nonan = dirty_data['age'].dropna()\n",
" zscores = np.abs(stats.zscore(age_nonan))\n",
" dirty_data['age_zscore'] = np.nan\n",
" dirty_data.loc[age_nonan.index, 'age_zscore'] = zscores\n",
"\n",
" # Typically, Z-score > 3 indicates an outlier\n",
" print(\"Rows with age Z-score > 3:\")\n",
" zscore_outliers = dirty_data[dirty_data['age_zscore'] > 3]\n",
" print(zscore_outliers[['customer_id', 'name', 'age', 'age_zscore']])\n",
"\n",
" # Clean up the temporary column\n",
" dirty_data = dirty_data.drop('age_zscore', axis=1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Manejo de valores atípicos\n",
"\n",
"Una vez detectados, los valores atípicos pueden manejarse de varias maneras:\n",
"1. **Eliminar**: Eliminar filas con valores atípicos (si son errores)\n",
"2. **Limitar**: Reemplazar con valores límite\n",
"3. **Reemplazar con NaN**: Tratar como datos faltantes y usar técnicas de imputación\n",
"4. **Conservar**: Si son valores extremos legítimos\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Create a cleaned version by replacing impossible ages with NaN\n",
"dirty_data['age_clean'] = dirty_data['age'].apply(\n",
" lambda x: np.nan if (x < 0 or x > 120) else x\n",
")\n",
"\n",
"print(\"Age column before and after cleaning:\")\n",
"print(dirty_data[['customer_id', 'name', 'age', 'age_clean']])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 3. Detectando Filas Casi Duplicadas\n",
"\n",
"Observa que nuestro conjunto de datos tiene múltiples entradas para \"John Smith\" con valores ligeramente diferentes. Identifiquemos posibles duplicados basándonos en la similitud de nombres.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# First, let's look at exact name matches (ignoring extra whitespace)\n",
"dirty_data['name_normalized'] = dirty_data['name'].str.strip().str.lower()\n",
"\n",
"print(\"Checking for duplicate names:\")\n",
"duplicate_names = dirty_data[dirty_data.duplicated(['name_normalized'], keep=False)]\n",
"print(duplicate_names.sort_values('name_normalized')[['customer_id', 'name', 'age', 'country']])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Encontrar casi duplicados con coincidencia difusa\n",
"\n",
"Para una detección de duplicados más avanzada, podemos usar coincidencia difusa para encontrar nombres similares:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"try:\n",
" from rapidfuzz import process, fuzz\n",
"\n",
" # Function to find potential duplicates\n",
" def find_near_duplicates(df, column, threshold=90):\n",
" \"\"\"\n",
" Find near-duplicate entries in a column using fuzzy matching.\n",
" \n",
" Parameters:\n",
" - df: DataFrame\n",
" - column: Column name to check for duplicates\n",
" - threshold: Similarity threshold (0-100)\n",
" \n",
" Returns: List of potential duplicate groups\n",
" \"\"\"\n",
" values = df[column].unique()\n",
" duplicate_groups = []\n",
" checked = set()\n",
" \n",
" for value in values:\n",
" if value in checked:\n",
" continue\n",
" \n",
" # Find similar values\n",
" matches = process.extract(value, values, scorer=fuzz.ratio, limit=len(values))\n",
" similar = [m[0] for m in matches if m[1] >= threshold]\n",
" \n",
" if len(similar) > 1:\n",
" duplicate_groups.append(similar)\n",
" checked.update(similar)\n",
" \n",
" return duplicate_groups\n",
"\n",
" # Find near-duplicate names\n",
" duplicate_groups = find_near_duplicates(dirty_data, 'name', threshold=90)\n",
"\n",
" print(\"Potential duplicate groups:\")\n",
" for i, group in enumerate(duplicate_groups, 1):\n",
" print(f\"\\nGroup {i}:\")\n",
" for name in group:\n",
" matching_rows = dirty_data[dirty_data['name'] == name]\n",
" print(f\" '{name}': {len(matching_rows)} occurrence(s)\")\n",
" for _, row in matching_rows.iterrows():\n",
" print(f\" - Customer {row['customer_id']}: age={row['age']}, country={row['country']}\")\n",
"except ImportError:\n",
" print(\"rapidfuzz is not installed. Skipping fuzzy matching for near-duplicates.\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Manejo de duplicados\n",
"\n",
"Una vez identificados, debes decidir cómo manejar los duplicados:\n",
"1. **Mantener la primera ocurrencia**: Usa `drop_duplicates(keep='first')`\n",
"2. **Mantener la última ocurrencia**: Usa `drop_duplicates(keep='last')`\n",
"3. **Agregar información**: Combina la información de las filas duplicadas\n",
"4. **Revisión manual**: Marca para revisión humana\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Example: Remove duplicates based on normalized name, keeping first occurrence\n",
"cleaned_data = dirty_data.drop_duplicates(subset=['name_normalized'], keep='first')\n",
"\n",
"print(f\"Original dataset: {len(dirty_data)} rows\")\n",
"print(f\"After removing name duplicates: {len(cleaned_data)} rows\")\n",
"print(f\"Removed: {len(dirty_data) - len(cleaned_data)} duplicate rows\")\n",
"\n",
"print(\"\\nCleaned dataset:\")\n",
"print(cleaned_data[['customer_id', 'name', 'age', 'country_clean']])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Resumen: Pipeline Completo de Limpieza de Datos\n",
"\n",
"Vamos a reunir todo en un pipeline integral de limpieza:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def clean_dataset(df):\n",
" \"\"\"\n",
" Comprehensive data cleaning function.\n",
" \"\"\"\n",
" # Create a copy to avoid modifying the original\n",
" cleaned = df.copy()\n",
" \n",
" # 1. Standardize categorical values (country)\n",
" country_mapping = {\n",
" 'usa': 'USA', 'u.s.a': 'USA', 'united states': 'USA',\n",
" 'uk': 'UK', 'united kingdom': 'UK',\n",
" 'canada': 'Canada', 'mexico': 'Mexico'\n",
" }\n",
" cleaned['country'] = cleaned['country'].str.lower().map(country_mapping)\n",
" \n",
" # 2. Clean abnormal age values\n",
" cleaned['age'] = cleaned['age'].apply(\n",
" lambda x: np.nan if (x < 0 or x > 120) else x\n",
" )\n",
" \n",
" # 3. Remove near-duplicate names (normalize whitespace)\n",
" cleaned['name'] = cleaned['name'].str.strip()\n",
" cleaned = cleaned.drop_duplicates(subset=['name'], keep='first')\n",
" \n",
" return cleaned\n",
"\n",
"# Apply the cleaning pipeline\n",
"final_cleaned_data = clean_dataset(dirty_data)\n",
"\n",
"print(\"Before cleaning:\")\n",
"print(f\" Rows: {len(dirty_data)}\")\n",
"print(f\" Unique countries: {dirty_data['country'].nunique()}\")\n",
"print(f\" Invalid ages: {((dirty_data['age'] < 0) | (dirty_data['age'] > 120)).sum()}\")\n",
"\n",
"print(\"\\nAfter cleaning:\")\n",
"print(f\" Rows: {len(final_cleaned_data)}\")\n",
"print(f\" Unique countries: {final_cleaned_data['country'].nunique()}\")\n",
"print(f\" Invalid ages: {((final_cleaned_data['age'] < 0) | (final_cleaned_data['age'] > 120)).sum()}\")\n",
"\n",
"print(\"\\nCleaned dataset:\")\n",
"print(final_cleaned_data[['customer_id', 'name', 'age', 'country', 'purchase_amount']])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 🎯 Ejercicio de Desafío\n",
"\n",
"¡Ahora es tu turno! A continuación, tienes una nueva fila de datos con múltiples problemas de calidad. ¿Puedes:\n",
"\n",
"1. Identificar todos los problemas en esta fila\n",
"2. Escribir código para corregir cada problema\n",
"3. Agregar la fila corregida al conjunto de datos\n",
"\n",
"Aquí están los datos problemáticos:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# New problematic row\n",
"new_row = pd.DataFrame({\n",
" 'customer_id': [13],\n",
" 'name': [' Diana Prince '], # Extra whitespace\n",
" 'age': [250], # Impossible age\n",
" 'country': ['U.S.A.'], # Inconsistent format\n",
" 'purchase_amount': [150.00]\n",
"})\n",
"\n",
"print(\"New row to clean:\")\n",
"print(new_row)\n",
"\n",
"# TODO: Your code here to clean this row\n",
"# Hints:\n",
"# 1. Strip whitespace from the name\n",
"# 2. Check if the name is a duplicate (Diana Prince already exists)\n",
"# 3. Handle the impossible age value\n",
"# 4. Standardize the country name\n",
"\n",
"# Example solution (uncomment and modify as needed):\n",
"# new_row_cleaned = new_row.copy()\n",
"# new_row_cleaned['name'] = new_row_cleaned['name'].str.strip()\n",
"# new_row_cleaned['age'] = np.nan # Invalid age\n",
"# new_row_cleaned['country'] = 'USA' # Standardized\n",
"# print(\"\\nCleaned row:\")\n",
"# print(new_row_cleaned)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Puntos clave\n",
"\n",
"1. **Categorías inconsistentes** son comunes en datos del mundo real. Siempre revisa los valores únicos y estandarízalos utilizando mapeos o coincidencias difusas.\n",
"\n",
"2. **Valores atípicos** pueden afectar significativamente tu análisis. Usa conocimiento del dominio combinado con métodos estadísticos (IQR, puntuación Z) para detectarlos.\n",
"\n",
"3. **Casi duplicados** son más difíciles de detectar que los duplicados exactos. Considera usar coincidencias difusas y normalizar los datos (convertir a minúsculas, eliminar espacios) para identificarlos.\n",
"\n",
"4. **La limpieza de datos es iterativa**. Es posible que necesites aplicar múltiples técnicas y revisar los resultados antes de finalizar tu conjunto de datos limpio.\n",
"\n",
"5. **Documenta tus decisiones**. Lleva un registro de los pasos de limpieza que aplicaste y por qué, ya que esto es importante para la reproducibilidad y la transparencia.\n",
"\n",
"> **Mejor práctica:** Siempre conserva una copia de tus datos originales \"sucios\". Nunca sobrescribas tus archivos de datos fuente; crea versiones limpias con convenciones de nombres claras como `data_cleaned.csv`.\n"
]
},
{
"cell_type": "markdown",
@ -3698,8 +4229,8 @@
"version": "3.5.4"
},
"coopTranslator": {
"original_hash": "8533b3a2230311943339963fc7f04c21",
"translation_date": "2025-09-01T21:37:59+00:00",
"original_hash": "6301339d1c9a301b00639c635dc9b731",
"translation_date": "2025-10-03T18:56:03+00:00",
"source_file": "2-Working-With-Data/08-data-preparation/notebook.ipynb",
"language_code": "es"
}

Loading…
Cancel
Save