Mostrando entradas con la etiqueta Pandas. Mostrar todas las entradas
Mostrando entradas con la etiqueta Pandas. Mostrar todas las entradas

martes, 22 de septiembre de 2026

LENGUAJES. Python

DataFrame (Pandas) (II)

Colecciones de datos

En la entrada anterior creamos un DataFrame partiendo de un diccionario integrado en el propio script. Esta fórmula es adecuada desde el punto de vista didáctico, ya que ayuda a comprender la conexión entre las estructuras nativas de Python y las de Pandas, así como a experimentar con sus primeras manipulaciones. Sin embargo no refleja la dinámica de trabajo habitual.

En esta segunda entrada vamos a proceder del modo en que es más común trabajar, ya que en los proyectos reales, los datos residen en fuentes externas, como archivos CSV u hojas de cálculo. Mediante funciones de lectura se cargan los datos en memoria en forma de DataFrame, lo que permite separar el almacenamiento de los datos y su procesamiento. Esto mejora el flujo de trabajo y permite aprovechar al máximo la potencia analítica de Pandas.

Para el caso vamos a utilizar el archivo Avandono_escolar.csv, obtenido de la página web del MEFPD, sobre el cual realizaremos diferentes manipulaciones mediante Pandas, una vez convertido en un DataFrame de trabajo.

Empezaremos por acceder al archivo .cvs (1) para cargarlo en nuestro script, siguiendo lo que te propongo a continuación.


import pandas as pd

# 1. Definir la ruta del archivo CSV
ruta_archivo = r'directorio_de_archov.csv' #Sustituye por tu ruta

# 2. Leer el archivo CSV y cargarlo en un DataFrame
df = pd.read_csv(ruta_archivo, sep=';', decimal=',')


Lo primero que deberás hacer es sustituir directorio desde el que se cargará el archivo .csv. Después debes prestar atención a la instrucción mediante la que se carga ese archivo con DataFrame df = pd.read_csv(ruta_archivo, sep=';', decimal=','), ya que contiene dos especificaciones que son necesarias teniendo en cuenta las características de nuestro archivo de origen, dado que los campos están separados por ; y se utiliza la , como separador de la parte decimal del número. Normalmente separamos los campos por , y en los números empleamos . como separador de decimales, por ello la instrucción normal de carga es más simple df = pd.read_csv(ruta_archivo), pero es este caso fallaría (2)

Ahora podremos, por ejemplo, visualizar los primeros y los últimos registros de nuestro DataFrame, lo que nos sirve para confirmar que la carga ha resultado exitosa y ajustada a la estructura del DataFrame. Para ello añadiremos el siguiente código al script.



# 3. Mostrar las primeras cinco filas del DataFrame
print("Primeras 5 filas y últimas 5 filas del DataFrame:")
print(df)

print("\nTipos de datos asignados por Pandas:")
print(df.dtypes)


Si sólo deseamos ver las primeras filas usamos la instrucción print(df.head()) y si añadimos una cantidad dentro de la función print(df.head(10)) esteremos explicitando cuantas filas queremos ver (3)

Visualizar el DataFrame puede ser interesante para conocer su contenido y estructura, pero muy posiblemente ambos nos sean ya conocidos (4). Lo que realmente hace útil a Pandas son las posibilidades que nos ofrece de manipular el contenido del DataFrame, utilizando filtros para seleccionar parte del mismo, realizando cálculos sobre sus datos o generando nuevas columnas a partir de las existentes. Veamos en el código que sigue algunas de estas opciones.



#Ejemplos de trabajos sobre el DataFrame

# 1. Filtros. Aplicar un filtro con dos condiciones combinadas con '&'. Cada condición DEBE ir encerrada entre paréntesis (...)
sub_df = df[(df['Territorio'] == 'TOTAL') & (df['Sexo'] == 'Ambos')] #Establecemos los filtros
sub_df = sub_df.sort_values(by='periodo', ascending=True) # Reordenamos el DataFrame en orden ascendente
n = len(sub_df) # Obtenemos el número de registros (filas) del DataFrame para su posterior visualización

