Practica de limpieza de texto con pandas - Solución

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:

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 :)
1Pandas > numpy? 🤔 Email: persona@example.com
2Me gusta el café colombiano; es buenísimo!!! #Café #Colombia @juan
3Oferta!!! 3x2 en jabón líquido 500ml - CÓDIGO: A-123
4TABLAS\t, \n espacios y saltos de línea.\r\n
5Teléfono: (57) 300-123-45-67; Whatsapp +57 300 222 33 44
6Dirección: Cll 10 # 5-20; Medellín. Barrio: La América
7Emoji test: 😀🙌🏽🏳️‍🌈, símbolos ©®™ y otros…
df_tweets:

tweet
0RT @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 😊
2Probando 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:

productoprecio
0Camisa talla M$1.234,50
1Pantalón-XLUSD 45
2Zapato, Talla: 4230,00 €
3Blusa s25.000
4Medias 10-12$ 0
5Polo Talla lS/. 120.90
6Vestido - 36COP 9.990
7Sombrero (talla Única)AR$ 2.550,00
df_personas:

nombrecategoriadireccionid_raw
0ana María LOPEZCalle 45 # 12-34, Bogotáabc-0001
1Juan perezsiAv. Siempre Viva 742 - Limaabc 001
2Ñandú GómezSICll. 10 No. 5-20 MedellínABC0002
3Miguel (Soporte)NoCra 7a # 45-60, BogotáAbc_003
4MÓNICA de la CRUZAv. 9 #12-34 Caliabc-00004
5luis-delgadoNoneAv. Insurgentes Sur 1234, CDMXABC-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"]]

textotexto_min
0¡Hola Mundo!!! Esto es un EJEMPLO: visita https://miweb.com #DataScience :)¡hola mundo!!! esto es un ejemplo: visita https://miweb.com #datascience :)
1Pandas > numpy? 🤔 Email: persona@example.compandas > numpy? 🤔 email: persona@example.com
2Me gusta el café colombiano; es buenísimo!!! #Café #Colombia @juanme gusta el café colombiano; es buenísimo!!! #café #colombia @juan
3Oferta!!! 3x2 en jabón líquido 500ml - CÓDIGO: A-123oferta!!! 3x2 en jabón líquido 500ml - código: a-123
4TABLAS\t, \n espacios y saltos de línea.\r\ntablas\t, \n espacios y saltos de línea.\r\n
5Teléfono: (57) 300-123-45-67; Whatsapp +57 300 222 33 44teléfono: (57) 300-123-45-67; whatsapp +57 300 222 33 44
6Dirección: Cll 10 # 5-20; Medellín. Barrio: La Américadirección: cll 10 # 5-20; medellín. barrio: la américa
7Emoji 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_espaciostexto_min
0¡hola mundo!!! esto es un ejemplo: visita https://miweb.com #datascience :)¡hola mundo!!! esto es un ejemplo: visita https://miweb.com #datascience :)
1pandas > numpy? 🤔 email: persona@example.compandas > numpy? 🤔 email: persona@example.com
2me gusta el café colombiano; es buenísimo!!! #café #colombia @juanme gusta el café colombiano; es buenísimo!!! #café #colombia @juan
3oferta!!! 3x2 en jabón líquido 500ml - código: a-123oferta!!! 3x2 en jabón líquido 500ml - código: a-123
4tablas\t, \n espacios y saltos de línea.tablas\t, \n espacios y saltos de línea.\r\n
5teléfono: (57) 300-123-45-67; whatsapp +57 300 222 33 44teléfono: (57) 300-123-45-67; whatsapp +57 300 222 33 44
6dirección: cll 10 # 5-20; medellín. barrio: la américadirección: cll 10 # 5-20; medellín. barrio: la américa
7emoji 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"]]

textoemailurls
0¡Hola Mundo!!! Esto es un EJEMPLO: visita https://miweb.com #DataScience :)[][https://miweb.com]
1Pandas > numpy? 🤔 Email: persona@example.com[persona@example.com][]
2Me gusta el café colombiano; es buenísimo!!! #Café #Colombia @juan[][]
3Oferta!!! 3x2 en jabón líquido 500ml - CÓDIGO: A-123[][]
4TABLAS\t, \n espacios y saltos de línea.\r\n[][]
5Teléfono: (57) 300-123-45-67; Whatsapp +57 300 222 33 44[][]
6Dirección: Cll 10 # 5-20; Medellín. Barrio: La América[][]
7Emoji 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

tweethashtagsmentionsurls
0RT @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]
2Probando 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

tweethashtagsmentionsurlses_rttweet_sin_rt
0RT @maria: Nuevo post en el blog -> http://blog.com/post?id=45 #nlp #python[#nlp, #python][@maria][http://blog.com/post?id=45]TrueNuevo 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 😊
2Probando cosas en Jupyter... sin link ni hashtag[][][]FalseProbando 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"]]

tweettweet_limpio
0RT @maria: Nuevo post en el blog -> http://blog.com/post?id=45 #nlp #pythonnuevo post en el blog
1¡Me encanta Pandas! #datos #Python https://example.org @data_science 😊me encanta pandas
2Probando cosas en Jupyter... sin link ni hashtagprobando cosas en jupyter sin link ni hashtag
3@juan y @ana lanzaron curso de NLP en https://cursos.ai #nlp #mly lanzaron curso de nlp en
4¿Pandas o Polars? debátanlo aquí 👉 https://foro.com #datapandas o polars debátanlo aquí

