Blog Sobre mí Contacto Iniciar Sesión Registro

Cómo optimizar el rendimiento y velocidad de Pandas Python

Existen varias formas de optimizar el rendimiento y velocidad de Pandas en Python. Algunas de las técnicas avanzadas de optimización para trabajar con grandes conjuntos de datos en Python son la siguientes:
  • Utilizar siempre el tipo de dato adecuado para cada columna de un DataFrame. Por ejemplo, si una columna solo contiene números enteros, debe utilizar el tipo de dato "int" en lugar de "float".
  • Utilizar la función "pd.to_numeric" para convertir columnas de tipo object a tipos numéricos, si es posible. Esto puede mejorar significativamente el rendimiento de operaciones matemáticas.
  • Utilizar la función "pd.DataFrame.apply" de forma inteligente. Por ejemplo, en lugar de aplicar una función a cada fila de un DataFrame, es posible utilizar "apply" de forma paralela utilizando el argumento "axis=1" y "worker_pool=True".
  • Utilizar la función "pd.DataFrame.eval" para realizar operaciones matemáticas y lógicas en lugar de utilizar funciones como "apply" o "iterrows".
  • Utilizar el módulo "numexpr" para realizar operaciones matemáticas en grandes conjuntos de datos de forma más rápida.
  • Utilizar el módulo "cython" para escribir funciones personalizadas en lugar de utilizar funciones de Pandas. Esto puede ser especialmente útil para aplicaciones que requieren un rendimiento extremo. Es importante tener en cuenta que la optimización del rendimiento de Pandas depende en gran medida del contexto específico en el que se esté trabajando. Por lo tanto, es importante medir el rendimiento y comparar diferentes enfoques para encontrar la solución más adecuada para cada caso.
Esto es un breve resumen de lo vas a encontrar en el artículo. Seguidamente explicamos cada ejemplo con código Python para poder usarlo en tus scripts y líneas de programación.
  • Índice de contenidos:

Utilizar el tipo de dato adecuado en cada columna de DataFrame

Vamos a suponer que tenemos un DataFrame con dos columnas: "id" y "valores". La columna "id" solo contiene números enteros, mientras que la columna "valores" contiene números decimales:
# Ejemplo de datos del DataFrame pandas:
------------------------------------
  id   valores
0  1   10.5
1  2   15.2
2  3   20.7
3  4   25.1
En este caso, podemos optimizar el rendimiento de Pandas al especificar el tipo de dato adecuado para cada columna al crear el DataFrame:
import pandas as pd

df = pd.DataFrame({'id': [1, 2, 3, 4], 'valores': [10.5, 15.2, 20.7, 25.1]}, dtype={'id': 'int32', 'valores': 'float32'})
print(df.dtypes)
Y tendremos una salida como la siguiente:

id         int32
valores float32
dtype: object

De esta manera, evitamos tener que realizar conversiones de tipos de datos posteriores, lo que puede mejorar el rendimiento de pandas, sobre todo si trabajamos con grandes conjuntos de datos ya que, habitualmente, cuanto mayor es la cantidad de datos, más esfuerzo computacional necesitaremos. Si desde el inicio de la programación declaramos los tipos de datos adecuados, podremos notar una reducción considerable de tiempo en el conjunto de operaciones realizadas con la biblioteca pandas de Python.

Convertir columnas tipo object a numérico

Si tenemos un DataFrame con dos columnas: "id" y "valores".
La columna "id" contiene números enteros o int, mientras que la columna "valores" contiene texto o String, en este caso son números pero en formato texto:
# Ejemplo de datos del DataFrame pandas:
------------------------------------
  id   valores
0  1   10.5
1  2   15.2
2  3   20.7
3  4   25.1
Podemos utilizar la función "pd.to_numeric" para convertir la columna "valores" a tipo numérico de forma rápida y eficiente:
import pandas as pd

df['valores'] = pd.to_numeric(df['valores'])

print(df.dtypes)
Y obtenemos la siguiente salida después de realizar la conversión de tipo de dato String a numeric:

id         int32
valores float64
dtype: object

Realizar operaciones de forma paralela en pandas

Por ejemplo, supongamos que tenemos un DataFrame con dos columnas: "a" y "b". Queremos aplicar una función que recibe dos argumentos a cada fila del DataFrame y crear una nueva columna "c" con el resultado:
import pandas as pd

def mi_funcion(a, b):
  return a + b

df = pd.DataFrame({'a': [1, 2, 3, 4], 'b': [10, 20, 30, 40]})
df['c'] = df.apply(lambda row: mi_funcion(row['a'], row['b']), axis=1)

print(df)
Y nos da como resultado lo siguiente:

   a   b   c
0  1  10  11
1  2  20  22
2  3  30  33
3  4  40  44

Sin embargo, podemos mejorar el rendimiento de este código utilizando el argumento "worker_pool=True" al llamar a "apply":
import pandas as pd

def mi_funcion(a, b):
  return a + b

df = pd.DataFrame({'a': [1, 2, 3, 4], 'b': [10, 20, 30, 40]})
df['c'] = df.apply(lambda row: mi_funcion(row['a'], row['b']), axis=1, worker_pool=True)

print(df)
De esta manera, la función "mi_funcion" se aplicará de forma paralela a cada fila del DataFrame, consiguiendo una mejora significativa el rendimiento de algunos conjuntos de datos con los que trabajamos. Es importante valorar si será útil el enfoque con la función "mi_funcion", generalmente si el procesamiento de los datos resulta lento.

Usar operaciones matemáticas y lógicas en lugar de apply e itterrows

