[PYTHON] Comparaison de vitesse lors du changement de groupe par pandas

introduction

Lorsque vous jouez avec des données de table dans des pandas, vous souhaiterez peut-être changer la direction de ligne pour chaque catégorie (par exemple, vous voulez décaler une période pour chaque utilisateur avec des données de série chronologique). Si vous voulez transformer des données par groupe avec des pandas, vous pouvez honnêtement le faire avec groupby (). Transform. Cependant, le processus de regroupement prend beaucoup de temps. Alors, examinons combien de temps cela prendra de plusieurs manières.

environnement

procédure

Préparation des données

J'ai préparé 10 millions de lignes pour le moment. Il y a 7 variables, 5 sont des nombres appropriés et 2 sont des variables catégorielles (X, Y), qui sont respectivement 10 catégories et 4 catégories.

import numpy as np
import pandas as pd


x = np.arange(10_000_000)
y = np.tile(np.arange(10), int(len(x)/10))
z = np.tile(np.arange(4), int(len(x)/4))

df = pd.DataFrame({"a": x, "b": x, "c": x, "d": x, "e": x, "Y":y, "Z": z})

image.png

Expérience

Cette fois, j'ai essayé de regrouper par deux variables catégoriques.

Méthode 1

De manière simple, c'est la base de comparaison.

%%timeit -n 1 -r 10

s = df.groupby(["Y", "Z"])["a"].transform(lambda x: x.shift(1))
# 3.25 s ± 107 ms per loop (mean ± std. dev. of 10 runs, 1 loop each)

Méthode 2

Il s'agit d'une méthode de combinaison de variables à grouper à l'avance. C'est plus rapide, mais cela prend plus de temps pour se joindre, donc il convient aux regroupements fréquents.

dg = df.copy()
dg["YZ"] = dg["Y"].astype("str") + dg["Z"].astype("str")
# 13.7 s ± 964 ms per loop (mean ± std. dev. of 10 runs, 1 loop each)
%%timeit -n 1 -r 10

s = dg.groupby(["YZ"])["a"].transform(lambda x: x.shift(1))
# 2.62 s ± 25.1 ms per loop (mean ± std. dev. of 10 runs, 1 loop each)

Méthode 3

Il s'agit d'une méthode de création et d'exécution d'une fonction de décalage numpy au lieu de la méthode pandas shift. Il semble que la vitesse ne change pas tellement.

Référence: python --Shift elements in a numpy array --Stack Overflow

def shift2(arr, num):
    result = np.empty(arr.shape[0])
    if num > 0:
        result[:num] = np.nan
        result[num:] = arr[:-num]
    elif num < 0:
        result[-num:] = np.nan
        result[:num] = arr[-num:]
    else:
        result = arr
    return result
%%timeit -n 1 -r 10

s = df.groupby(["Y", "Z"])["a"].transform(lambda x: shift2(x, 1))
# 3.2 s ± 15.1 ms per loop (mean ± std. dev. of 10 runs, 1 loop each)

Méthode 4

C'est une méthode pour traiter chaque groupe en itérant après le regroupement. Même avec cette méthode, la vitesse ne change pas tellement et un traitement plus flexible peut être appliqué, c'est donc une méthode préférée.

%%timeit -n 1 -r 10

l = [group["a"].shift(1) for _, group in df.groupby(["Y", "Z"])]    
dh = pd.concat(l, axis=0).sort_index()
# 3.12 s ± 14.4 ms per loop (mean ± std. dev. of 10 runs, 1 loop each)

Méthode 5

En fait, «transformer» n'est pas nécessaire. C'est le plus rapide.

%%timeit -n 1 -r 10

s = df.groupby(["Y", "Z"])["a"].shift(1)
# 983 ms ± 10.9 ms per loop (mean ± std. dev. of 10 runs, 1 loop each)

résultat

Méthode La description time(per loop)
Méthode 1 Méthode standard 3.25 s ± 0.107 s
Méthode 2 Pré-rejoindre 2.62 s ± 0.0251 s
Méthode 3 numpy shift 3.2 s ± 0.0151 s
Méthode 4 Itération 3.12 s ± 0.0144 s
Méthode 5 Pas de transformation 0.983 s ± 0.0109 s

en conclusion

N'utilisez pas transform (commandement)

Recommended Posts

Comparaison de vitesse lors du changement de groupe par pandas
Standardisez par groupe avec les pandas
Comparaison de la vitesse de traitement de la pile par langue
Manipuler des chaînes avec un groupe pandas par
Génération de fonctionnalités avec pandas group par
Comparaison de vitesse lors de la recherche à l'aide de plusieurs clés dans les pandas (MultiIndex vs Others)
Quand to_csv avec Pandas, c'est devenu ligne par ligne
Trier par pandas
Comparaison de vitesse de chaque langue par la méthode de Monte Carlo