Fügen Sie einfach die Swifter-Methode hinzu, bevor Sie die Pandas-Apply-Methode anwenden
import pandas as pd
import numpy as np
import swifter
#Erstellen Sie einen geeigneten DataFrame
df = pd.DataFrame({'col': np.random.normal(size=10000000)})
#Fügen Sie vor der Apply-Methode eine Swifter-Methode hinzu.
%time df['col2'] = df['col'].swifter.apply(lambda x: x**2)
# Wall time: 50 ms
#Zum Vergleich (normale Pandas wenden Methode an)
%time df['col2'] = df['col'].apply(lambda x: x**2)
# Wall time: 3.48 s
Für pip
$ pip install -U pandas # upgrade pandas
$ pip install swifter
Für conda
$ conda update pandas # upgrade pandas
$ conda install -c conda-forge swifter
Der Berechnungsaufwand für die Pandas-Methode beträgt O (N). Es spielt keine Rolle, ob der DataFrame ungefähr 10.000 Zeilen hat. Die Verarbeitung großer DataFrames kann sehr schmerzhaft sein. Glücklicherweise gibt es verschiedene Möglichkeiten, Pandas zu beschleunigen.
Wenn beispielsweise keine Vektorisierung möglich ist, wird eine Parallelverarbeitung durch Dask durchgeführt. Die parallele Verarbeitung auf einem DataFrame ohne viele Zeilen kann den Prozess jedoch verlangsamen. ** schneller ** ist am besten für Leute wie mich geeignet, die es umständlich finden, von Fall zu Fall die beste Beschleunigungsmethode zu wählen.
swifter Nach dem offiziellen Dokument [^ 3] macht swifter Folgendes:
Es ist sehr praktisch, automatisch die beste Methode auszuwählen. Wie ich später zeigen werde, ist in vielen Fällen schneller schneller als Pandas Ist es nicht schlecht, immer swfiter zu verwenden?
Im Folgenden möchte ich untersuchen, wie schnell Swifter im Vergleich zu Dask, Pandas usw. ist. Da sich swifter je nach Vektorisierung unterschiedlich verhält und nicht, werden wir jeden Fall überprüfen. Die Spezifikationen des verwendeten PCs sind Intel Core i5-8350U bei 1,70 GHz und der Speicher beträgt 16 GB.
Da Swifter vektorisiert wird, wenn es vektorisiert werden kann, ist die Berechnungszeit von Swifter dieselbe wie wenn es einfach vektorisiert wird. Sollte ungefähr gleich sein. Lassen Sie uns dies überprüfen.
Wenn vektorisierbar
import pandas as pd
import numpy as np
import dask.dataframe as dd
import swifter
import multiprocessing
import gc
pandas_time_list = []
dask_time_list = []
vector_time_list = []
swifter_time_list = []
#Vektorisierbare Funktion
def multiple_func(df):
return df['col1']*df['col2']
def apply_func_to_df(df):
return df.apply(multiple_func, axis=1)
for num in np.logspace(2, 7, num=7-2+1, base=10, dtype='int'):
df = pd.DataFrame()
df['col1'] = np.random.normal(size=num)
df['col2'] = np.random.normal(size=num)
ddf = dd.from_pandas(df, npartitions=multiprocessing.cpu_count())
pandas_time = %timeit -n2 -r1 -o -q df.apply(multiple_func, axis=1)
dask_time = %timeit -n2 -r1 -o -q ddf.map_partitions(apply_func_to_df).compute(scheduler='processes')
vector_time = %timeit -n2 -r1 -o -q df['col1']*df['col2']
swifter_time = %timeit -n2 -r1 -o -q df.swifter.apply(multiple_func, axis=1)
pandas_time_list.append(pandas_time.average)
dask_time_list.append(dask_time.average)
vector_time_list.append(vector_time.average)
swifter_time_list.append(swifter_time.average)
del df, ddf
gc.collect()
Die horizontale Achse der Figur ist die Anzahl der Zeilen des DataFrame, und die vertikale Achse ist die verstrichene Zeit. Beachten Sie, dass es sich um ein doppeltes logarithmisches Diagramm handelt.
Die verstrichene Zeit von swifter </ font> liegt nahe an der verstrichenen Zeit von Vektorisierung </ font>, sodass Sie sehen können, dass sie vektorisiert ist. ..
Bei DataFrames mit weniger als 100.000 Zeilen ist ein einzelner Kern von Pandas </ font> schneller als die parallele Verarbeitung von Dask </ font>. Da die verstrichene Zeit von Dask </ font> von 100.000 Zeilen oder weniger konstant ist, kann gefolgert werden, dass dies auf den Overhead zurückzuführen ist, wie z. B. die gemeinsame Nutzung von Speicher aufgrund der Parallelverarbeitung. (Funktionsberechnungszeit <Datenkopierzeit für Speicherfreigabe)
Als nächstes betrachten wir den Fall, in dem eine Vektorisierung nicht möglich ist. Wenn es nicht vektorisiert werden kann, sollte swifter zwischen Parallelverarbeitung und Einzelkernverarbeitung wählen, je nachdem, was besser ist.
Wenn eine Vektorisierung nicht möglich ist
pandas_time_list_non_vectorize = []
dask_time_list_non_vectorize = []
swifter_time_list_non_vectorize = []
#Funktionen, die nicht vektorisiert werden können
def compare_func(df):
if df['col1'] > df['col2']:
return 1
else:
return -1
def apply_func_to_df(df):
return df.apply(compare_func, axis=1)
for num in np.logspace(2, 7, num=7-2+1, base=10, dtype='int'):
df = pd.DataFrame()
df['col1'] = np.random.normal(size=num)
df['col2'] = np.random.normal(size=num)
ddf = dd.from_pandas(df, npartitions=multiprocessing.cpu_count())
pandas_time = %timeit -n2 -r1 -o -q df.apply(compare_func, axis=1)
dask_time = %timeit -n2 -r1 -o -q ddf.map_partitions(apply_func_to_df).compute(scheduler='processes')
swifter_time = %timeit -n2 -r1 -o -q df.swifter.apply(compare_func, axis=1)
pandas_time_list_non_vectorize.append(pandas_time.average)
dask_time_list_non_vectorize.append(dask_time.average)
swifter_time_list_non_vectorize.append(swifter_time.average)
del df, ddf
gc.collect()
swifter </ font> wird von einem einzelnen Kern verarbeitet, wenn keine Parallelverarbeitung erzielt wird. Wenn die Parallelverarbeitung dem Einzelkern überlegen ist, können Sie sehen, dass die Parallelverarbeitung ausgewählt ist.
swifter ist ein hervorragendes Modul, das je nach Situation automatisch die optimale Beschleunigungsmethode auswählt. Um keine wertvolle Zeit zu verschwenden, verwenden Sie bei Verwendung der Pandas-Apply-Methode den Swifter.
Eine Vektorisierungsfunktion ist eine Funktion, die automatisch auf alle Elemente angewendet wird, ohne eine explizite for-Schleife zu schreiben. Ich denke, es ist einfacher zu verstehen, wenn Sie sich ein Beispiel ansehen.
Wenn nicht vektorisiert
array_sample = np.random.normal(size=1000000)
def non_vectorize(array_sample):
result = []
for i in array_sample:
result.append(i*i)
return np.array(result)
%time non_vectorize_result = non_vectorize(array_sample)
# Wall time: 350 ms
Wenn vektorisiert
def vectorize(array_sample):
return array_sample*array_sample
%time vectorize_result = vectorize(array_sample)
# Wall time: 4.09 ms
Durch Vektorisierung ist es ungefähr 80-mal schneller. Überprüfen Sie, ob die beiden Ergebnisse übereinstimmen.
Überprüfen Sie, ob sie übereinstimmen
np.allclose(non_vectorize_result, vectorize_result)
# True
Recommended Posts