Obecnie pracuję nad algorytmem do implementacji kroczącego filtru mediany (analogicznego do kroczącego filtru średniej) w C. Z moich poszukiwań w literaturze wynika, że istnieją dwa racjonalnie efektywne sposoby na zrobienie tego. Pierwszym jest posortowanie początkowego okna wartości, a następnie wykonanie wyszukiwania binarnego w celu wstawienia nowej wartości i usunięcia istniejącej przy każdej iteracji.
Drugi (z Hardle i Steiger, 1995, JRSS-C, Algorithm 296) buduje dwustronną strukturę sterty, z maxheap na jednym końcu, minheap na drugim i medianą w środku. Daje to algorytm czasu liniowego zamiast algorytmu O (n log n).
Oto mój problem: wdrożenie tego pierwszego jest wykonalne, ale muszę to uruchomić na milionach szeregów czasowych, więc wydajność ma duże znaczenie. To ostatnie okazuje się bardzo trudne do wdrożenia. Znalazłem kod w pliku Trunmed.c kodu pakietu statystycznego R, ale jest on raczej nieczytelny.
Czy ktoś wie o dobrze napisanej implementacji C dla liniowego algorytmu mediany kroczącej w czasie?
Edytuj: Link do kodu Trunmed.c http://google.com/codesearch/p?hl=en&sa=N&cd=1&ct=rc#mYw3h_Lb_e0/R-2.2.0/src/library/stats/src/Trunmed.c
Odpowiedzi:
Patrzyłem na R
src/library/stats/src/Trunmed.c
kilka razy, ponieważ chciałem też czegoś podobnego w samodzielnej podprocedurze klasy C ++ / C. Zauważ, że są to właściwie dwie implementacje w jednej, zobaczsrc/library/stats/man/runmed.Rd
(źródło pliku pomocy), który mówiByłoby miło zobaczyć to ponownie użyte w bardziej samodzielny sposób. Czy jesteś wolontariuszem? Mogę pomóc z niektórymi bitami R.
Edycja 1 : Oprócz linku do starszej wersji Trunmed.c powyżej, tutaj są aktualne kopie SVN
Srunmed.c
(dla wersji Stuetzle)Trunmed.c
(dla wersji Turlach)runmed.R
dla funkcji R wywołującej teEdycja 2 : Ryan Tibshirani ma trochę kodu C i Fortran na temat szybkiego binowania mediany, co może być odpowiednim punktem wyjścia dla podejścia okienkowego.
źródło
Nie mogłem znaleźć nowoczesnej implementacji struktury danych c ++ ze statystyką zamówień, więc ostatecznie zaimplementowałem oba pomysły w linku do najlepszych programistów sugerowanym przez MAK ( Match Editorial : przewiń w dół do FloatingMedian).
Dwa zestawy multisetowe
Pierwsza idea dzieli dane na dwie struktury danych (sterty, zestawy wielozbiorowe itp.) Z O (ln N) na wstawianie / usuwanie nie pozwala na dynamiczną zmianę kwantyla bez dużych kosztów. Oznacza to, że możemy mieć kroczącą medianę lub kroczące 75%, ale nie obie jednocześnie.
Drzewo segmentów
Drugi pomysł wykorzystuje drzewo segmentów, które jest O (ln N) do wstawiania / usuwania / zapytań, ale jest bardziej elastyczne. Najlepsze ze wszystkich „N” to rozmiar zakresu danych. Więc jeśli twoja krocząca mediana ma okno miliona elementów, ale twoje dane wahają się od 1..65536, wtedy tylko 16 operacji jest wymaganych na ruch przesuwanego okna 1 miliona !!
Kod C ++ jest podobny do tego, co Denis opublikował powyżej („Oto prosty algorytm dla skwantyzowanych danych”)
Drzewa statystyk porządkowych GNU
Tuż przed poddaniem się stwierdziłem, że stdlibc ++ zawiera drzewa statystyk zamówień !!!
Mają dwie krytyczne operacje:
Zobacz podręcznik libstdc ++ policy_based_data_structures_test (wyszukaj „podziel i dołącz”).
Zapakowałem drzewo do użycia w wygodnym nagłówku dla kompilatorów obsługujących częściowe typy plików typu c ++ 0x / c ++ 11:
źródło
Zrobiłem realizację C tutaj . W tym pytaniu jest jeszcze kilka szczegółów: Mediana krocząca w implementacji C - Turlach .
Przykładowe użycie:
źródło
Używam tego przyrostowego estymatora mediany:
który ma taką samą postać jak bardziej powszechny estymator średniej:
Tutaj eta jest małym parametrem szybkości uczenia się (np.
0.001
) Isgn()
jest funkcją signum, która zwraca jedną z{-1, 0, 1}
. (Użyj stałejeta
takiej jak ta, jeśli dane są niestacjonarne i chcesz śledzić zmiany w czasie; w przeciwnym razie w przypadku źródeł stacjonarnych użyj czegoś podobnegoeta = 1 / n
do zbieżności, gdzien
jest liczba próbek widzianych do tej pory).Zmodyfikowałem również estymator mediany, aby działał dla dowolnych kwantyli. Ogólnie rzecz biorąc, funkcja kwantylowa podaje wartość, która dzieli dane na dwa ułamki:
p
i1 - p
. Następujący szacuje tę wartość w sposób przyrostowy:Wartość
p
powinna mieścić się w granicach[0, 1]
. To zasadniczo przesuwasgn()
symetryczne wyjście funkcji,{-1, 0, 1}
aby pochyliło się w jedną stronę, dzieląc próbki danych na dwa pojemniki o nierównej wielkości (odpowiednio ułamkip
i1 - p
dane są mniejsze / większe niż oszacowanie kwantylowe). Zauważ, że dlap = 0.5
, to sprowadza się do estymatora mediany.źródło
Oto prosty algorytm dla skwantowanych danych (miesiące później):
źródło
Medianę kroczącą można znaleźć, zachowując dwie partycje liczb.
Do obsługi partycji użyj Min Heap i Max Heap.
Max Heap będzie zawierał liczby mniejsze niż równe medianie.
Sterta minimalna będzie zawierała liczby większe niż równe medianie.
Wiązanie równoważące: jeśli całkowita liczba elementów jest parzysta, obie sterty powinny mieć równe elementy.
jeśli całkowita liczba elementów jest nieparzysta, wówczas Max Heap będzie miał o jeden element więcej niż Min Heap.
Element mediany: jeśli obie partycje mają równą liczbę elementów, mediana będzie równa połowie sumy elementu maksymalnego z pierwszej partycji i elementu minimalnego z drugiej partycji.
W przeciwnym razie mediana będzie maksymalnym elementem z pierwszej partycji.
źródło
Może warto zauważyć, że istnieje szczególny przypadek, który ma proste, dokładne rozwiązanie: kiedy wszystkie wartości w strumieniu są liczbami całkowitymi w (stosunkowo) małym zdefiniowanym zakresie. Na przykład załóżmy, że wszystkie muszą mieścić się w przedziale od 0 do 1023. W tym przypadku po prostu zdefiniuj tablicę 1024 elementów i liczbę i wyczyść wszystkie te wartości. Dla każdej wartości w strumieniu zwiększ odpowiedni pojemnik i liczbę. Po zakończeniu strumienia znajdź przedział, który zawiera największą liczbę zliczeń / 2 - łatwo to zrobić dodając kolejne przedziały, zaczynając od 0. W ten sam sposób można znaleźć wartość dowolnego rzędu. (Występuje niewielka komplikacja, jeśli wykrycie nasycenia zasobnika i „uaktualnienie” rozmiaru pojemników pamięci do większego typu będzie potrzebne podczas przebiegu).
Ten szczególny przypadek może wydawać się sztuczny, ale w praktyce jest bardzo powszechny. Można go również zastosować jako przybliżenie liczb rzeczywistych, jeśli mieszczą się one w zakresie i znany jest „dostatecznie dobry” poziom dokładności. Potwierdziłoby to prawie każdy zestaw pomiarów na grupie obiektów „świata rzeczywistego”. Na przykład wzrost lub waga grupy osób. Nie jest wystarczająco duży zestaw? Działałoby to równie dobrze w przypadku długości lub wagi wszystkich (pojedynczych) bakterii na planecie - zakładając, że ktoś mógłby dostarczyć dane!
Wygląda na to, że źle odczytałem oryginał - który wydaje się, że chce mieć przesuwaną środkową część okna zamiast tylko mediany bardzo długiego strumienia. To podejście nadal się sprawdza. Załaduj wartości pierwszego strumienia N dla okna początkowego, a następnie dla wartości strumienia N + 1 zwiększaj odpowiedni pojemnik, zmniejszając jednocześnie pojemnik odpowiadający zerowej wartości strumienia. W tym przypadku konieczne jest zachowanie ostatnich wartości N, aby umożliwić dekrementację, co można zrobić efektywnie poprzez cykliczne adresowanie tablicy o rozmiarze N. Ponieważ pozycja mediany może się zmieniać tylko o -2, -1,0,1 , 2 na każdym kroku przesuwanego okna nie jest konieczne sumowanie wszystkich koszy do mediany na każdym kroku, wystarczy dostosować „wskaźnik mediany” w zależności od tego, która strona (e) została zmodyfikowana. Na przykład, jeśli zarówno nowa wartość, jak i usuwana wartość spadną poniżej bieżącej mediany, to się nie zmienia (przesunięcie = 0). Metoda nie działa, gdy N staje się zbyt duże, aby wygodnie przechowywać je w pamięci.
źródło
Jeśli masz możliwość odniesienia się do wartości jako funkcji punktów w czasie, możesz próbkować wartości z zamianą, stosując metodę ładowania początkowego, aby wygenerować początkową wartość mediany w przedziałach ufności. Może to umożliwić obliczenie przybliżonej mediany z większą wydajnością niż ciągłe sortowanie przychodzących wartości w strukturę danych.
źródło
Dla tych, którzy potrzebują bieżącej mediany w Javie ... PriorityQueue jest Twoim przyjacielem. O (log N) wstaw, O (1) bieżąca mediana i O (N) usuń. Jeśli znasz dystrybucję swoich danych, możesz zrobić o wiele lepiej.
źródło
}), higher = new PriorityQueue<Integer>();
lubnew PriorityQueue<Integer>(10,
. Nie mogłem uruchomić kodu.Oto jeden, którego można użyć, gdy dokładny wynik nie jest ważny (do celów wyświetlania itp.) Potrzebujesz totalcount i lastmedian plus newvalue.
Daje dość dokładne wyniki dla rzeczy takich jak page_display_time.
Reguły: strumień wejściowy musi być płynny w kolejności czasu wyświetlania strony, mieć dużą liczbę (> 30 itd.) I mieć niezerową medianę.
Przykład: czas ładowania strony, 800 elementów, 10 ms ... 3000 ms, średnio 90 ms, rzeczywista mediana: 11 ms
Po 30 danych wejściowych błąd mediany wynosi zwykle <= 20% (9 ms..12 ms) i jest coraz mniejszy. Po 800 wejściach błąd wynosi + -2%.
Inny myśliciel z podobnym rozwiązaniem jest tutaj: Median Filter Super wydajna implementacja
źródło
Oto implementacja Java
źródło
Jeśli potrzebujesz tylko wygładzonej średniej, szybkim / łatwym sposobem jest pomnożenie ostatniej wartości przez x, a wartość średnią przez (1-x), a następnie dodanie ich. To staje się nową średnią.
edycja: nie to, o co prosił użytkownik i nie jest tak statystycznie ważne, ale wystarczająco dobre do wielu zastosowań.
Zostawię to tutaj (pomimo głosów przeciw) do wyszukiwania!
źródło