# 2. Cálculos. Calcular la diferencia interanual sobre la columna del valor ('Total %'). diff() resta a la fila actual el valor de la fila anterior
sub_df['dif'] = sub_df['Total %'].diff().round(2)

# 3. Hacer una copia explícita (Buena práctica recomendada en Pandas). Así evitamos avisos tipo 'SettingWithCopyWarning' si se modifica el sub_df
sub_df = sub_df.copy()

# 4. Visualizar el resultado
print(f"Filas resultantes en el sub-dataframe: {len(sub_df)}")
print(sub_df.head(n))


Finalmente, nos puede interesar guardar un archivo CSV con el trabajo realizado, creando un nuevo archivo (sin modificar el original) con los resultados de nuestro trabajo. Para ello añadiremos al script el código que sigue (4).



# Guardamos el sub-DataFrame como csv

# 1. Definir la ruta completa del nuevo archivo CSV (en la misma carpeta)
ruta_salida = r'C:\Mi_Ruta\Nuevo_Archivo.csv'

# 2. Guardar el sub-DataFrame
sub_df.to_csv(ruta_salida, sep=';', decimal=',', index=False, encoding='utf-8-sig')

print(f"El archivo se ha guardado correctamente en: {ruta_salida}")


Mediante Pandas es posible realizar manipulaciones de un DataFrame mucho más complejas que las explicadas en estos script, lo que permite realizar estudios diversos sobre los datos que contiene. También podemos construir nuevos DataFrames a partir de otros combinando sus datos, siempre que sus estructuras y contenidos lo permitan. Todo ello implica concretar un plan de trabajo, lo que es más propio de un proyecto de análisis que de una entrada como la actual, pensada para explicar la naturaleza de la colección de datos llamada DataFrame.

Otro campo de interés en el trabajo con estas colecciones de datos usando Pandas es la ejecutar funciones estadísticas sobre sus datos, pero esto lo dejaremos para tratarlo en otra sección. De momento esta la damos por cerrarda aquí.

NOTAS
1 Para lo que previamente habremos descargado dicho archivo desde el enlace que ofrezco en el párrafo anterior o, si se prefiere, desde el enlace a la web del MEFPD. En ambos casos deberemos haber guardado el archivo con .csv en un determinado directorio. Esa ubicación será la que sustituya a que empleamos en el script a modo de ejemplo.
2 Fallaría no en el momento de la carga, sino al trabajar con el DataFrame. Por eso doy toda esta explicación para evitarte tener que investigar sobre esta cuestión. En todo caso, es conveniente que conozcas de antemano cómo está construído tu archivo .csv para que ajuste la instrucción de carga.
3 En este caso veremos 10 filas o registros. Para ver sólo las últimas filas empleamos print(df.tail())
4 Ya sabes que puedes visualizar un archivo csv desde Bloc de notas o desde Calc, lo que te recomiendo como alternativa a visualizarlo en el CMD mediante Python.
4 Como supondrás, deberás modificar la instrucción ruta_salida = r'C:\Mi_Ruta\Nuevo_Archivo.csv' sustituyendo la ruta por la que corresponda en tu caso. Te sugiero que sea la misma que empleaste para guardar el archivo csv original, dando al nuevo un nombre que ilustre su contenido. En este, por ejemplo, caso algo así como Datos_Totales.csv.

sábado, 19 de septiembre de 2026

LENGUAJES. Python

DataFrame (Pandas) (I)

Colecciones de datos

Un DataFrame es una estructura de datos bidimensional (organizada en filas y columnas) similar a una tabla de base de datos, una hoja de cálculo o una matriz. Es el objeto fundamental para la manipulación y análisis de datos en programación, especialmente en bibliotecas como Pandas (Python).

