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.
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})
Cette fois, j'ai essayé de regrouper par deux variables catégoriques.
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)
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)
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)
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)
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)
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 |
N'utilisez pas transform
(commandement)
Recommended Posts