Breve tutorial de Python: Temas avanzados en Pandas (2023)

Publicado en:
En 2023, exploré las funciones avanzadas de Pandas para Python.

Introducción

He estado sumergido en el procesamiento y análisis de datos durante años, y puedo afirmar que Pandas es mucho más que una biblioteca más de Python. Ya sea que estés manejando conjuntos de datos complejos o mejorando el rendimiento para operaciones de datos a gran escala, Pandas actúa como la columna vertebral para una manipulación de datos eficiente y sólida. Es una herramienta que he utilizado una y otra vez, no solo para tareas básicas, sino también en etapas cruciales de los flujos de trabajo de aprendizaje automático. A través de ensayos, errores y muchas horas de programación, he acumulado conocimientos y mejores prácticas que estoy ansioso por compartir – consejos que son especialmente cruciales para aquellos que se inician en el mundo de la ciencia de datos en Python.

Manipulación de Datos Avanzada con MultiIndex

Cuando comencé a trabajar con Pandas, manejar datos planos era bastante sencillo. Sin embargo, al enfrentarme a estructuras de datos más complejas, como varios niveles de agrupación dentro de un solo conjunto de datos, las cosas se volvieron más complicadas. Ahí es donde entra MultiIndex: una solución revolucionaria para trabajar con datos de alta dimensión en una estructura de datos bidimensional.

Entonces, ¿cuál es el asunto con MultiIndex? Imagina que estás manejando datos de ventas que abarcan diferentes regiones, varios productos y múltiples periodos de tiempo. Sin MultiIndex, tendrías que lidiar con múltiples tablas dinámicas o combinar conjuntos de datos constantemente. Con MultiIndex, puedes estructurar estos datos en un solo DataFrame, con índices que representan varias dimensiones.

Aquí tienes un ejemplo rápido para mostrarte cómo se hace. Supongamos que tenemos datos de ventas para dos productos ("Widgets" y "Gadgets") en dos regiones ("Norte" y "Sur") durante dos trimestres:

import pandas as pd

Quisiera que el siguiente párrafo se reescribiera en español perfectamente. Por favor, no realices una traducción literal.

arrays = [['Norte', 'Norte', 'Sur', 'Sur'], ['T1', 'T2', 'T1', 'T2'], ['Widgets', 'Widgets', 'Gadgets', 'Gadgets']]
tuplas = list(zip(*arrays))
índice = pd.MultiIndex.from_tuples(tuplas, names=['Región', 'Trimestre', 'Producto'])
datos = {'Ventas': [25000, 30000, 15000, 20000]}
df = pd.DataFrame(datos, index=índice)

Esto nos proporciona un DataFrame con un índice jerárquico, lo cual puedes confirmar simplemente imprimiendo `df`.

Ahora, supongamos que quiero analizar las ventas totales por región, sin importar el trimestre o el producto. Aquí es donde la función `sum`, junto con el parámetro `level`, resulta muy útil.