Los datos de cada columna pueden ser de distinto tipo (pero los de la misma columna han de ser del mismo), lo que le otorga gran flexibilidad, y dispone de índices por filas y por columnas, lo que le proporciona un funcionamiento eficiente en cuanto al acceso, el filtrado de datos y su alineación. Además es posible realizar operaciones vectorizadas, lo que nos permite aplicar transformaciones, filtros y cálculos matemáticos a columnas completas de forma eficiente sin necesidad de recurrir a bucles explícitos. En todas estas características se hace notar la influencia de los array de NumPy, base sobre la que se diseñó la librería Pandas y el concepto de DataFrame en Python (1).

La creación de un DataFrame (2) puede partir de la transformación de colecciones de datos nativas de Python como, por ejemplo, a partir de un diccionario cuyos valores son listas de datos:



import pandas as pd

# Creacción del diccionario
datos = {
    'Nombre': ['Ana', 'Carlos', 'Elena', 'David'],
    'Edad': [4, 6, 8, 10],
    'Centro': ['CP San Tirso', 'Col. Donato Barato', 'CP Sevilla', 'CP Luna LLena'],
    'Madre': ['María', 'Rosana', 'Carlota', 'Venancia']
}

# Conversión de diccionario a DataFrame

df = pd.DataFrame(datos)


Primero importamos la biblioteca Pandas con el alias que es costumbre import pandas as pd y después de crear el diccionario datos lo convertimos en una DataFrame df = pd.DataFrame(datos). A partir de aquí podemos realizar diferentes operaciones con el DataFrame creado haciendo uso de instrucciones Python y de otras específicas de Pandas.

Por ejemplo, podemos mostar el contenido del DataFrame en el CMD mediante print(df), obteniendo...

También podemos incrementar el contenido en ambas estructuras (diccionario y DataFrame), comprobando de paso las diferencias en el manejo de ambas estructuras.



# ==================================================
# PROCEDIMIENTO A: Añadir "registro" al DICCIONARIO
# ===================================================

# Al tratarse de un diccionario de listas, hay que añadir cada elemento clave a clave en su lista correspondiente.

datos['Nombre'].append('Lucas')
datos['Edad'].append(5)
datos['Centro'].append('CP San Tirso')
datos['Madre'].append('Laura')

print("Diccionario actualizado:")
print(datos)
print("--------------------------")


# =================================================
# PROCEDIMIENTO B: Añadir "registro" al DATAFRAME
# =================================================

# Los DataFrames de Pandas son estructuras inmutables en tamaño sobre la memoria de NumPy.
# A partir de Pandas 2.0+, la forma de añadir una fila es crear un mini-DataFrame y concatenarlo mediante pd.concat().

nuevo_df = pd.DataFrame([{
    'Nombre': 'Lucas',
    'Edad': 5,
    'Centro': 'CP San Tirso',
    'Madre': 'Laura'
}])

df = pd.concat([df, nuevo_df], ignore_index=True)

print("DataFrame actualizado:")
print(df)
print("--------------------------")

También podemos ejecutar diferentes modificaciones en el mismo, como, por ejemplo...



# 1. Selección de columnas

nombres = df['Nombre']
centros_y_madres = df[['Centro', 'Madre']]

# 2. Filtrado por condición (operación vectorizada)

# Alumnos mayores de 6 años (Elena y David)
mayores_de_6 = df[df['Edad'] > 6]

# Alumnos del colegio "CP San Tirso"
san_tirso = df[df['Centro'] == 'CP San Tirso']

# 3. Creación y modificación de columnas (operación vectorizada)

# Calcular el año de nacimiento estimado
df['Año_Nacimiento'] = 2026 - df['Edad']

# Indicador booleano para etapa infantil (menores o iguales a 5 años)
df['Es_Infantil'] = df['Edad'] <= 5

# 4. Agregación y resumen de datos

edad_promedio = df['Edad'].mean()


... que realizan las modificaciones y cálculos explicados en los comentarios, pero que no se muestran en pantalla al faltar las instrucciones print() que dejo de tu mano. Una vez realizadas estas modificaciones, lo lógico es guardar el DataFrame modificado para poder utilizarlo más adelante. Para ello es suficiente con implementar al final del script el código siguiente (3)



