[PYTHON] So beschleunigen Sie die Anwendungsmethode von Pandas mit nur einem Satz (mit Verifizierungsberechnung)

Fazit

Fügen Sie einfach die Swifter-Methode hinzu, bevor Sie die Pandas-Apply-Methode anwenden

Konkretes Beispiel

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

Wie installiert man

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

Was macht schneller?

Pandas gelten nur langsam

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.

Pandas Beschleunigungsmethode

  1. [Vectoring](#Was ist Vectorizing)
  2. Benutze Cython und Numba [^ 1]
  3. Parallelverarbeitung durch Dask [^ 2]

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:

  1. Vektorisierung Wenn möglich, vektorisieren.
  2. Wenn keine Vektorisierung möglich ist, wird automatisch die schnellere Parallelverarbeitung von Dask und die Anwendung von Pandas ausgewählt.

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?

Überprüfung

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.

Wenn vektorisierbar

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()

vect.png

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)

Wenn eine Vektorisierung nicht möglich ist

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()

non_vect.png

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.

Zusammenfassung

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.

Bonus

Was ist Vektorisierung?

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