Estoy tratando de encontrar una forma más optimizada de agregar datos a un marco de datos de pandas . Ya vi otras preguntas relacionadas donde las personas sugirieron crear primero listas y luego agregar los datos a pandas (que ahora implementé).
En mi configuración actual, recorro diferentes listas (en el ejemplo, es librarynr , books y sections ) y luego calculo varias variables (en el ejemplo, no están calculadas pero ya están configuradas; nrofletters , excitment y review ) que agrego a las listas y al final agregue las listas al marco de datos.
¿Alguien sabe de más optimizaciones para mejorar el rendimiento en este código de ejemplo?
Nota importante: en mi código final, las variables no son las mismas para todas las filas, sino que se calculan según los iteradores de los bucles (consulte el ejemplo de cálculo de excitment ).
Código de ejemplo:
import pandas as pd import time books = ['LordOfTheRings','HarryPotter','LoveStory','RandomBook'] sections = ['Introduction','MainPart','Plottwist','SurprisingEnd'] librarynr = list(range(30000)) nrofletters = 3000 excitment = True review = 'positive' start_time = time.time() summarydf = pd.DataFrame() indexlist = [] nrofletterlist = [] excitmentlist = [] reviewlist = [] for library in librarynr: for book in books: for section in sections: indexlist.append(str(library)+book+section) nrofletterlist.append(nrofletters) #example of variable calculation depending on iterators of loop: if (library % 2 == 0) or (book[1] == 'L'): excitment = False else: excitment = True excitmentlist.append(excitment) reviewlist.append(review) summarydf['index'] = indexlist summarydf['nrofletters'] = nrofletterlist summarydf['excitment'] = excitmentlist summarydf['review'] = reviewlist listtime = time.time() - start_time print(listtime)Append es muy lento , debe producir su DataFrame de una sola vez.
IIUC, quieres un producto de todas las posibilidades. Puede utilizar itertools.product
Estoy dando aquí un ejemplo con solo librarynr = 5 . Su condición con librarynr = 300000 produciría 4,8 millones de filas.
from itertools import product librarynr = 5 idx = map(''.join, product(map(str, range(librarynr)), books, sections)) df = pd.DataFrame([], index=idx) df[['nrofletters', 'excitment', 'review']] = [3000, True, 'positive']Producción:
>>> print(df.reset_index()) index nrofletters excitment review 0 0LordOfTheRingsIntroduction 3000 True positive 1 0LordOfTheRingsMainPart 3000 True positive 2 0LordOfTheRingsPlottwist 3000 True positive 3 0LordOfTheRingsSurprisingEnd 3000 True positive 4 0HarryPotterIntroduction 3000 True positive .. ... ... ... ... 75 4LoveStorySurprisingEnd 3000 True positive 76 4RandomBookIntroduction 3000 True positive 77 4RandomBookMainPart 3000 True positive 78 4RandomBookPlottwist 3000 True positive 79 4RandomBookSurprisingEnd 3000 True positive [80 rows x 4 columns]Los bucles de CPython son muy lentos debido al intérprete (tenga en cuenta que hay intérpretes de Python más rápidos como PyPy que usan un compilador JIT ). Puede usar la lista de comprensión para acelerar significativamente los bucles. El uso itertools puede ayudar un poco más, además de convertir la library en una cadena en el bucle más externo. Sin embargo, el resultado aún no es muy rápido respecto a la operación realizada.
Otro problema proviene de la conversión de la lista de Python a matrices Numpy . De hecho, Pandas usa Numpy internamente y Numpy convierte cada referencia de cadena en una cadena limitada estática (por lo tanto, usa un gran búfer de memoria sin formato y no una matriz de objetos contados por referencia). Esto significa que cada cadena es analizada y copiada por Numpy, lo cual es muy costoso. La mejor solución es escribir directamente la matriz Numpy usando funciones vectorizadas si es posible. Si esto no es posible, puedes usar Numba . Sin embargo, tenga en cuenta que Numba apenas admite arreglos de cadenas hasta ahora. Otra posible solución es usar Cython . El uso de una asignación directa de Pandas también puede ser muy rápido para configurar todas las cadenas a la vez (ya que Numpy solo analiza una vez la cadena internamente).
En mi máquina, aproximadamente 2/3 del tiempo se gasta en el bucle y 1/3 en la conversión de cadena Numpy (una parte menor proviene de algunos gastos generales adicionales de Pandas).