# Guardar el DataFrame en la ruta especificada

df.to_csv("D:/datos/mi_df.csv", index=False, encoding='utf-8')

print("DataFrame guardado correctamente en D:/datos/mi_df.csv")
print("--------------------------")


NOTAS
1 Pandas es una librería diseñada en 2008 por Wes McKinney, quien construyó la arquitectura del DataFrame para que usara las ventajas de bajo nivel de NumPy.
2 Lo frecuente es trabajar con archivos ya creados (tablas en hojas de datos o archivos CSV), pero en esta entrada nos limitaremos a la transformación de colecciones de datos insertas en el script.
3 La ruta donde se guardar realmente queda de tu mano, por lo que deberás modificar la instrucción "D:/datos/mi_df.csv" de df.to_csv("D:/datos/mi_df.csv", index=False, encoding='utf-8'). Observa que el DataFrame se guarda como archivo csv. Finalmente, mediante index=False evitamos que se guarde la columna del índice numérico (0, 1, 2, 3)

viernes, 15 de mayo de 2026

DATOS. Tratamiento de datos

Limpieza de datos (II)

Normalización de datos

Cuando en la tabla-formulario de un documento de texto cabe la posibilidad de que los datos se expresen de diferentes maneras, existe una elevada probabilidad de que así sea. Aunque predomine una determinada forma, vamos a decir que canónica, es altamente probable que también se empleen otras formas que no lo son tanto. Un buen ejemplo de ello lo tienes en la tabla que sigue.

Como puedes ver en este ejemplo ficticio, que no lo es en este aspecto de la variabilidad de expresión de la fecha, junto a la forma canónica (círculo naranja), nos encontramos con otras forma diferentes (por ejemplo, pero no sólo, la marcada con el círculo verde). Aunque para un análisis visual de los datos esto no supone ningún problema (salvo en casos muy concretos), para el tratamiento automatizado de los datos se convierte en un serio problema: esas fecha no-canónicas no son reconocidas como tales fechas. Para evitar la pérdida de datos que esto implica, es necesario convertir estos datos al formato fecha. Podemos hacerlo manualmente, con ayuda de las funciones de formato de Calc (o Excel), aunque esto nos llevará tiempo. También contamos con la opción de automatizar el reajuste de formato mediante un script Python como el que sigue.



# 0. Bibliotecas y módulos ---

import pandas as pd
import re
from datetime import datetime

# 1. Funciones ---

# 1.a Función secundaria. Transforma string en fechas ---

def normalizar_fecha(texto):

    if pd.isna(texto) or str(texto).strip() == "":
        return None
    
    dato = str(texto).lower().strip()
    
    meses_map = {
        'enero': 1, 'febrero': 2, 'marzo': 3, 'abril': 4, 'mayo': 5, 'junio': 6,
        'julio': 7, 'agosto': 8, 'septiembre': 9, 'octubre': 10, 'noviembre': 11, 'diciembre': 12
    }

    try:
        return pd.to_datetime(dato, dayfirst=True).date()     # Intento 1: Formato numérico completo (ej. 2/02/2026)
    except:
        pass
    anio_detectado = re.search(r'(\d{4})', dato)               # Intento 2: Formato parcial (ej. enero 2014)
    if anio_detectado:
        anio = int(anio_detectado.group(1))
        for mes_nombre, mes_numero in meses_map.items():
            if mes_nombre in dato:
                return datetime(anio, mes_numero, 1).date()      # Retornamos siempre el día 1 del mes encontrado
    return None                                                  # Si llega aquí, no se pudo convertir y devolvemos None

# 1.b Función principal ---

