Czy w związku z tą odpowiedzią istnieje szybki sposób na obliczenie median na podstawie tablicy zawierającej grupy o nierównej liczbie elementów?
Na przykład:
data = [1.00, 1.05, 1.30, 1.20, 1.06, 1.54, 1.33, 1.87, 1.67, ... ]
index = [0, 0, 1, 1, 1, 1, 2, 3, 3, ... ]
Następnie chcę obliczyć różnicę między liczbą a medianą na grupę (np. Mediana grupy 0
jest 1.025
pierwszym wynikiem 1.00 - 1.025 = -0.025
). Tak więc dla powyższej tablicy wyniki wyglądałyby następująco:
result = [-0.025, 0.025, 0.05, -0.05, -0.19, 0.29, 0.00, 0.10, -0.10, ...]
Ponieważ np.median.reduceat
nie istnieje (jeszcze), czy istnieje inny szybki sposób na osiągnięcie tego? Moja tablica będzie zawierać miliony wierszy, więc szybkość ma kluczowe znaczenie!
Można założyć, że indeksy są ciągłe i uporządkowane (łatwo je przekształcić, jeśli nie są).
Przykładowe dane do porównań wydajności:
import numpy as np
np.random.seed(0)
rows = 10000
cols = 500
ngroup = 100
# Create random data and groups (unique per column)
data = np.random.rand(rows,cols)
groups = np.random.randint(ngroup, size=(rows,cols)) + 10*np.tile(np.arange(cols),(rows,1))
# Flatten
data = data.ravel()
groups = groups.ravel()
# Sort by group
idx_sort = groups.argsort()
data = data[idx_sort]
groups = groups[idx_sort]
python
performance
numpy
median
numpy-ufunc
Jean-Paul
źródło
źródło
scipy.ndimage.median
sugestię w połączonej odpowiedzi? Nie wydaje mi się, że potrzebuje takiej samej liczby elementów na etykietę. A może coś przeoczyłem?Odpowiedzi:
Czasami musisz napisać nieidiomatyczny kod numpy, jeśli naprawdę chcesz przyspieszyć swoje obliczenia, czego nie możesz zrobić z natywną numpy.
numba
kompiluje kod Pythona do niskiego poziomu C. Ponieważ sama liczba numpy jest zwykle tak szybka jak C, zwykle jest to przydatne, jeśli twój problem nie nadaje się do natywnej wektoryzacji za pomocą numpy. To jest jeden przykład (gdzie założyłem, że indeksy są ciągłe i posortowane, co znajduje również odzwierciedlenie w przykładowych danych):A oto niektóre czasy z wykorzystaniem
%timeit
magii IPython :Wykorzystując zaktualizowane przykładowe dane w pytaniu, te liczby (tj. Środowisko wykonawcze funkcji python vs. środowisko uruchomieniowe funkcji przyspieszonej przez JIT) są
Odpowiada to przyspieszeniu 65-krotnemu w mniejszym przypadku i 26-krotnym przyspieszeniu w większym przypadku (oczywiście w porównaniu z kodem wolnej pętli) przy użyciu kodu przyspieszonego. Kolejną zaletą jest to, że (w przeciwieństwie do typowej wektoryzacji z natywną numpy) nie potrzebowaliśmy dodatkowej pamięci, aby osiągnąć tę prędkość, chodzi o zoptymalizowany i skompilowany kod niskiego poziomu, który ostatecznie jest uruchamiany.
Powyższa funkcja zakłada, że tablice int numpy są
int64
domyślnie, co w rzeczywistości nie ma miejsca w systemie Windows. Alternatywą jest więc usunięcie podpisu z wywołania donumba.njit
, uruchamiając odpowiednią kompilację just-in-time. Oznacza to jednak, że funkcja zostanie skompilowana podczas pierwszego wykonania, co może wtrącać się w wyniki pomiaru czasu (możemy wykonać funkcję raz ręcznie, używając reprezentatywnych typów danych, lub po prostu zaakceptować, że pierwsze wykonanie pomiaru czasu będzie znacznie wolniejsze, co powinno być zignorowanym). Właśnie tego próbowałem zapobiec, określając podpis, który uruchamia kompilację z wyprzedzeniem.W każdym razie, w przypadku właściwego JIT, dekorator, którego potrzebujemy, jest po prostu
Zauważ, że powyższe czasy, które pokazałem dla funkcji skompilowanej z użyciem Jit, mają zastosowanie tylko po skompilowaniu funkcji. Dzieje się tak w momencie definicji (przy gorącej kompilacji, gdy przekazywany jest wyraźny podpis
numba.njit
) lub podczas pierwszego wywołania funkcji (przy leniwej kompilacji, gdy nie jest przekazywany żaden podpisnumba.njit
). Jeśli funkcja ma zostać wykonana tylko raz, należy również wziąć pod uwagę czas kompilacji dla szybkości tej metody. Zazwyczaj warto kompilować funkcje tylko wtedy, gdy całkowity czas kompilacji + wykonania jest krótszy niż nieskompilowany środowisko wykonawcze (co w rzeczywistości jest prawdą w powyższym przypadku, gdy natywna funkcja python jest bardzo wolna). Dzieje się tak głównie wtedy, gdy wywołujesz skompilowaną funkcję wiele razy.Jak zauważono w komentarzu max9111 , jedną ważną cechą
numba
jestcache
słowo kluczowe tojit
. Przekazaniecache=True
donumba.jit
spowoduje zapisanie skompilowanej funkcji na dysku, dzięki czemu podczas następnego wykonania danego modułu Pythona funkcja zostanie stamtąd załadowana, a nie ponownie skompilowana, co w dłuższej perspektywie może zaoszczędzić ci czasu działania.źródło
index
danych Roganjosha . Zostawię o tym notatkę, dzięki :)cache=True
aby uniknąć ponownej kompilacji przy każdym ponownym uruchomieniu interpretera.Jednym z podejść byłoby użycie
Pandas
tutaj wyłącznie w celu wykorzystaniagroupby
. Podniosłem nieco rozmiary wejściowe, aby lepiej zrozumieć czasy (ponieważ tworzenie DF wiąże się z dodatkowymi kosztami).Daje następujące
timeit
:W przypadku tej samej wielkości próbki podejście Aryerez brzmi :
Jeśli jednak zwiększymy nakłady o kolejny współczynnik 10, czasy stają się:
Jednak kosztem pewnego reability, odpowiedź przez Divakar użyciu czystego numpy znalazł się na:
W świetle nowego zestawu danych (który naprawdę powinien był zostać ustawiony na początku):
źródło
Może już to zrobiłeś, ale jeśli nie, sprawdź, czy to wystarczająco szybko:
Wynik:
źródło
np.vectorize
jest bardzo cienkie opakowanie na pętlę, więc nie spodziewałbym się, że to podejście będzie szczególnie szybkie.data
iindex
taknp.array
jak w pytaniu.Oto podejście oparte na NumPy, aby uzyskać medianę binned dla dodatnich wartości bin / wartości indeksu -
Aby rozwiązać nasz konkretny przypadek odejmowanych -
źródło
df.groupby('index').transform('median')
?