```python
ventas_regionales = df.sum(level='Region')
print(ventas_regionales)

Observarás que esta operación respeta la estructura jerárquica, sumando únicamente dentro del nivel especificado.

Ahora, ¿qué pasa si necesito reformatear estos datos? Con MultiIndex, hacer una tabla dinámica es muy sencillo. Supongamos que quiero comparar las ventas de productos lado a lado para cada trimestre. El método unstack puede reorganizar nuestro DataFrame con MultiIndex en algo más convencional:

df_desapilado = df.unstack(level="Product")
print(df_desapilado)

El antónimo de unstack es, como era de esperar, stack. Este método compacta uno de los niveles del DataFrame, lo cual puede ser útil cuando necesitas serializar los datos o introducirlos en alguna fase de un pipeline de aprendizaje automático—simplemente no en el que podrías estar aprendiendo en otra sección de esta serie de artículos.

La segmentación es bastante sencilla con MultiIndex, especialmente con la utilidad pd.IndexSlice. Si necesito obtener datos para "Widgets" en la región "Norte":

En el siguiente código se define idx como un objeto pd.IndexSlice. Luego, se extraen los datos de "Widgets" para la región "North" de un DataFrame df utilizando loc con idx, almacenándolos en widgets_north_data. Finalmente, se imprimen estos datos con print.

Parece sencillo una vez que lo entiendes, ¿verdad?

Para aquellos que deseen profundizar aún más, pueden encontrar una documentación extensa sobre MultiIndex en la documentación oficial de Pandas (https://pandas.pydata.org/pandas-docs/stable/user_guide/advanced.html). Y si eres como yo y aprendes mejor observando ejemplos, el repositorio de GitHub de Pandas (https://github.com/pandas-dev/pandas) está lleno de pruebas unitarias y ejemplos de uso que muestran aplicaciones del mundo real de MultiIndex.

Debo decir que dominar el uso de MultiIndex ha resultado ser excelente para mis tareas de manipulación de datos. Aporta una dimensión poderosa a las operaciones con DataFrames, convirtiendo tareas que parecían complicadas en unas pocas líneas de código. Y para los principiantes, no se sientan abrumados. Cuanto más integré MultiIndex en mis flujos de trabajo, más intuitivo se volvió. ¡Feliz indexación!

Optimización del Rendimiento en Pandas

La optimización del rendimiento es esencial al trabajar con grandes conjuntos de datos en Pandas. La eficiencia puede marcar la diferencia entre una tarea que toma minutos y otra que se extiende por horas. He descubierto varias técnicas que reducen significativamente el tiempo de ejecución de mi código y estoy ansioso por compartir algunas de estas estrategias de optimización.

Una de las primeras cosas que aprendí fue la importancia de seleccionar los tipos de datos adecuados. Pandas utiliza por defecto tipos de 64 bits, lo cual puede ser excesivo y consumir cantidades innecesarias de memoria. Al convertir a tipos de datos más pequeños cuando sea posible, puedes reducir drásticamente el uso de memoria. Veamos un ejemplo básico:

import pandas as pd

Supongamos que df es nuestro gran DataFrame.

# Verifica los tipos de datos y el uso de memoria
print(df.dtypes)
print(df.memory_usage(deep=True))

# Convertir int64 a int32 y float64 a float32
df_int = df.select_dtypes(include=['int64']).astype('int32')
df[df_int.columns] = df_int

df_float = df.select_dtypes(include=['float64']).astype('float32')
df[df_float.columns] = df_float

# Comprobar la reducción del uso de memoria
print(df.memory_usage(deep=True))

Si trabajas con datos categóricos, el tipo de dato `category` en Pandas puede ser de gran ayuda. Esto es especialmente útil si tienes una gran cantidad de repetición en tus datos de texto.

```python
# Convertir objeto a categoría
df_object = df.select_dtypes(include=['object']).nunique().sort_values()
# Supongamos que tenemos una columna llamada 'category' que tiene un número limitado de valores únicos
df['category'] = df['category'].astype('category')

Verifica el uso de memoria antes y después ejecutando: print(df.memory_usage(deep=True))

Otro consejo de rendimiento que sigo es evitar los bucles siempre que sea posible y utilizar las operaciones vectorizadas de Pandas. Esto transformó por completo mis scripts cuando los reescribí para eliminar los bucles. Así es como podría verse en la práctica:

# Mala práctica con el bucle
for index, row in df.iterrows():
    df.at[index, 'new_col'] = row['col1'] * row['col2']

Mejor Práctica con Vectorización

df['nueva_col'] = df['col1'] * df['col2']

Por último, utilizo con frecuencia los métodos query() y eval() para acelerar mis operaciones de indexación booleana y cálculos. En lugar de las operaciones estándar de pandas, que pueden ser lentas, estos métodos agilizan las operaciones usando Numexpr. Aquí tienes una comparación:

# Utilizando operaciones estándar de Pandas
df_filtrado = df[df['col']  0]

Utilizando la consulta para una operación más rápida

df_filtrado = df.query('col 0')

Y para realizar cálculos:

# Utilizando operaciones estándar de Pandas
df['nueva_col'] = df['col1'] * df['col2'] + df['col3']

# Empleando eval para una operación más rápida
df.eval('nueva_col = col1 * col2 + col3', inplace=True)

Una combinación de estas técnicas generalmente mejora el rendimiento de manera espectacular, pero hay que recordar que los resultados pueden variar según el tamaño y la naturaleza de tu conjunto de datos. La lección principal que he aprendido de mis experiencias es que siempre se deben probar diferentes enfoques para encontrar la solución más adecuada.