def procesar_limpieza(ruta_entrada, columna_objetivo, ruta_salida):
    
    try:
        print(f"Cargando archivo: {ruta_entrada}...")
        df = pd.read_csv(ruta_entrada, sep=';', encoding='utf-8')
        df['fecha_corregida'] = df[columna_objetivo].apply(normalizar_fecha)                # Aplicamos la normalización (funcion 1a)
        df['fecha_corregida'] = pd.to_datetime(df['fecha_corregida'], errors='coerce')      # Convertimos a datetime Pandas (AAAA-MM-DD)
        df.to_csv(ruta_salida, sep=';', index=False, encoding='utf-8')                      # Exportamos el resultado (csv)
        print(f"Éxito: Archivo '{ruta_salida}' generado con fechas estandarizadas.")
        
    except Exception as e:
        print(f"Error durante el proceso de código: {e}")

# 2. Script principal (Llamada a la función principal) ---

if __name__ == "__main__":

    archivo_origen = r''            # Aquí la ruta del archivo csv de trabajo
    columna_fechas = 'fecha'        # Aquí el nombre de la columna a procesar
    archivo_destino = r''      		# Aquí el nombre del csv resultante
    
    procesar_limpieza(archivo_origen, columna_fechas, archivo_destino) # Llamada a la función


La estructura del script es relativamente simple: dos funciones y un script. El script llama a la función principal pasando parámetros (procesar_limpieza(archivo_origen, columna_fechas, archivo_destino)) que el profesional asigna a variables (vg. archivo_origen = r'') y la función principal hace uso de la secundaria para tratar el contenido textual del campo y devolver un formato fecha del módulo datetime en formato Pandas (AAA-MM-DD).

La función principal es sumamente importante: (1) Carga el csv de trabajo, (2) Llama a la función secundaria para normalizar el formato (3) convierte el dato devuelto por esa función al formato datetime de Pandas y (4) genera el scv de salida.

  1. df = pd.read_csv(ruta_entrada, sep=';', encoding='utf-8')
  2. df['fecha_corregida'] = df[columna_objetivo].apply(normalizar_fecha)
  3. df['fecha_corregida'] = pd.to_datetime(df['fecha_corregida'], errors='coerce')
  4. df.to_csv(ruta_salida, sep=';', index=False, encoding='utf-8')

Pero la función secundaria no es menos interesante, como veremos a continuación. Recordemos:



def normalizar_fecha(texto):

    if pd.isna(texto) or str(texto).strip() == "":
        return None
    
    dato = str(texto).lower().strip()
    
    meses_map = {
        'enero': 1, 'febrero': 2, 'marzo': 3, 'abril': 4, 'mayo': 5, 'junio': 6,
        'julio': 7, 'agosto': 8, 'septiembre': 9, 'octubre': 10, 'noviembre': 11, 'diciembre': 12
    }

    try:
        return pd.to_datetime(dato, dayfirst=True).date()     # Intento 1: Formato numérico completo (ej. 2/02/2026)
    except:
        pass
    anio_detectado = re.search(r'(\d{4})', dato)               # Intento 2: Formato parcial (ej. enero 2014)
    if anio_detectado:
        anio = int(anio_detectado.group(1))
        for mes_nombre, mes_numero in meses_map.items():
            if mes_nombre in dato:
                return datetime(anio, mes_numero, 1).date()      # Retornamos siempre el día 1 del mes encontrado
    return None                                                  # Si llega aquí, no se pudo convertir y devolvemos None



En esta función podemos diferenciar varias partes:
  • Primero un procedimiento de control para cuando la variable texto está vacía o no es válida
  • A continuación la variable texto se convierte a str y se normaliza a minúscula, pasando esta doble conversión a la varible dato
  • Después construímos una diccionario de meses:numerales meses_map para capturar el contenido del campo que procesamos y que a esta función hemos pasado como parámetro def normalizar_fecha(texto)

Lo que viene a continuación es el núcleo central de la función, que se desarrolla dentro de una estructura de control de excepciones try...except, en la que la parte try controla la presencia en dato de la formulación de la fecha según el formato dd/mm/aaaa y la transformar una entrada un objeto de fecha puro Año-Mes-Día de la librería Pandas. La parte except controla la posible comisión de errores

