Ajoutez simplement la méthode plus rapide avant la méthode d'application Pandas
import pandas as pd
import numpy as np
import swifter
#Créez un DataFrame adapté
df = pd.DataFrame({'col': np.random.normal(size=10000000)})
#Ajoutez une méthode plus rapide avant la méthode Apply.
%time df['col2'] = df['col'].swifter.apply(lambda x: x**2)
# Wall time: 50 ms
#Pour comparaison (les pandas normaux appliquent la méthode)
%time df['col2'] = df['col'].apply(lambda x: x**2)
# Wall time: 3.48 s
Pour pip
$ pip install -U pandas # upgrade pandas
$ pip install swifter
Pour conda
$ conda update pandas # upgrade pandas
$ conda install -c conda-forge swifter
Le montant de calcul de la méthode d'application de Pandas est O (N). Peu importe si le DataFrame a environ 10000 lignes, Le traitement de grands DataFrames peut être assez pénible. Heureusement, il existe plusieurs façons d'accélérer les pandas.
Par exemple, lorsque la vectorisation n'est pas possible, un traitement parallèle par Dask est effectué. Cependant, le traitement parallèle sur un DataFrame qui n'a pas beaucoup de lignes peut ralentir le processus. Pour les gens comme moi qui trouvent compliqué de choisir la meilleure méthode d'accélération au cas par cas, ** plus rapide ** est le meilleur choix.
swifter Selon le document officiel [^ 3], swifter fait ce qui suit:
Il est très pratique de sélectionner automatiquement la meilleure méthode. Comme je le montrerai plus tard, dans de nombreux cas, plus rapide est plus rapide que les Pandas, donc N'est-il pas mal de toujours utiliser swfiter?
Ci-dessous, je voudrais vérifier à quelle vitesse est plus rapide que Dask, Pandas, etc. Puisque swifter se comporte différemment selon qu'il peut être vectorisé ou non, nous vérifierons chaque cas. Les spécifications du PC utilisé sont Intel Core i5-8350U à 1,70 GHz et la mémoire est de 16 Go.
Etant donné que plus rapide est vectorisé lorsqu'il peut être vectorisé, le temps de calcul de plus rapide est le même que lorsqu'il est simplement vectorisé. Devrait être à peu près égal. Vérifions ça.
Lorsque vectorisable
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 = []
#Fonction vectorisable
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()
L'axe horizontal de la figure correspond au nombre de lignes du DataFrame et l'axe vertical correspond au temps écoulé. Notez qu'il s'agit d'un double graphe logarithmique.
Le temps écoulé de swifter </ font> est proche du temps écoulé de vectorisation </ font>, vous pouvez donc voir qu'il est vectorisé. ..
Pour les DataFrames de moins de 100 000 lignes, un seul noyau de Pandas </ font> est plus rapide que le traitement parallèle de Dask </ font>. Puisque le temps écoulé de Dask </ font> de 100 000 lignes ou moins est constant, on peut en déduire que cela est dû à la surcharge comme le partage de mémoire dû au traitement parallèle. (Temps de calcul de la fonction <Temps de copie des données pour le partage de la mémoire)
Examinons ensuite le cas où la vectorisation n'est pas possible. S'il ne peut pas être vectorisé, plus rapide devrait choisir entre le traitement parallèle et le traitement monocœur, selon ce qui est le mieux.
Lorsque la vectorisation n'est pas possible
pandas_time_list_non_vectorize = []
dask_time_list_non_vectorize = []
swifter_time_list_non_vectorize = []
#Fonctions non vectorisées
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> est traité par un seul cœur lorsque le traitement parallèle n'est pas obtenu. Si le traitement parallèle est supérieur au single core, vous pouvez voir que le traitement parallèle est sélectionné.
swifter est un excellent module qui sélectionne automatiquement la méthode d'accélération optimale en fonction de la situation. Pour éviter de perdre un temps précieux, utilisez plus rapidement lorsque vous utilisez la méthode d'application de Pandas.
Une fonction de vectorisation est une fonction qui s'applique automatiquement à tous les éléments sans écrire une boucle for explicite. Je pense que c'est plus facile à comprendre si vous regardez un exemple.
Si non vectorisé
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
Lorsque vectorisé
def vectorize(array_sample):
return array_sample*array_sample
%time vectorize_result = vectorize(array_sample)
# Wall time: 4.09 ms
En vectorisant, c'est environ 80 fois plus rapide. Vérifiez que les deux résultats correspondent.
Vérifiez s'ils correspondent
np.allclose(non_vectorize_result, vectorize_result)
# True
Recommended Posts