Wie gruppiert man eine Spalte, die in verschiedenen Intervallen von einer anderen in Pandas vorgespannt ist?
Ich habe den folgenden pd.DataFrame:
source = pd.DataFrame([[0.99, 0.98, 0.93, 0.81, 0.85, 0.71, 0.7, 0.69, 0.68, 0.66],
[100, 12, 312, 23, 2, 12, 32, 21, 21, 21]]).T
Ich möchte es so schnell wie möglich konvertieren zu:
desired_result = pd.DataFrame([[0.9, 0.8, 0.7, 0.6], [424, 25, 44, 63]]).T
Wo oben definiere ich ein Intervall, 0.1
dessen Anwendung ich auf die Spalte 0
des source
Datenrahmens anwende und die 1
Spalte desselben Datenrahmens summiere. Die Idee ist, dass dies mit unterschiedlichen Intervallen funktionieren sollte.
Was ich versucht habe:
Ich habe darüber nachgedacht,
pd.cut
aber das scheint nicht das zu sein, wonach ich suche.Ich weiß, dass ich, wenn ich eine neue Spalte
source
mit doppelten Werten von 0,9, 0,8, 0,7 und 0,6 in den entsprechenden Zeilen hinzufüge,groupby
diese neue Spalte verwenden kannsum
, aber ich frage mich, ob es einen saubereren und schnelleren Weg gibt um dies zu tun? zB so etwas:
interval = 0.1
source['ints'] = (source[0] / interval).astype(int)
result = source.groupby(source['ints']).sum().reset_index()
result
Das Obige würde jedoch nicht funktionieren, wenn ich beispielsweise die Intervallform von 0,1 auf 0,05 ändern würde.
Jede Hilfe wäre dankbar.
Antworten
Für die Geschwindigkeit: Versuchen Sie immer, alles zu vektorisieren, was Sie können, und vermeiden Sie apply
so viel wie möglich.
Hier ist ein schnellerer Weg (Dank an @DavidErickson für sort=False
):
interval = 0.1
source.groupby(np.trunc(source[0] / interval) * interval, sort=False)[1].sum().reset_index()
# out:
0 1
0 0.9 424.0
1 0.8 25.0
2 0.7 12.0
3 0.6 95.0
Der Geschwindigkeitsunterschied kann für große ziemlich dramatisch sein df
.
Versuchen Sie es mit 1 Million Zeilen, gruppiert in 10K-Bins:
source = pd.DataFrame(np.random.normal(scale=1000, size=(int(1e6), 2)))
%%timeit
# ... (as above)
26.7 ms ± 292 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
Mit einem apply
stattdessen:
1.51 s ± 11 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
(50x langsamer).
Sie können eine custom_round
Funktion verwenden, von der ich 3 Änderungen vorgenommen habehttps://stackoverflow.com/a/40372261/6366770::
- Ich habe
np.floor
stattround
wie du runter gehen willst. - Dies bringt Werte durcheinander, die sich am "Rand" eines Fachs befinden, also füge ich hinzu
+ base/100
(so0.9
wäre es0.9 + .009 = 0.909
und runde auf 0,9 statt falsch auf 0,8 ab), so dass es direkt über dem Rand liegt und korrekt abgerundet wird. Ich denke, das wird dich bedecken. Sie können tun1 / 1000
, um sicher zu sein. - Die Antwort, die ich teile, war gesucht
int
, also entferntint
, da wir uns mit runden Schwimmern befassen
source = pd.DataFrame(np.random.normal(scale=1000, size=(int(1e6), 2)))
def custom_round(x, y, base):
return source.groupby((base * np.floor((x + (base / 100)) / base)), sort=False)[y].sum()
%timeit custom_round(source[0], 1, .1)
89.8 ms ± 1.14 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
Auf meinem Computer ist die akzeptierte Antwort langsamer:
102 ms ± 1.86 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)