Si la primera solución no resolvió el procesamiento de la variable, la segunda utiliza la expresión regular re.search(r'(\d{4})', dato) para localizar y obtener el dato año (4 dígitos dentro del texto) y si lo encuentra recorre el listado de meses para ver si el término nombre del mes está escrito en dato. Si lo está, sobre el dato año y la conversión numérica del dato mes se construye una fecha, usando 1 como dato día.

Con este script hemos normalizado reformulandolo el campo fecha para que sea posible trabajar con esta variable en la fase de análisis de datos. El resultado es una nueva columna o campo (fecha_corregida). Cierto que que no todos los datos se han podido modificar según lo esperado debido a que el dato original no se ajusta a las formas esperadas. esto puede dar lugar a una nueva pérdida de datos, aunque al ser una circunstancia controlada y de relativa poca importancia, también podemos optar por realizar una última revisión "manual" a fin de evitar una pérdida de datos innecesaria y no deseada. Llegados a este punto, no es una mala solución.

sábado, 9 de mayo de 2026

DATOS. Tratamiento de datos

Limpieza de datos (I)

Datos faltantes

Es muy frecuente que en una colección de datos de cierta entidad se observen campos en blanco o con una marca sustituta (ver tabla abajo), a consecuencia de diversos motivos, incluídos los errores de recogida de datos y la simple ausencia de éstos por no producirse determinada respuesta. Lo que en realidad importa para su tratamiento posterior es que las ausencias sean razonablemente escasas; en caso contrario, la calidad del conjunto de datos se ve muy seriamente comprometida.

Entorno Elemento
Python puro None
Data Science (Pandas/NumPy) NaN
Base de datos (SQL) NULL
Lenguaje R NA

Las opciones disponibles para hacer frente a esta realidad son varias, desde la eliminación del registro hasta el relleno del campo con determinado valor, resultante de algún tipo de cálculo. Para conocer cuales son esas alternativas, además de consultar a la IA, que lo que hace es resumir el conjunto de posibilidades disponibles, sustituyendo así a una simple búsqueda web, también podemos consultar alguna página específicamente pensada para tratar este tema. Yo aquí te ofrezco un ejemplo de la segunda opción; la primera corre de tu cuenta.