Para profundizar más en el tema de optimización de rendimiento con Pandas, te recomiendo encarecidamente revisar la documentación oficial de Pandas sobre [cómo mejorar el rendimiento](https://pandas.pydata.org/pandas-docs/stable/user_guide/enhancingperf.html). Además, es útil que evalúes continuamente tu código con herramientas como `%timeit` en IPython o el módulo `time` de Python. Sigue experimentando para encontrar la configuración óptima para tu caso específico.





<!-- RENDERRELATEDBLOCK HERE -->


<div class="flex items-center pt-5 -mb-3">
<div class="flex-shrink-0 mr-2 rtl:mr-0 rtl:ml-2">
</div>
<h2 class="text-2xl tracking-tight leading-tight -mt-6" id="anlisis-de-series-temporales-hecho-fcil">Análisis de Series Temporales Hecho Fácil</h2>
</div>

El análisis de series temporales es una herramienta esencial para cualquier persona que trabaje con datos influenciados por el tiempo. Con pandas, una de las principales bibliotecas de Python, puedes manipular y analizar datos de series temporales de manera eficiente, incluso si tienes poca experiencia en ciencia de datos. Compartiré algunos aspectos básicos para que puedas comenzar de manera rápida.

Primero, asegúrate de tener pandas instalado:

```python
pip install pandas

Supongamos que tenemos un archivo CSV llamado 'stock_data.csv' que contiene precios diarios de acciones y una columna de fechas. Crear un DataFrame de pandas a partir de este archivo es un proceso sencillo.

Pandas es una biblioteca de software para Python especializada en el manejo y análisis de datos.

df = pd.read_csv('stock_data.csv', parse_dates=['Date'], index_col='Date')

El parámetro parse_dates garantiza que pandas reconozca nuestra columna 'Date' como objetos de tipo datetime, lo cual es crucial para el análisis de series temporales. Al establecer esta columna como índice con index_col, se facilita la segmentación y manipulación de los datos basados en el tiempo.

Con esta configuración, puedo acceder rápidamente a los precios de las acciones en un día específico.

dia_especifico = df.loc['2023-01-01']

Si necesito obtener todos los precios de las acciones para enero de 2023, utilizo:

datos_enero = df['2023-01']

Una operación frecuente es la de remuestreo, que modifica la frecuencia de los puntos temporales en los datos. Por ejemplo, para convertir nuestros datos diarios en datos mensuales y calcular el precio medio por mes.

datos_mensuales = df.resample('M').mean()

Las medias móviles suelen utilizarse para suavizar las fluctuaciones a corto plazo y resaltar las tendencias a más largo plazo. El cálculo de una media móvil simple de 7 días se realiza de la siguiente manera:

df['media_móvil_7_días'] = df['Precio'].rolling(window=7).mean()

Para realizar pronósticos de series temporales, pandas puede preparar la estructura de datos que se ingresará en un modelo de predicción. Imaginemos un modelo de aprendizaje automático que predice precios futuros de acciones basándose en datos históricos. Nuestro primer paso sería crear una característica rezagada, que es el precio del día anterior.

En el DataFrame, se ha añadido una columna llamada 'Prev_day_price' que contiene los valores de la columna 'Price' desplazados una posición hacia abajo.

Sin embargo, el análisis de series temporales va más allá de la simple manipulación de datos. Frecuentemente enfrentamos valores faltantes, especialmente cuando el mercado de valores está cerrado durante los fines de semana y días festivos. Podemos completar estos valores faltantes hacia adelante usando pandas.

df = df.asfreq('D', method='ffill')

La representación gráfica es también una parte importante del análisis. Con pandas, visualizar tus datos de series temporales es muy sencillo. Así es como puedo graficar rápidamente nuestro DataFrame monthly_data:

import matplotlib.pyplot as plt

monthly_data.plot()
plt.title('Precio Promedio Mensual de las Acciones')
plt.show()

Si deseas ampliar tus conocimientos más allá de lo básico que te he mostrado, hay una gran cantidad de recursos disponibles. Para técnicas avanzadas y escenarios complejos, la documentación de pandas (pandas.pydata.org) es muy detallada. Además, el libro "Python Data Science Handbook" de Jake VanderPlas es un recurso excelente y cuenta con un repositorio asociado en GitHub (github.com/jakevdp/PythonDataScienceHandbook) que incluye notebooks de ejemplo para que puedas seguirlos fácilmente.

Para avanzar más allá del nivel principiante, considera trabajar en un proyecto con datos del mundo real. La reconocida práctica de Pronóstico de Series Temporales en Kaggle (kaggle.com) ofrece conjuntos de datos y desafíos que pondrán a prueba tus habilidades y desarrollarán tu comprensión.

Recuerda que aunque este resumen cubre aspectos clave del análisis de series temporales con pandas, la verdadera comprensión se logra con la práctica. Así que toma un conjunto de datos y comienza a explorar.

Integración de Pandas con IO Asíncrono Moderno en Python

Integrar E/S asíncrona con Pandas es como tratar de encajar una pieza cuadrada en un agujero redondo; originalmente no fueron diseñados para funcionar juntos de manera perfecta. No obstante, las características asíncronas de Python pueden ofrecer beneficios de rendimiento significativos cuando se trata de operaciones limitadas por E/S. Como alguien que ha trabajado con grandes y complicados conjuntos de datos que dependen de flujos de datos en vivo, he desarrollado algunas estrategias para combinar el mundo sincrónico de Pandas con las funciones asíncronas de Python. Vamos a repasarlas.

La biblioteca asyncio en Python permite la concurrencia mediante el uso de corutinas. Las operaciones sincrónicas tradicionales bloquean el hilo hasta su finalización, mientras que una operación asincrónica se suspende cuando está a la espera de un resultado que no está disponible de inmediato, permitiendo así que el hilo realice otras tareas mientras tanto.

Sin embargo, la mayoría de las funciones de Pandas son síncronas y, por lo tanto, bloquearán el bucle de eventos cuando se ejecuten. Para evitar esto, debemos ejecutar las operaciones de Pandas en un grupo de hilos, utilizando el método run_in_executor de asyncio. Esto permite usar Pandas de manera no bloqueante dentro de una aplicación asíncrona. Vamos a demostrar esto con un ejemplo.

Supongamos que tenemos una función asíncrona para obtener datos a través de la red (una operación asíncrona común). Ejecutaremos la obtención de datos de manera asíncrona, pero luego procesaremos los datos recuperados utilizando Pandas en un grupo de hilos.

import aiohttp
import pandas as pd
import asyncio
from concurrent.futures import ThreadPoolExecutor

# Obtener datos de forma asíncrona
async def obtener_datos(session, url):
async with session.get(url) as response:
return await response.text()

# Procesar datos con Pandas de manera segura para hilos
def process_data(data):
    df = pd.read_csv(data)
    # Realizar algunas operaciones de Pandas en el df
    return df

async def main():
# Lista de URLs para obtener datos
urls = ["http://example.com/data1.csv", "http://example.com/data2.csv"]
# Crear un ThreadPoolExecutor
executor = ThreadPoolExecutor(max_workers=2)

async con aiohttp.ClientSession() como session:
# Programar todas las operaciones de obtención para que se ejecuten de forma asíncrona
futuros_fetch = [fetch_data(session, url) para url en urls]
lista_datos_crudos = await asyncio.gather(*futuros_fetch)

Cuando tengamos los datos, procesémoslos en paralelo utilizando Pandas de manera segura para hilos. Usamos el bucle de eventos asyncio para esperar el resultado de varias tareas que se ejecutan en un ejecutor, aplicando la función de procesamiento a cada conjunto de datos de la lista inicial.

Ahora df_list incluye todos nuestros DataFrames procesados.

Si __name__ == "__main__":
asyncio.run(main())

El código emplea `aiohttp` para realizar solicitudes HTTP asíncronas y obtener datos CSV de URLs de ejemplo. Posteriormente, procesa estos datos en DataFrames de Pandas de manera segura para hilos utilizando un `ThreadPoolExecutor`. Aunque es un ejemplo simplificado, ilustra la configuración básica esencial.

Es importante destacar que aunque `asyncio` y Pandas no se comunican directamente, este método de manejar las tareas intensivas de Pandas en un hilo separado permite que tu aplicación se beneficie de la entrada/salida asíncrona. Deberás ajustar el número de trabajadores en el `ThreadPoolExecutor` para que coincida con las capacidades de tu sistema, ya que crear demasiados hilos puede tener un impacto negativo en el rendimiento.

Al aplicar este enfoque híbrido en tu desarrollo en Python, estás combinando las fortalezas de la programación asíncrona con la poderosa manipulación de datos de Pandas. Aunque no estamos convirtiendo a Pandas en una biblioteca asíncrona, estamos permitiendo que se integre perfectamente en un marco asíncrono.

Para los desarrolladores y analistas de datos en formación, dominar las complejidades de Pandas y el async IO de Python puede marcar la diferencia entre una aplicación que simplemente funciona y una que destaca por su eficiencia. Solo recuerda que el mundo async está lleno de trampas, así que prueba cada paso minuciosamente y mantén atención a tu bucle de eventos.





<div class="flex items-center pt-5 -mb-3">
<div class="flex-shrink-0 mr-2 rtl:mr-0 rtl:ml-2">
</div>
<h2 class="text-2xl tracking-tight leading-tight -mt-6" id="aprovechando-pandas-para-flujos-de-trabajo-de-aprendizaje-automtico">Aprovechando Pandas para Flujos de Trabajo de Aprendizaje Automático</h2>
</div>

Después de explorar las diversas capacidades de Pandas para la manipulación avanzada de datos, la optimización del rendimiento y el manejo de series temporales, me gustaría dirigir su atención a cómo esta potente biblioteca se integra con los flujos de trabajo de aprendizaje automático.

En el aprendizaje automático, la preprocesamiento de datos es un paso crucial. Pandas es excelente en esta tarea. La limpieza de datos, el manejo de valores perdidos y la ingeniería de características son solo algunos ejemplos donde Pandas actúa como un caballo de batalla preliminar antes de que comiences a entrenar modelos.

Comencemos importando las bibliotecas esenciales:

```python
importa pandas como pd
desde sklearn.model_selection importa train_test_split
desde sklearn.preprocessing importa StandardScaler

Imagina que has cargado un conjunto de datos en un DataFrame de Pandas. Primero, querrás encargarte de cualquier valor faltante.

# Código de ejemplo para tratar valores faltantes
df.fillna(df.mean(), inplace=True)

Una vez que has solucionado los valores nulos de tus datos, puedes proceder a codificar las variables categóricas si es necesario.

# Convertir la columna categórica 'Category' a valores numéricos
df['Category'] = df['Category'].astype('category').cat.codes

Antes de procesar tus datos con un modelo de aprendizaje automático, es recomendable normalizar las características.

# Estandarización de las columnas de características
escalador = StandardScaler()
df[['Feature1', 'Feature2']] = escalador.fit_transform(df[['Feature1', 'Feature2']])

Dividir tu conjunto de datos en conjuntos de entrenamiento y prueba es fácil usando train_test_split de sklearn. Sin embargo, es importante mencionar que Pandas se integra bien con esta división.

# Nota: Se asume que 'Label' es la columna que se está prediciendo
X = df.drop('Label', axis=1)
y = df['Label']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

Como puedes observar, puedo pasar estructuras de Pandas directamente a las funciones de entrenamiento y prueba, lo que simplifica el proceso. La mayoría de la API de sklearn puede manejar DataFrames y Series de Pandas sin problemas.

La selección de características es otra área en la que Pandas demuestra su eficacia. Supongamos que has calculado la matriz de correlación y deseas eliminar las características que están altamente correlacionadas.

# Calcular la matriz de correlación
matriz_corr = df.corr().abs()

Selecciona el triángulo superior de la matriz de correlación utilizando upper = corr_matrix.where(np.triu(np.ones(corr_matrix.shape), k=1).astype(np.bool)).

Identificar características con una correlación superior a 0.95

to_drop = [columna for columna in upper.columns if any(upper[columna] 0.95)]

Eliminar características: df.drop(to_drop, axis=1, inplace=True)

Después de haber entrenado tu modelo, Pandas puede ayudarte a evaluar los resultados. Si has realizado algunas predicciones, puedes compararlas con los valores reales y calcular métricas directamente desde el DataFrame.

Utilizando la función accuracy_score del módulo sklearn.metrics.

predicciones = modelo.predecir(X_test) resultados = pd.DataFrame({'Real': y_test, 'Pronosticado': predicciones}) print(accuracy_score(resultados['Real'], resultados['Pronosticado']))

A lo largo de tu proceso de aprendizaje automático, descubrirás que Pandas ofrece formas intuitivas, flexibles y eficientes para manipular tus datos. Desde la limpieza hasta la extracción de características, e incluso en el análisis posterior al modelado, utilizar Pandas durante este proceso puede hacerlo mucho más fluido.

Aunque al principio esto pueda parecer abrumador para un principiante, quiero asegurarte que una vez que te familiarices con estas operaciones, se volverán parte natural de tu práctica en ciencia de datos. Recuerda que la clave es la persistencia, y que experimentar con estas herramientas dará sus frutos a medida que te adentres más en proyectos de aprendizaje automático.

En resumen, al aprovechar el poder de Pandas junto con tus algoritmos de aprendizaje automático, simplificas la fase de preprocesamiento, permitiéndote concentrarte en la construcción y optimización de modelos más avanzados. La relación entre el preprocesamiento de datos con Pandas y la creación de modelos es simbiótica, cada uno fortalece el éxito del otro. Adopta estas técnicas; son herramientas indispensables en tu arsenal de ciencia de datos.


Compartir

Comentarios (0)

Publicar un comentario

© 2023 - 2025 — TensorScience. Todos los derechos reservados.