10) Talla en df_productos

Extrae y estandariza la talla:

  • Letras: XS, S, M, L, XL, XXL, o TU (talla única).
  • Números: captura como talla_num. Crea la columnatalla_std (letras) y talla_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

productopreciotalla_stdtalla_num
0Camisa talla M$1.234,50MNaN
1Pantalón-XLUSD 45XLNaN
2Zapato, Talla: 4230,00 €NaN42.0
3Blusa s25.000SNaN
4Medias 10-12$ 0NaN10.0
5Polo Talla lS/. 120.90LNaN
6Vestido - 36COP 9.990NaN36.0
7Sombrero (talla Única)AR$ 2.550,00TUNaN

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

productopreciotalla_stdtalla_numprecio_num
0Camisa talla M$1.234,50MNaN1234.50
1Pantalón-XLUSD 45XLNaN45.00
2Zapato, Talla: 4230,00 €NaN42.030.00
3Blusa s25.000SNaN25.00
4Medias 10-12$ 0NaN10.00.00
5Polo Talla lS/. 120.90LNaNNaN
6Vestido - 36COP 9.990NaN36.09.99
7Sombrero (talla Única)AR$ 2.550,00TUNaN2550.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]

productopreciotalla_stdtalla_numprecio_num
0Camisa talla M$1.234,50MNaN1234.5
1Pantalón-XLUSD 45XLNaN45.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 (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

nombrecategoriadireccionid_rawnombre_limpiocategoria_bool
0ana María LOPEZCalle 45 # 12-34, Bogotáabc-0001Ana María LopezTrue
1Juan perezsiAv. Siempre Viva 742 - Limaabc 001Juan PerezTrue
2Ñandú GómezSICll. 10 No. 5-20 MedellínABC0002Ñandú GómezTrue
3Miguel (Soporte)NoCra 7a # 45-60, BogotáAbc_003MiguelFalse
4MÓNICA de la CRUZAv. 9 #12-34 Caliabc-00004Mónica de la CruzFalse
5luis-delgadoNoneAv. Insurgentes Sur 1234, CDMXABC-0005Luis DelgadoFalse

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

nombrecategoriadireccionid_rawnombre_limpiocategoria_boolciudad
0ana María LOPEZCalle 45 # 12-34, Bogotáabc-0001Ana María LopezTrueBogotá
1Juan perezsiAv. Siempre Viva 742 - Limaabc 001Juan PerezTrueLima
2Ñandú GómezSICll. 10 No. 5-20 MedellínABC0002Ñandú GómezTrueMedellín
3Miguel (Soporte)NoCra 7a # 45-60, BogotáAbc_003MiguelFalseBogotá
4MÓNICA de la CRUZAv. 9 #12-34 Caliabc-00004Mónica de la CruzFalseCali
5luis-delgadoNoneAv. Insurgentes Sur 1234, CDMXABC-0005Luis DelgadoFalseCDMX

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_rawid_normid_valido
0abc-0001ABC-0001True
1abc 001ABC-0001True
2ABC0002ABC-0002True
3Abc_003ABC-0003True
4abc-00004ABC-0004True
5ABC-0005ABC-0005True

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"]]

textotexto_limpio_final
0¡Hola Mundo!!! Esto es un EJEMPLO: visita https://miweb.com #DataScience :)hola mundo esto es un ejemplo visita
1Pandas > numpy? 🤔 Email: persona@example.compandas numpy email persona com
2Me gusta el café colombiano; es buenísimo!!! #Café #Colombia @juanme gusta el cafe colombiano es buenisimo
3Oferta!!! 3x2 en jabón líquido 500ml - CÓDIGO: A-123oferta x en jabon liquido ml codigo a
4TABLAS\t, \n espacios y saltos de línea.\r\ntablas espacios y saltos de linea
5Teléfono: (57) 300-123-45-67; Whatsapp +57 300 222 33 44telefono whatsapp
6Dirección: Cll 10 # 5-20; Medellín. Barrio: La Américadireccion cll medellin barrio la america
7Emoji 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"]]

textotexto_limpio_finallen_rawlen_clean
0¡Hola Mundo!!! Esto es un EJEMPLO: visita https://miweb.com #DataScience :)hola mundo esto es un ejemplo visita8436
1Pandas > numpy? 🤔 Email: persona@example.compandas numpy email persona com4830
2Me gusta el café colombiano; es buenísimo!!! #Café #Colombia @juanme gusta el cafe colombiano es buenisimo6640
3Oferta!!! 3x2 en jabón líquido 500ml - CÓDIGO: A-123oferta x en jabon liquido ml codigo a5237
4TABLAS\t, \n espacios y saltos de línea.\r\ntablas espacios y saltos de linea4833
5Teléfono: (57) 300-123-45-67; Whatsapp +57 300 222 33 44telefono whatsapp5617
6Dirección: Cll 10 # 5-20; Medellín. Barrio: La Américadireccion cll medellin barrio la america5440
7Emoji test: 😀🙌🏽🏳️‍🌈, símbolos ©®™ y otros…emoji test simbolos y otros4227

Guardar de resultados

Guarda el dataframe df_texto en parquet con df_texto.to_parquet('archivo.parquet', index=False).

Phd. Jose R. Zapata