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:
En este caso, podemos optimizar el rendimiento de Pandas al especificar el tipo de dato adecuado para cada columna al crear el DataFrame:
Y tendremos una salida como la siguiente:
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.
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:
Podemos utilizar la función "pd.to_numeric" para convertir la columna "valores" a tipo numérico de forma rápida y eficiente:
Y obtenemos la siguiente salida después de realizar la conversión de tipo de dato String a numeric:
Y nos da como resultado lo siguiente:
Sin embargo, podemos mejorar el rendimiento de este código utilizando el argumento "worker_pool=True" al llamar a "apply":
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.
Y la salida sería:
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:
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.
Dándonos la salida de datos siguiente:
Entonces, para mejorar el rendimiento importaríamos y usaríamos el módulo "numexpr":
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".
Dándonos como resultado:
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:
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.
RECUERDA:
- 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.
- Í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
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)
id int32
valores float32
dtype: object
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
import pandas as pd
df['valores'] = pd.to_numeric(df['valores'])
print(df.dtypes)
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)
a b c
0 1 10 11
1 2 20 22
2 3 30 33
3 4 40 44
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)
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)
a b c
0 1 10 10
1 2 20 40
2 3 30 90
3 4 40 160
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)
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)
a b c
0 1 10 10
1 2 20 40
2 3 30 90
3 4 40 160
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)
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)
a b c
0 1 10 11
1 2 20 22
2 3 30 33
3 4 40 44
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)
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:
- Utilizar siempre el tipo de dato adecuado para cada columna de un DataFrame.
- Utilizar la función "pd.to_numeric" para convertir columnas de tipo object a tipos numéricos.
- 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.
- 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 de forma más eficiente.
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: