Por Jose R. Zapata - https://joserzapata.github.io/
Objetivo: practicar métodos de
pandas.Series.str
para limpiar y normalizar texto en DataFrames. Cada ejercicio incluye un Enunciado y una celda donde se ve el resultado de la solución.
Referencias:
- Procesamiento básico de texto (curso de NLP): https://joserzapata.github.io/courses/nlp/procesamiento-basico/
- Documentación de
pandas.Series.str
: https://pandas.pydata.org/docs/reference/series.html#string-handling
NOTA: Realizar Primero los ejercicios y luego revisar las soluciones propuestas.
Para realizar los ejercicios prácticos de este capitulo, hacer click en el siguiente enlace para descargar el notebook y agregarlo a su repositorio
Notebook - Ejercicio Limpieza de datos - click para descargar
Instalación de dependencias
instala e importa las librerias que necesites para ejecutar los ejercicios, por ejemplo para instalar Jupyter usa:
uv add jupyter
# Copie su código aca
import pandas as pd
import numpy as np
import re
pd.set_option("display.max_colwidth", None)
Conjuntos de datos de juguete
Creamos 4 DataFrames para practicar distintas tareas de limpieza: texto general, tweets, productos y personas.
## Texto general
df_texto = pd.DataFrame({
"texto": [
" ¡Hola Mundo!!! Esto es un EJEMPLO: visita https://miweb.com #DataScience :) ",
"Pandas > numpy? 🤔 Email: persona@example.com ",
"Me gusta el café colombiano; es buenísimo!!! #Café #Colombia @juan",
"Oferta!!! 3x2 en jabón líquido 500ml - CÓDIGO: A-123",
" TABLAS\t, \n espacios y saltos de línea.\r\n",
"Teléfono: (57) 300-123-45-67; Whatsapp +57 300 222 33 44",
"Dirección: Cll 10 # 5-20; Medellín. Barrio: La América",
"Emoji test: 😀🙌🏽🏳️🌈, símbolos ©®™ y otros…",
]
})
## Tweets
df_tweets = pd.DataFrame({
"tweet": [
"RT @maria: Nuevo post en el blog -> http://blog.com/post?id=45 #nlp #python",
"¡Me encanta Pandas! #datos #Python https://example.org @data_science 😊",
"Probando cosas en Jupyter... sin link ni hashtag",
"@juan y @ana lanzaron curso de NLP en https://cursos.ai #nlp #ml",
"¿Pandas o Polars? debátanlo aquí 👉 https://foro.com #data",
]
})
## Productos y precios
df_productos = pd.DataFrame({
"producto": [
"Camisa talla M",
"Pantalón-XL",
"Zapato, Talla: 42",
"Blusa s",
"Medias 10-12",
"Polo Talla l",
"Vestido - 36",
"Sombrero (talla Única)",
],
"precio": [
"$1.234,50",
"USD 45",
"30,00 €",
"25.000",
"$ 0",
"S/. 120.90",
"COP 9.990",
"AR$ 2.550,00",
],
})
## Personas, respuestas y direcciones
df_personas = pd.DataFrame({
"nombre": [
"ana María LOPEZ",
"Juan perez",
"Ñandú Gómez",
"Miguel (Soporte)",
" MÓNICA de la CRUZ ",
"luis-delgado",
],
"categoria": ["Sí", "si", "SI ", "No", "—", None],
"direccion": [
"Calle 45 # 12-34, Bogotá",
"Av. Siempre Viva 742 - Lima",
"Cll. 10 No. 5-20 Medellín",
"Cra 7a # 45-60, Bogotá",
"Av. 9 #12-34 Cali",
"Av. Insurgentes Sur 1234, CDMX",
],
"id_raw": ["abc-0001", "abc 001", "ABC0002", "Abc_003", "abc-00004", "ABC-0005"],
})
print("df_texto:")
display(df_texto)
print("\ndf_tweets:")
display(df_tweets)
print("\ndf_productos:")
display(df_productos)
print("\ndf_personas:")
display(df_personas)
df_texto:
texto | |
---|---|
0 | ¡Hola Mundo!!! Esto es un EJEMPLO: visita https://miweb.com #DataScience :) |
1 | Pandas > numpy? 🤔 Email: persona@example.com |
2 | Me gusta el café colombiano; es buenísimo!!! #Café #Colombia @juan |
3 | Oferta!!! 3x2 en jabón líquido 500ml - CÓDIGO: A-123 |
4 | TABLAS\t, \n espacios y saltos de línea.\r\n |
5 | Teléfono: (57) 300-123-45-67; Whatsapp +57 300 222 33 44 |
6 | Dirección: Cll 10 # 5-20; Medellín. Barrio: La América |
7 | Emoji test: 😀🙌🏽🏳️🌈, símbolos ©®™ y otros… |
df_tweets:
tweet | |
---|---|
0 | RT @maria: Nuevo post en el blog -> http://blog.com/post?id=45 #nlp #python |
1 | ¡Me encanta Pandas! #datos #Python https://example.org @data_science 😊 |
2 | Probando cosas en Jupyter... sin link ni hashtag |
3 | @juan y @ana lanzaron curso de NLP en https://cursos.ai #nlp #ml |
4 | ¿Pandas o Polars? debátanlo aquí 👉 https://foro.com #data |
df_productos:
producto | precio | |
---|---|---|
0 | Camisa talla M | $1.234,50 |
1 | Pantalón-XL | USD 45 |
2 | Zapato, Talla: 42 | 30,00 € |
3 | Blusa s | 25.000 |
4 | Medias 10-12 | $ 0 |
5 | Polo Talla l | S/. 120.90 |
6 | Vestido - 36 | COP 9.990 |
7 | Sombrero (talla Única) | AR$ 2.550,00 |
df_personas:
nombre | categoria | direccion | id_raw | |
---|---|---|---|---|
0 | ana María LOPEZ | Sí | Calle 45 # 12-34, Bogotá | abc-0001 |
1 | Juan perez | si | Av. Siempre Viva 742 - Lima | abc 001 |
2 | Ñandú Gómez | SI | Cll. 10 No. 5-20 Medellín | ABC0002 |
3 | Miguel (Soporte) | No | Cra 7a # 45-60, Bogotá | Abc_003 |
4 | MÓNICA de la CRUZ | — | Av. 9 #12-34 Cali | abc-00004 |
5 | luis-delgado | None | Av. Insurgentes Sur 1234, CDMX | ABC-0005 |
Inspección rápida
- Explora los cuatro DataFrames
- Identifica qué columnas requieren limpieza textual y por qué (espacios, acentos, emojis, URLs, etc.).
- Escribe un breve comentario (como comentario en la celda) con tus observaciones.
# Copie su código aca
df_texto.info()
df_tweets.info()
df_productos.info()
df_personas.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 8 entries, 0 to 7
Data columns (total 1 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 texto 8 non-null object
dtypes: object(1)
memory usage: 196.0+ bytes
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 5 entries, 0 to 4
Data columns (total 1 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 tweet 5 non-null object
dtypes: object(1)
memory usage: 172.0+ bytes
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 8 entries, 0 to 7
Data columns (total 2 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 producto 8 non-null object
1 precio 8 non-null object
dtypes: object(2)
memory usage: 260.0+ bytes
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 6 entries, 0 to 5
Data columns (total 4 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 nombre 6 non-null object
1 categoria 5 non-null object
2 direccion 6 non-null object
3 id_raw 6 non-null object
dtypes: object(4)
memory usage: 324.0+ bytes
1) Normaliza a minúsculas
En el DataFrame df_texto
, crea una nueva columna texto_min
que contenga el texto de la columna texto
en minúsculas.
# Copie su código aca
df_texto["texto_min"] = df_texto["texto"].astype(str).str.lower()
df_texto[["texto", "texto_min"]]
texto | texto_min | |
---|---|---|
0 | ¡Hola Mundo!!! Esto es un EJEMPLO: visita https://miweb.com #DataScience :) | ¡hola mundo!!! esto es un ejemplo: visita https://miweb.com #datascience :) |
1 | Pandas > numpy? 🤔 Email: persona@example.com | pandas > numpy? 🤔 email: persona@example.com |
2 | Me gusta el café colombiano; es buenísimo!!! #Café #Colombia @juan | me gusta el café colombiano; es buenísimo!!! #café #colombia @juan |
3 | Oferta!!! 3x2 en jabón líquido 500ml - CÓDIGO: A-123 | oferta!!! 3x2 en jabón líquido 500ml - código: a-123 |
4 | TABLAS\t, \n espacios y saltos de línea.\r\n | tablas\t, \n espacios y saltos de línea.\r\n |
5 | Teléfono: (57) 300-123-45-67; Whatsapp +57 300 222 33 44 | teléfono: (57) 300-123-45-67; whatsapp +57 300 222 33 44 |
6 | Dirección: Cll 10 # 5-20; Medellín. Barrio: La América | dirección: cll 10 # 5-20; medellín. barrio: la américa |
7 | Emoji test: 😀🙌🏽🏳️🌈, símbolos ©®™ y otros… | emoji test: 😀🙌🏽🏳️🌈, símbolos ©®™ y otros… |
2) Eliminar Espacios
Genera la columna texto_espacios
eliminando espacios al inicio/fin
# Copie su código aca
df_texto["texto_espacios"] = df_texto["texto_min"].str.strip()
df_texto[["texto_espacios", "texto_min"]]
texto_espacios | texto_min | |
---|---|---|
0 | ¡hola mundo!!! esto es un ejemplo: visita https://miweb.com #datascience :) | ¡hola mundo!!! esto es un ejemplo: visita https://miweb.com #datascience :) |
1 | pandas > numpy? 🤔 email: persona@example.com | pandas > numpy? 🤔 email: persona@example.com |
2 | me gusta el café colombiano; es buenísimo!!! #café #colombia @juan | me gusta el café colombiano; es buenísimo!!! #café #colombia @juan |
3 | oferta!!! 3x2 en jabón líquido 500ml - código: a-123 | oferta!!! 3x2 en jabón líquido 500ml - código: a-123 |
4 | tablas\t, \n espacios y saltos de línea. | tablas\t, \n espacios y saltos de línea.\r\n |
5 | teléfono: (57) 300-123-45-67; whatsapp +57 300 222 33 44 | teléfono: (57) 300-123-45-67; whatsapp +57 300 222 33 44 |
6 | dirección: cll 10 # 5-20; medellín. barrio: la américa | dirección: cll 10 # 5-20; medellín. barrio: la américa |
7 | emoji test: 😀🙌🏽🏳️🌈, símbolos ©®™ y otros… | emoji test: 😀🙌🏽🏳️🌈, símbolos ©®™ y otros… |
3) Puntuación
Crea texto_sin_punct
removiendo puntuación y símbolos, conservando letras y espacios.
# Copie su código aca
df_texto["texto_sin_punct"] = df_texto["texto_espacios"].str.replace(
r"[^\w\s]", "", regex=True
)
df_texto["texto_sin_punct"]
0 hola mundo esto es un ejemplo visita httpsmiwebcom datascience
1 pandas numpy email personaexamplecom
2 me gusta el café colombiano es buenísimo café colombia juan
3 oferta 3x2 en jabón líquido 500ml código a123
4 tablas\t \n espacios y saltos de línea
5 teléfono 57 3001234567 whatsapp 57 300 222 33 44
6 dirección cll 10 520 medellín barrio la américa
7 emoji test símbolos y otros
Name: texto_sin_punct, dtype: object
4) Acentos
Elimina los acentos de las vocales del texto df_texto
y grábalo en una nueva columna texto_sin_acentos
.
# Copie su código aca
def quitar_acentos(text: str) -> str:
MAP_VOCALES = {
"á": "a",
"é": "e",
"í": "i",
"ó": "o",
"ú": "u",
"ü": "u",
}
translate = str.maketrans(MAP_VOCALES)
text = text.translate(translate)
return text
df_texto["texto_sin_acentos"] = df_texto["texto_sin_punct"].apply(quitar_acentos)
df_texto["texto_sin_acentos"]
0 hola mundo esto es un ejemplo visita httpsmiwebcom datascience
1 pandas numpy email personaexamplecom
2 me gusta el cafe colombiano es buenisimo cafe colombia juan
3 oferta 3x2 en jabon liquido 500ml codigo a123
4 tablas\t \n espacios y saltos de linea
5 telefono 57 3001234567 whatsapp 57 300 222 33 44
6 direccion cll 10 520 medellin barrio la america
7 emoji test simbolos y otros
Name: texto_sin_acentos, dtype: object
5) Emojis
Crear la columna texto_sin_emoji
quitando emojis, pictogramas y símbolos gráficos comunes.
# Copie su código aca
emoji_re = re.compile("[\U0001f300-\U0001faff\U00002700-\U000027bf]+", flags=re.UNICODE)
df_texto["texto_sin_emoji"] = df_texto["texto_espacios"].apply(
lambda s: emoji_re.sub("", s)
)
df_texto["texto_sin_emoji"]
0 ¡hola mundo!!! esto es un ejemplo: visita https://miweb.com #datascience :)
1 pandas > numpy? email: persona@example.com
2 me gusta el café colombiano; es buenísimo!!! #café #colombia @juan
3 oferta!!! 3x2 en jabón líquido 500ml - código: a-123
4 tablas\t, \n espacios y saltos de línea.
5 teléfono: (57) 300-123-45-67; whatsapp +57 300 222 33 44
6 dirección: cll 10 # 5-20; medellín. barrio: la américa
7 emoji test: ️, símbolos ©®™ y otros…
Name: texto_sin_emoji, dtype: object
6) Extrae emails y URLs
En df_texto
, crea las columnas email
y urls
extrayendo emails y URLs del texto original.
Patrones sugeridos:
- Email:
[A-Za-z0-9_.+-]+@[A-Za-z0-9-]+\.[A-Za-z0-9.-]+
- URL:
https?://\S+
# Copie su código aca
email_pat = r"[A-Za-z0-9_.+-]+@[A-Za-z0-9-]+\.[A-Za-z0-9.-]+"
url_pat = r"https?://\S+"
df_texto["email"] = df_texto["texto"].str.findall(email_pat)
df_texto["urls"] = df_texto["texto"].str.findall(url_pat)
df_texto[["texto", "email", "urls"]]
texto | urls | ||
---|---|---|---|
0 | ¡Hola Mundo!!! Esto es un EJEMPLO: visita https://miweb.com #DataScience :) | [] | [https://miweb.com] |
1 | Pandas > numpy? 🤔 Email: persona@example.com | [persona@example.com] | [] |
2 | Me gusta el café colombiano; es buenísimo!!! #Café #Colombia @juan | [] | [] |
3 | Oferta!!! 3x2 en jabón líquido 500ml - CÓDIGO: A-123 | [] | [] |
4 | TABLAS\t, \n espacios y saltos de línea.\r\n | [] | [] |
5 | Teléfono: (57) 300-123-45-67; Whatsapp +57 300 222 33 44 | [] | [] |
6 | Dirección: Cll 10 # 5-20; Medellín. Barrio: La América | [] | [] |
7 | Emoji test: 😀🙌🏽🏳️🌈, símbolos ©®™ y otros… | [] | [] |
7) Hashtags, menciones y URLs en tweets
En df_tweets
, crea las columnas hashtags
, mentions
y urls
extrayendo hashtags, menciones y URLs del texto del tweet.
# Copie su código aca
pat_hash = r"#\w+"
pat_ment = r"@\w+"
pat_url = r"https?://\S+"
df_tweets["hashtags"] = df_tweets["tweet"].str.findall(pat_hash)
df_tweets["mentions"] = df_tweets["tweet"].str.findall(pat_ment)
df_tweets["urls"] = df_tweets["tweet"].str.findall(pat_url)
df_tweets
tweet | hashtags | mentions | urls | |
---|---|---|---|---|
0 | RT @maria: Nuevo post en el blog -> http://blog.com/post?id=45 #nlp #python | [#nlp, #python] | [@maria] | [http://blog.com/post?id=45] |
1 | ¡Me encanta Pandas! #datos #Python https://example.org @data_science 😊 | [#datos, #Python] | [@data_science] | [https://example.org] |
2 | Probando cosas en Jupyter... sin link ni hashtag | [] | [] | [] |
3 | @juan y @ana lanzaron curso de NLP en https://cursos.ai #nlp #ml | [#nlp, #ml] | [@juan, @ana] | [https://cursos.ai] |
4 | ¿Pandas o Polars? debátanlo aquí 👉 https://foro.com #data | [#data] | [] | [https://foro.com] |
8) ¿Es retuit? Limpia el prefijo
Crea la columna es_rt
(booleano) si el tweet comienza con RT
. Luego genera la columna tweet_sin_rt
quitando el prefijo RT @usuario:
del inicio.
# Copie su código aca
df_tweets["es_rt"] = df_tweets["tweet"].str.startswith("RT ")
df_tweets["tweet_sin_rt"] = df_tweets["tweet"].str.replace(
r"^RT\s+@\w+:\s*", "", regex=True
)
df_tweets
tweet | hashtags | mentions | urls | es_rt | tweet_sin_rt | |
---|---|---|---|---|---|---|
0 | RT @maria: Nuevo post en el blog -> http://blog.com/post?id=45 #nlp #python | [#nlp, #python] | [@maria] | [http://blog.com/post?id=45] | True | Nuevo post en el blog -> http://blog.com/post?id=45 #nlp #python |
1 | ¡Me encanta Pandas! #datos #Python https://example.org @data_science 😊 | [#datos, #Python] | [@data_science] | [https://example.org] | False | ¡Me encanta Pandas! #datos #Python https://example.org @data_science 😊 |
2 | Probando cosas en Jupyter... sin link ni hashtag | [] | [] | [] | False | Probando cosas en Jupyter... sin link ni hashtag |
3 | @juan y @ana lanzaron curso de NLP en https://cursos.ai #nlp #ml | [#nlp, #ml] | [@juan, @ana] | [https://cursos.ai] | False | @juan y @ana lanzaron curso de NLP en https://cursos.ai #nlp #ml |
4 | ¿Pandas o Polars? debátanlo aquí 👉 https://foro.com #data | [#data] | [] | [https://foro.com] | False | ¿Pandas o Polars? debátanlo aquí 👉 https://foro.com #data |
9) tweet_limpio
Crea tweet_limpio
removiendo URLs, menciones y hashtags; además, elimina la puntuación sobrante, quita espacios y pasa a minúsculas.
# Copie su código aca
tmp = df_tweets["tweet_sin_rt"] if "tweet_sin_rt" in df_tweets else df_tweets["tweet"]
tmp = tmp.str.replace(r"https?://\S+", " ", regex=True)
tmp = tmp.str.replace(r"@\w+|#\w+", " ", regex=True)
tmp = tmp.str.replace(r"[^\w\sáéíóúÁÉÍÓÚñÑ]", " ", regex=True)
tmp = tmp.str.replace(r"\s+", " ", regex=True).str.strip().str.casefold()
df_tweets["tweet_limpio"] = tmp
df_tweets[["tweet", "tweet_limpio"]]
tweet | tweet_limpio | |
---|---|---|
0 | RT @maria: Nuevo post en el blog -> http://blog.com/post?id=45 #nlp #python | nuevo post en el blog |
1 | ¡Me encanta Pandas! #datos #Python https://example.org @data_science 😊 | me encanta pandas |
2 | Probando cosas en Jupyter... sin link ni hashtag | probando cosas en jupyter sin link ni hashtag |
3 | @juan y @ana lanzaron curso de NLP en https://cursos.ai #nlp #ml | y lanzaron curso de nlp en |
4 | ¿Pandas o Polars? debátanlo aquí 👉 https://foro.com #data | pandas o polars debátanlo aquí |
10) Talla en df_productos
Extrae y estandariza la talla:
- Letras:
XS, S, M, L, XL, XXL
, oTU
(talla única). - Números: captura como
talla_num
. Crea la columnatalla_std
(letras) ytalla_num
(numérica).
# Copie su código aca
pat_letra = r"(?i)\b(xs|s|m|l|xl|xxl|única|unica|u)\b"
pat_num = r"(\d+(?:\.\d+)?)"
def extraer_talla(row):
txt = str(row["producto"])
m1 = re.search(pat_letra, txt)
m2 = re.search(pat_num, txt)
talla_std = None
talla_num = None
if m1:
val = m1.group(1).lower()
talla_std = "TU" if val in {"única", "unica", "u"} else val.upper()
if m2:
talla_num = float(m2.group(1)) if m2.group(1) is not None else None
return pd.Series({"talla_std": talla_std, "talla_num": talla_num})
df_productos[["talla_std", "talla_num"]] = df_productos.apply(extraer_talla, axis=1)
df_productos
producto | precio | talla_std | talla_num | |
---|---|---|---|---|
0 | Camisa talla M | $1.234,50 | M | NaN |
1 | Pantalón-XL | USD 45 | XL | NaN |
2 | Zapato, Talla: 42 | 30,00 € | NaN | 42.0 |
3 | Blusa s | 25.000 | S | NaN |
4 | Medias 10-12 | $ 0 | NaN | 10.0 |
5 | Polo Talla l | S/. 120.90 | L | NaN |
6 | Vestido - 36 | COP 9.990 | NaN | 36.0 |
7 | Sombrero (talla Única) | AR$ 2.550,00 | TU | NaN |
11) Extraer de precios
Convierte la columna precio
a numérico (precio_num
) independiente del formato ($1.234,50
, USD 45
, 30,00 €
, COP 9.990
, etc.).
# Copie su código aca
def parse_precio(s):
if pd.isna(s):
return np.nan
s = str(s).replace("\xa0", " ").strip()
s2 = re.sub(r"[^0-9,.-]", "", s)
if "," in s2 and "." in s2:
s2 = s2.replace(".", "").replace(",", ".")
elif "," in s2 and "." not in s2:
s2 = s2.replace(",", ".")
s2 = s2.replace(",", "")
try:
return float(s2)
except Exception:
return np.nan
df_productos["precio_num"] = df_productos["precio"].apply(parse_precio)
df_productos
producto | precio | talla_std | talla_num | precio_num | |
---|---|---|---|---|---|
0 | Camisa talla M | $1.234,50 | M | NaN | 1234.50 |
1 | Pantalón-XL | USD 45 | XL | NaN | 45.00 |
2 | Zapato, Talla: 42 | 30,00 € | NaN | 42.0 | 30.00 |
3 | Blusa s | 25.000 | S | NaN | 25.00 |
4 | Medias 10-12 | $ 0 | NaN | 10.0 | 0.00 |
5 | Polo Talla l | S/. 120.90 | L | NaN | NaN |
6 | Vestido - 36 | COP 9.990 | NaN | 36.0 | 9.99 |
7 | Sombrero (talla Única) | AR$ 2.550,00 | TU | NaN | 2550.00 |
12) Filtrado por texto
Filtra el DataFrame df_productos
para mostrar solo filas cuyo producto
contenga camisa o pantalón, ignorando acentos y mayúsculas/minúsculas.
# Copie su código aca
mask = (
df_productos["producto"]
.str.lower()
.apply(quitar_acentos)
.str.contains("camisa|pantalon")
)
df_productos[mask]
producto | precio | talla_std | talla_num | precio_num | |
---|---|---|---|---|---|
0 | Camisa talla M | $1.234,50 | M | NaN | 1234.5 |
1 | Pantalón-XL | USD 45 | XL | NaN | 45.0 |
13) Limpieza de nombres propios
En el DataFrame df_personas
, crea nombre_limpio
en df_personas
:
- Quita paréntesis y su contenido.
- Reemplaza guiones por espacio y elimina espacios extra.
- Aplica title case y conserva conectores (
de
,del
,la
,y
) en minúscula.
# Copie su código aca
def limpiar_nombre(s):
s = re.sub(r"\([^)]*\)", " ", str(s))
s = s.replace("-", " ")
s = re.sub(r"\s+", " ", s).strip()
s = s.title()
for w in [" De ", " Del ", " La ", " Y "]:
s = s.replace(w, w.lower())
return s
df_personas["nombre_limpio"] = df_personas["nombre"].apply(limpiar_nombre)
df_personas["nombre_limpio"]
0 Ana María Lopez
1 Juan Perez
2 Ñandú Gómez
3 Miguel
4 Mónica de la Cruz
5 Luis Delgado
Name: nombre_limpio, dtype: object
14) Normaliza respuestas categóricas
En el DataFrame df_personas
, convierte categoria
en booleano categoria_bool
donde cualquier variante de sí (con/ sin tilde) sea True
; el resto False
.
# Copie su código aca
def a_bool_si(s):
if s is None:
return False
val = str(s).strip().casefold()
return val in {"si", "sí", "si.", "sí."}
df_personas["categoria_bool"] = df_personas["categoria"].apply(a_bool_si)
df_personas
nombre | categoria | direccion | id_raw | nombre_limpio | categoria_bool | |
---|---|---|---|---|---|---|
0 | ana María LOPEZ | Sí | Calle 45 # 12-34, Bogotá | abc-0001 | Ana María Lopez | True |
1 | Juan perez | si | Av. Siempre Viva 742 - Lima | abc 001 | Juan Perez | True |
2 | Ñandú Gómez | SI | Cll. 10 No. 5-20 Medellín | ABC0002 | Ñandú Gómez | True |
3 | Miguel (Soporte) | No | Cra 7a # 45-60, Bogotá | Abc_003 | Miguel | False |
4 | MÓNICA de la CRUZ | — | Av. 9 #12-34 Cali | abc-00004 | Mónica de la Cruz | False |
5 | luis-delgado | None | Av. Insurgentes Sur 1234, CDMX | ABC-0005 | Luis Delgado | False |
15) Ciudad desde la dirección
En el DataFrame df_personas
, Extrae la ciudad en una columna ciudad
# Copie su código aca
def extraer_ciudad(s):
s = str(s)
m = re.search(r",\s*([^,]+)$", s)
if m:
return m.group(1).strip()
return s.strip().split()[-1]
df_personas["ciudad"] = df_personas["direccion"].apply(extraer_ciudad)
df_personas
nombre | categoria | direccion | id_raw | nombre_limpio | categoria_bool | ciudad | |
---|---|---|---|---|---|---|---|
0 | ana María LOPEZ | Sí | Calle 45 # 12-34, Bogotá | abc-0001 | Ana María Lopez | True | Bogotá |
1 | Juan perez | si | Av. Siempre Viva 742 - Lima | abc 001 | Juan Perez | True | Lima |
2 | Ñandú Gómez | SI | Cll. 10 No. 5-20 Medellín | ABC0002 | Ñandú Gómez | True | Medellín |
3 | Miguel (Soporte) | No | Cra 7a # 45-60, Bogotá | Abc_003 | Miguel | False | Bogotá |
4 | MÓNICA de la CRUZ | — | Av. 9 #12-34 Cali | abc-00004 | Mónica de la Cruz | False | Cali |
5 | luis-delgado | None | Av. Insurgentes Sur 1234, CDMX | ABC-0005 | Luis Delgado | False | CDMX |
16) Normaliza y valida IDs
En el DataFrame df_personas
, a partir de id_raw
, crea la columna id_norm
con formato ABC-0001
(tres letras + guion + 4 dígitos) y id_valido
(True/False).
# Copie su código aca
def normalizar_id(s):
m = re.search(r"([A-Za-z]{3})\s*[-_ ]?\s*(\d+)", str(s))
if not m:
return None
pref = m.group(1).upper()
num = int(m.group(2))
return f"{pref}-{num:04d}"
df_personas["id_norm"] = df_personas["id_raw"].apply(normalizar_id)
df_personas["id_valido"] = df_personas["id_norm"].str.match(r"^[A-Z]{3}-\d{4}$")
df_personas[["id_raw", "id_norm", "id_valido"]]
id_raw | id_norm | id_valido | |
---|---|---|---|
0 | abc-0001 | ABC-0001 | True |
1 | abc 001 | ABC-0001 | True |
2 | ABC0002 | ABC-0002 | True |
3 | Abc_003 | ABC-0003 | True |
4 | abc-00004 | ABC-0004 | True |
5 | ABC-0005 | ABC-0005 | True |
17) Mini pipeline de limpieza general
Escribe una función limpia_basica(s)
que: ponga el texto en minuscula, quite acentos, URLs, menciones y hashtags, puntuación, dígitos y colapse espacios. Aplícala a df_texto['texto']
y guarda en texto_limpio_final
.
# Copie su código aca
def limpia_basica(s: str) -> str:
"""Limpieza básica de texto.
Convierte a minúsculas, quita acentos, URLs, menciones y hashtags,
puntuación, dígitos y quita espacios.
Arguments:
s (str): Texto a limpiar.
Returns:
str: Texto limpio.
Ejemplo:
>>> limpia_basica(' ¡Hola Mundo!!! Visita https://miweb.com #DataScience :) ')
'hola mundo visita'
"""
if s is None:
return s
s = str(s).lower()
MAP_VOCALES = {
"á": "a",
"é": "e",
"í": "i",
"ó": "o",
"ú": "u",
"ü": "u",
}
translate = str.maketrans(MAP_VOCALES)
s = s.translate(translate)
s = re.sub(r"https?://\S+", " ", s)
s = re.sub(r"[@#]\w+", " ", s)
s = re.sub(r"[^a-z\sñ]", " ", s)
s = re.sub(r"\s+", " ", s).strip()
return s
df_texto["texto_limpio_final"] = df_texto["texto"].apply(limpia_basica)
df_texto[["texto", "texto_limpio_final"]]
texto | texto_limpio_final | |
---|---|---|
0 | ¡Hola Mundo!!! Esto es un EJEMPLO: visita https://miweb.com #DataScience :) | hola mundo esto es un ejemplo visita |
1 | Pandas > numpy? 🤔 Email: persona@example.com | pandas numpy email persona com |
2 | Me gusta el café colombiano; es buenísimo!!! #Café #Colombia @juan | me gusta el cafe colombiano es buenisimo |
3 | Oferta!!! 3x2 en jabón líquido 500ml - CÓDIGO: A-123 | oferta x en jabon liquido ml codigo a |
4 | TABLAS\t, \n espacios y saltos de línea.\r\n | tablas espacios y saltos de linea |
5 | Teléfono: (57) 300-123-45-67; Whatsapp +57 300 222 33 44 | telefono whatsapp |
6 | Dirección: Cll 10 # 5-20; Medellín. Barrio: La América | direccion cll medellin barrio la america |
7 | Emoji test: 😀🙌🏽🏳️🌈, símbolos ©®™ y otros… | emoji test simbolos y otros |
18)¿Qué tanto cambió el texto?
En el dataframe df_texto
,
Cual es la diferencia entre la longitud original (len_raw
) y la longitud tras la limpieza (len_clean
)?
# Copie su código aca
df_texto["len_raw"] = df_texto["texto"].str.len()
df_texto["len_clean"] = df_texto["texto_limpio_final"].str.len()
df_texto[["texto", "texto_limpio_final", "len_raw", "len_clean"]]
texto | texto_limpio_final | len_raw | len_clean | |
---|---|---|---|---|
0 | ¡Hola Mundo!!! Esto es un EJEMPLO: visita https://miweb.com #DataScience :) | hola mundo esto es un ejemplo visita | 84 | 36 |
1 | Pandas > numpy? 🤔 Email: persona@example.com | pandas numpy email persona com | 48 | 30 |
2 | Me gusta el café colombiano; es buenísimo!!! #Café #Colombia @juan | me gusta el cafe colombiano es buenisimo | 66 | 40 |
3 | Oferta!!! 3x2 en jabón líquido 500ml - CÓDIGO: A-123 | oferta x en jabon liquido ml codigo a | 52 | 37 |
4 | TABLAS\t, \n espacios y saltos de línea.\r\n | tablas espacios y saltos de linea | 48 | 33 |
5 | Teléfono: (57) 300-123-45-67; Whatsapp +57 300 222 33 44 | telefono whatsapp | 56 | 17 |
6 | Dirección: Cll 10 # 5-20; Medellín. Barrio: La América | direccion cll medellin barrio la america | 54 | 40 |
7 | Emoji test: 😀🙌🏽🏳️🌈, símbolos ©®™ y otros… | emoji test simbolos y otros | 42 | 27 |
Guardar de resultados
Guarda el dataframe df_texto en parquet con df_texto.to_parquet('archivo.parquet', index=False)
.
Phd. Jose R. Zapata