Por ejemplo, supongamos que tenemos un DataFrame con dos columnas: "a" y "b". Queremos crear una nueva columna "c" que contenga el resultado de multiplicar cada valor de la columna "a" por el valor correspondiente de la columna "b":
import pandas as pd

df = pd.DataFrame({'a': [1, 2, 3, 4], 'b': [10, 20, 30, 40]})
df['c'] = df.apply(lambda row: row['a'] * row['b'], axis=1)

print(df)
Y la salida sería:

   a   b   c
0  1  10  10
1  2  20  40
2  3  30  90
3  4  40 160

De la misma forma, también podemos optimizar el rendimiento y mejorar la velocidad del procesamiento del conjunto de datos en Python usando la función "pd.DataFrame.eval" de pandas:
import pandas as pd

df = pd.DataFrame({'a': [1, 2, 3, 4], 'b': [10, 20, 30, 40]})
df['c'] = df.eval('a * b')

print(df)
Con la función eval de pandas evitamos tener que crear una función anónima y utilizar "apply" para aplicarla a cada fila del DataFrame. Esto puede mejorar significativamente el rendimiento en según qué casos.

Procesar conjuntos de datos mucho más rápido con "numexpr"

Si tenemos un DataFrame con dos columnas: "a" y "b" y deseamos crear una nueva columna "c" que contenga el resultado de multiplicar cada valor de la columna "a" por el valor correspondiente de la columna "b" haríamos lo siguiente:
import pandas as pd

df = pd.DataFrame({'a': [1, 2, 3, 4], 'b': [10, 20, 30, 40]})
df['c'] = df.apply(lambda row: row['a'] * row['b'], axis=1)

print(df)
Dándonos la salida de datos siguiente:

   a   b   c
0  1  10  10
1  2  20  40
2  3  30  90
3  4  40 160

Entonces, para mejorar el rendimiento importaríamos y usaríamos el módulo "numexpr":
import pandas as pd
import numexpr as ne

df = pd.DataFrame({'a': [1, 2, 3, 4], 'b': [10, 20, 30, 40]})
df['c'] = ne.evaluate('a * b')

print(df)
Utilizar la biblioteca "numexpr" nos ofrece realizar multiplicaciones de forma más eficiente y rápida. Es importante tener en cuenta que este enfoque solo es útil para operaciones matemáticas simples y no tiene soporte para funciones como "sin" o "cos".

Usar funciones personalizadas en Python con "cython"

import pandas as pd

def mi_funcion(a, b):
  return a + b

df = pd.DataFrame({'a': [1, 2, 3, 4], 'b': [10, 20, 30, 40]})
df['c'] = df.apply(lambda row: mi_funcion(row['a'], row['b']), axis=1)

print(df)
Dándonos como resultado:

   a   b   c
0  1  10  11
1  2  20  22
2  3  30  33
3  4  40  44

Entonces, vamos a optimizar el rendimiento de pandas creando una función personalizada con el módulo "cython". Esto dará como resultado un código más eficiente, aunque en algunos casos puede resultar excesivamente complicado. Sin embargo, vamos a ver cómo sería la forma de crearla:
import pandas as pd
from cython.parallel import parallel, prange

@parallel(num_threads=4, nogil=True)
def mi_funcion_otimizada(int[:] a, int[:] b, int[:] c):
  for i in prange(len(a)):
    c[i] = a[i] + b[i]

df = pd.DataFrame({'a': [1, 2, 3, 4], 'b': [10, 20, 30, 40]})

a = df['a'].values
b = df['b'].values
c = np.empty(len(a), dtype=np.int32)

mi_funcion_otimizada(a, b, c)

df['c'] = c

print(df)
Como hemos indicado, al utilizar el módulo "cython" de Python, podemos escribir funciones más rápidas y con mejor rendimiento, en este caso la función que hemos llamado "mi_funcion" que aprovechará la paralelización.

NOTA: hay que tener presente que al usar cython podemos sobrecargar el equipo en ciertas condiciones. El uso del módulo cython es aconsejado para aplicaciones que requieren un rendimiento extremo, es decir, mucho más alto que las típicas funciones sencillas de la mayoría de scripts.

*** Notas finales sobre la mejora de la velocidad en pandas

Hasta aquí, hemos mostrado cómo es posible mejorar el rendimiento y velocidad en Pandas Python.

RECUERDA:
  1. Utilizar siempre el tipo de dato adecuado para cada columna de un DataFrame.
  2. Utilizar la función "pd.to_numeric" para convertir columnas de tipo object a tipos numéricos.
  3. Utilizar la función "pd.DataFrame.apply" de forma inteligente y paralela; utilizar la función "pd.DataFrame.eval" para realizar operaciones matemáticas y lógicas.
  4. Utilizar el módulo "numexpr" para realizar operaciones matemáticas en grandes conjuntos de datos de forma más rápida.
  5. Utilizar el módulo "cython" para escribir funciones personalizadas de forma más eficiente.
Y como siempre, es muy importante valorar cada caso, el conjunto de datos con el que trabajamos y si necesitamos un rendimiento superior o podemos permitirnos una menor velocidad a cambio de mayor legibilidad del código. La optimización del rendimiento de Pandas depende en gran medida del contexto en el que se está trabajando. En cualquier caso, siempre será bueno medir el rendimiento y comparar diferentes enfoques y alternativas para encontrar la solución más adecuada que necesitemos.
Pandas Python Data Analysis Library

Comentarios del artículo "Cómo optimizar el rendimiento y velocidad de Pandas Python"


¿Te ha gustado la información? Coméntanos tus opiniones, dudas y sugerencias:


Añade un comentario:

Tu dirección de correo electrónico no será publicada. | Registro requerido. | ¿Ya eres usuario? Login