No es por comodidad, pero en este caso voy a optar por la forma más simple de resolver el problema: eliminando el registro. No es precisamente la mejor solución, especialmente cuando no disponemos de muchos registros, pero sí la más adecuada para un caso como el que nos ocupa en el que no hay la pretensión de representatividad estadística ni interés por crear un procedimiento de (aprendizaje automático (AA). Además, en este caso, en la mayoría de los registros faltan todos los datos que pueden faltar, dado que son resultado de una opción de tratamiento del documento que ahora no me intersa comentar; sirva con decir que las tablas simplemente no están cumplimentadas, por lo que es hasta coherente eliminar estos registros. Puede que no lo sea tanto en los casos en que sólo falta la fecha, pero son muy escasos y el riesgo real de pérdida de información es mínimo.



import pandas as pd
import os
import numpy as np

# --- Función ---

def limpiar_tabla_especifica(ruta_absoluta):
    if not os.path.exists(ruta_absoluta):
        print(f"Error: No se localiza el archivo en {ruta_absoluta}")
        return

    try:
        df = pd.read_csv(ruta_absoluta, sep=';', engine='python')   # 1. Carga los datos con el separador identificado (sep=';')
        total_inicial = len(df)                                     # Conteo inicial para calcular los registros afectados

        df = df.replace(r'^\s*$', np.nan, regex=True)               # 2. Convertir espacios vacíos (" ") en nulos reales (NaN)

        columnas_a_validar = ['fecha', 'SEO', 'OE']                 # 3. Eliminar registros si falta dato en fecha-SEO-OE
        df_limpio = df.dropna(subset=columnas_a_validar)

        total_final = len(df_limpio)                                # 4. Cálcular los registros eliminados
        eliminados = total_inicial - total_final

        ruta_directorio = os.path.dirname(ruta_absoluta)            # 5. Exportar el resultado a csv
        nombre_salida = "lista_t1_sin_nulos.csv"
        ruta_salida = os.path.join(ruta_directorio, nombre_salida)

        df_limpio.to_csv(ruta_salida, sep=';', index=False)

        print("-" * 30)                                              # 6. Informe a mostrar por consola
        print("INFORME DE PROCESAMIENTO")
        print("-" * 30)
        print(f"Registros analizados: {total_inicial}")
        print(f"Registros eliminados (faltaba fecha, SEO o OE): {eliminados}")
        print(f"Registros válidos restantes: {total_final}")
        print(f"Archivo generado: {ruta_salida}")

    except Exception as e:
        print(f"Se produjo un error durante la ejecución: {e}")

# --- Llamada a la función ---

ruta_csv = r"" 														# Aqui la ruta de tu archivo csv
limpiar_tabla_especifica(ruta_csv)                                  # Llamada a la función


Mediante este script eliminamos los registros que carecen de datos en las columnas (variables) de interés columnas_a_validar = ['fecha', 'SEO', 'OE'], cosa que sucede gracias al uso de la función df.dropna() de Pandas (df_limpio = df.dropna(subset=columnas_a_validar)). Al especificar el identificador de las columnas hacemos que el script sea dependiente de la tabla, a la vez que nos permite identificar lo que deberemos cambiar si cambiamos de tabla.

Y hablando de especificidades, debo decir que para que el script funcione ha sido necesario especificar el separador de campos de la tabla (df = pd.read_csv(ruta_absoluta, sep=';', engine='python')), ya que el csv-base utiliza ; en vez del esperado (,) por defecto por Pandas.

El resultado tiene una doble expresión: archivo como csv (comentario 5 del script) se muestra una síntesis por consola (comentario 6). No proporciono ninguna prueba por innecesaria; es suficiente con que pienses en la última tabla de esta entrada sin el registro INF_003.dot.

domingo, 1 de marzo de 2026

DATOS. Acceso a datos

CSV. Datos estructurados

Biblioteca Pandas. Acceso a .csv

Aunque el módulo nativo (CSV) es suficiente cuando la tabla de datos es sencilla y el volumen de datos que contiene moderado, para trabajar con bases de datos grandes y complejas es preferible usar la bibliteca Pandas. Por eso no vamos a dedicar al módulo nativo más tiempo del necesario, puesto que será Pandas la opción preferente: lo que sirve para lo mucho, sirve para lo poco. Ganamos tiempo.

Veamos cómo acceder a nuestra base de datos de libros ahora con Pandas.



import pandas as pd
import os

ruta_archivo = 'datos\libros3.csv'

def acceder_con_pandas():
    # 1. Verificar si el archivo existe
    if not os.path.exists(ruta_archivo):
        print(f"❌ El archivo '{ruta_archivo}' no se encuentra.")
        return

    try:
        # 2. Cargar el CSV
        # Pandas detecta automáticamente la cabecera
        df = pd.read_csv(ruta_archivo, encoding='utf-8')

        print("--- 🐼 Acceso Exitoso con Pandas ---")
        
        # 3. Visualización rápida
        print("\n📊 Resumen del contenido (Primeras 5 filas):")
        print(df.head())

        # 4. Información técnica (Nº filas, columnas y tipos de datos)
        print("\nℹ️ Información técnica:")
        df.info()

        # 5. Ejemplos de acceso directo
        if not df.empty:
            # Acceder a una columna específica (ej: 'Título')
            if 'Título' in df.columns:
                print("\n📖 Lista de Títulos:")
                print(df['Título'].to_list())

            # Acceder a un registro específico por su índice (ej: el primero)
            print("\n📍 Primer registro completo:")
            print(df.iloc[0])

    except UnicodeDecodeError:
        print("❌ Error de codificación. Prueba con encoding='latin1' o 'ansi'.")
    except Exception as e:
        print(f"❌ Ocurrió un error inesperado: {e}")

if __name__ == "__main__":
    acceder_con_pandas()

Para ir avanzando puedes analizar este script y aplicarlo a un caso real (un archivo csv como éste) para ver su funcionamiento. Esto es especialmente interesante su lo comparas con el funcionamiento de script basados en el módulo nativo CSV.

NOTA: Acuérdate de guardar tu cvs como librso3.csv dentro de una carpeta que deberás llamar datos o modificar la ruta de acceso de la instrucción ruta_archivo = 'datos\libros3.csv'

martes, 23 de septiembre de 2025

Datos. Archivos pdf

Biblioteca Camelot (I)

Tablas en pdf

Cuando trabajamos con documentos pdf, uno de los contenidos de especial interés y que además resulta especialmente complicado de manejar son las tablas.

Para acceder a su contenido podemos emplear recursos como PyPDF2, pero el resultado no es muy satisfactorio como hemos tenido ocasión de comprobar, así que decidí probar con otro recurso.

Realmente resulta muy complicado automatizar la extracción de datos de tablas de un documento pdf de forma que resulte preciso y funcional, y son precisamente las tablas las que mayor dificultad presentan. Camelot es una librería pensada específicamente para esta tarea y funciona satisfactoriamente, al menos con tablas sencillas y reales, no imágenes incrustadas.

Cierto es que trabajar con Camelot presenta cierta dificultad, incluyendo algunas complicaciones para su instalación, pero se pueden superar. Por eso te recomiendo que conozcas esta biblioteca por su fuente, su propia página web, incluyendo la lectura de su manual on-line. También combiene plantearse metas sencillas y trabajar con pdf también sencillos; al menos al principio. Ya podrás incrementar la dificultad conforme vayas dominando la herramienta.

En esta entrada te mostraré la que es posiblemente la forma más sencilla de abordar un problema básico: mostrar en pantalla el contenido de una tabla de un sencillo documento pdf de una página que contiene una tabla simple. Inmediatamente podremos pasar a cuestiones de mayor complejidad. Supongamos que ese documento pdf (tablaSimple.pdf) contiene esta tabla...

... a la que deseamos acceder desde un script de Python,concretamente desde este...



import camelot
# Especifica la ruta al archivo PDF
file_path = 'tablaSimple.pdf'
# Lee las tablas que contiene el PDF
tables = camelot.read_pdf(file_path)
# Imprime el número de tablas encontradas
print (f"Total de tablas encontradas: {tables.n}")
# Imprime el contenido de la primera tabla
print (tables[ 0 ].df)


... que nos devuelve esa misma tabla en el IDE Shell

Vistas así las cosas, la verdad es que este resultado parece bastante pobre, pero podemos hacer varias cosas a partir de aquí, entre otras pasar la tabla a un archivo Excel o trabajar directamente con el contenido de la tabla mediante librerías como Pandas .

En este caso vamos a importar la librería pandas, sin entrar ahora en más explicación, y añadir unas cuantas instrucciones más al script, por ejemplo acceder a la tabla capturada y pasar los datos a una colección como paso previo para manejarla, por ejemplo, accediendo a las "celdas" [1-0] y [1-1] y mostrando su contenido...



import pandas as pd
tabla = tables[0].df
nombre = tabla.iloc[1,0]
dato = tabla.iloc[1,1]
print(f'Nombre {nombre} - Edad {dato}')


... para obtener por pantalla Nombre Juan - Edad 23

Tampoco esto parece gran cosa, pero detrás de ello tenemos toda la potencia del manejo de datos que permiten librerías como Pandas y todas aquellas que facilitan el tratamiento de datos en Python. El que lo podamos conseguir partiendo de una tabla de datos de un documento pdf es gracias a Camelot, por lo que conocer esta biblioteca es una buena forma de empezar.