Je travaille actuellement sur un algorithme pour implémenter un filtre médian roulant (analogue à un filtre à moyenne mobile) en C. D'après ma recherche dans la littérature, il semble y avoir deux façons raisonnablement efficaces de le faire. La première consiste à trier la fenêtre initiale de valeurs, puis à effectuer une recherche binaire pour insérer la nouvelle valeur et supprimer l'existante à chaque itération.
Le second (de Hardle et Steiger, 1995, JRSS-C, Algorithme 296) construit une structure de tas à deux extrémités, avec un maxheap à une extrémité, un minheap à l'autre et la médiane au milieu. Cela donne un algorithme en temps linéaire au lieu d'un algorithme O (n log n).
Voici mon problème: mettre en œuvre le premier est faisable, mais je dois l'exécuter sur des millions de séries chronologiques, donc l'efficacité compte beaucoup. Ce dernier s'avère très difficile à mettre en œuvre. J'ai trouvé du code dans le fichier Trunmed.c du code du package stats de R, mais il est plutôt indéchiffrable.
Est-ce que quelqu'un connaît une implémentation C bien écrite pour l'algorithme de médiane linéaire en temps glissant?
Modifier: Lien vers le code 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
Réponses:
J'ai regardé les R à
src/library/stats/src/Trunmed.c
quelques reprises car je voulais aussi quelque chose de similaire dans un sous-programme autonome de classe / C C ++. Notez qu'il s'agit en fait de deux implémentations en une, voirsrc/library/stats/man/runmed.Rd
(la source du fichier d'aide) qui ditCe serait bien de le voir réutilisé de manière plus autonome. Faites-vous du bénévolat? Je peux vous aider avec certains des bits R.
Edit 1 : Outre le lien vers l'ancienne version de Trunmed.c ci-dessus, voici les copies SVN actuelles de
Srunmed.c
(pour la version Stuetzle)Trunmed.c
(pour la version Turlach)runmed.R
pour la fonction R appelant cesEdit 2 : Ryan Tibshirani a du code C et Fortran sur le binning médian rapide qui peut être un point de départ approprié pour une approche fenêtrée.
la source
Je n'ai pas pu trouver une implémentation moderne d'une structure de données c ++ avec des statistiques d'ordre et j'ai donc fini par implémenter les deux idées dans le lien des meilleurs codeurs suggéré par MAK ( Match Editorial : faites défiler jusqu'à FloatingMedian).
Deux multisets
La première idée partitionne les données en deux structures de données (tas, multisets, etc.) avec O (ln N) par insertion / suppression ne permet pas de modifier dynamiquement le quantile sans un coût élevé. C'est-à-dire que nous pouvons avoir une médiane glissante, ou 75% glissants, mais pas les deux en même temps.
Arborescence des segments
La deuxième idée utilise un arbre de segments qui est O (ln N) pour les insertions / suppressions / requêtes mais qui est plus flexible. Mieux encore, le "N" est la taille de votre plage de données. Donc, si votre médiane glissante a une fenêtre d'un million d'éléments, mais que vos données varient de 1..65536, alors seulement 16 opérations sont nécessaires par mouvement de la fenêtre glissante de 1 million !!
Le code c ++ est similaire à ce que Denis a posté ci-dessus ("Voici un algorithme simple pour les données quantifiées")
Arbres statistiques de commande GNU
Juste avant d'abandonner, j'ai trouvé que stdlibc ++ contient des arbres de statistiques d'ordre !!!
Ceux-ci ont deux opérations critiques:
Voir le manuel de libstdc ++ policy_based_data_structures_test (recherchez "split and join").
J'ai enveloppé l'arborescence pour l'utiliser dans un en-tête pratique pour les compilateurs prenant en charge les typedefs partiels de style c ++ 0x / c ++ 11:
la source
J'ai fait une implémentation C ici . Quelques détails supplémentaires sont dans cette question: Médiane mobile dans l'implémentation C - Turlach .
Exemple d'utilisation:
la source
J'utilise cet estimateur médian incrémental:
qui a la même forme que l'estimateur moyen le plus courant:
Ici, eta est un petit paramètre de taux d'apprentissage (par exemple
0.001
), etsgn()
est la fonction signum qui renvoie l'un des{-1, 0, 1}
. (Utilisez une constanteeta
comme celle-ci si les données ne sont pas stationnaires et que vous souhaitez suivre les changements au fil du temps; sinon, pour les sources stationnaires, utilisez quelque chose commeeta = 1 / n
pour converger, oùn
est le nombre d'échantillons vus jusqu'à présent.)De plus, j'ai modifié l'estimateur médian pour qu'il fonctionne pour des quantiles arbitraires. En général, une fonction quantile vous indique la valeur qui divise les données en deux fractions:
p
et1 - p
. Ce qui suit estime cette valeur de manière incrémentielle:La valeur
p
doit être à l'intérieur[0, 1]
. Cela déplace essentiellement lasgn()
sortie symétrique de la fonction{-1, 0, 1}
vers un côté, en partitionnant les échantillons de données en deux bacs de taille inégale (les fractionsp
et1 - p
des données sont inférieures / supérieures à l'estimation quantile, respectivement). Notez que pourp = 0.5
, cela se réduit à l'estimateur médian.la source
Voici un algorithme simple pour les données quantifiées (des mois plus tard):
la source
La médiane mobile peut être trouvée en conservant deux partitions de nombres.
Pour maintenir les partitions, utilisez Min Heap et Max Heap.
Max Heap contiendra des nombres inférieurs à la médiane.
Min Heap contiendra des nombres supérieurs à la médiane.
Contrainte d'équilibrage: si le nombre total d'éléments est pair, les deux tas doivent avoir des éléments égaux.
si le nombre total d'éléments est impair, Max Heap aura un élément de plus que Min Heap.
Élément médian: Si les deux partitions ont un nombre égal d'éléments, la médiane sera la moitié de la somme de l'élément max de la première partition et de l'élément min de la deuxième partition.
Sinon, la médiane sera l'élément maximum de la première partition.
la source
Il est peut-être intéressant de souligner qu'il existe un cas particulier qui a une solution exacte simple: lorsque toutes les valeurs du flux sont des entiers dans une plage définie (relativement) petite. Par exemple, supposons qu'ils doivent tous être compris entre 0 et 1023. Dans ce cas, définissez simplement un tableau de 1024 éléments et un nombre, et effacez toutes ces valeurs. Pour chaque valeur du flux, incrémentez le bac correspondant et le nombre. Une fois le flux terminé, trouvez le bac qui contient la valeur count / 2 la plus élevée - facilement accompli en ajoutant des bacs successifs à partir de 0. En utilisant la même méthode, la valeur d'un ordre de classement arbitraire peut être trouvée. (Il y a une complication mineure si la détection de la saturation du bac et la «mise à niveau» de la taille des bacs de stockage vers un type plus grand pendant une analyse sont nécessaires.)
Ce cas particulier peut sembler artificiel, mais en pratique, il est très courant. Il peut également être appliqué comme une approximation pour les nombres réels s'ils se situent dans une plage et qu'un niveau de précision «assez bon» est connu. Cela vaut pour à peu près n'importe quel ensemble de mesures sur un groupe d'objets «du monde réel». Par exemple, les hauteurs ou les poids d'un groupe de personnes. Pas un ensemble assez grand? Cela fonctionnerait tout aussi bien pour la longueur ou le poids de toutes les bactéries (individuelles) de la planète - en supposant que quelqu'un puisse fournir les données!
Il semble que j'ai mal lu l'original - ce qui semble vouloir une médiane de fenêtre glissante au lieu de la médiane d'un très long flux. Cette approche fonctionne toujours pour cela. Chargez les N premières valeurs de flux pour la fenêtre initiale, puis pour la valeur de N + 1ème flux, incrémentez le bac correspondant tout en décrémentant le bac correspondant à la 0ème valeur de flux. Il faut dans ce cas retenir les N dernières valeurs pour permettre la décrémentation, ce qui peut être fait efficacement en adressant cycliquement un tableau de taille N. Puisque la position de la médiane ne peut changer que de -2, -1,0,1 , 2 à chaque étape de la fenêtre glissante, il n'est pas nécessaire de faire la somme de toutes les cases jusqu'à la médiane à chaque étape, il suffit d'ajuster le "pointeur médian" en fonction du (des) côté (s) des cases modifiées. Par exemple, si la nouvelle valeur et celle supprimée tombent en dessous de la médiane actuelle, cela ne change pas (offset = 0). La méthode échoue lorsque N devient trop grand pour être conservé en mémoire.
la source
Si vous avez la possibilité de référencer des valeurs en fonction de points dans le temps, vous pouvez échantillonner des valeurs avec remplacement, en appliquant le bootstrap pour générer une valeur médiane bootstrap dans les intervalles de confiance. Cela peut vous permettre de calculer une médiane approximative avec une plus grande efficacité que de trier constamment les valeurs entrantes dans une structure de données.
la source
Pour ceux qui ont besoin d'une médiane en cours d'exécution en Java ... PriorityQueue est votre ami. Insertion O (log N), médiane actuelle O (1) et suppression O (N). Si vous connaissez la distribution de vos données, vous pouvez faire beaucoup mieux que cela.
la source
}), higher = new PriorityQueue<Integer>();
ounew PriorityQueue<Integer>(10,
. Je n'ai pas pu exécuter le code.En voici un qui peut être utilisé lorsque la sortie exacte n'est pas importante (à des fins d'affichage, etc.) Vous avez besoin de totalcount et lastmedian, plus la newvalue.
Produit des résultats assez exacts pour des choses comme page_display_time.
Règles: le flux d'entrée doit être fluide dans l'ordre du temps d'affichage de la page, grand en nombre (> 30, etc.), et avoir une médiane non nulle.
Exemple: temps de chargement de la page, 800 éléments, 10 ms ... 3000 ms, moyenne 90 ms, médiane réelle: 11 ms
Après 30 entrées, l'erreur médiane est généralement <= 20% (9ms..12ms), et devient de moins en moins. Après 800 entrées, l'erreur est de + -2%.
Un autre penseur avec une solution similaire est ici: Filtre médian Mise en œuvre super efficace
la source
Voici l'implémentation java
la source
Si vous avez juste besoin d'une moyenne lissée, un moyen rapide / facile consiste à multiplier la dernière valeur par x et la valeur moyenne par (1-x), puis les ajouter. Cela devient alors la nouvelle moyenne.
edit: Pas ce que l'utilisateur a demandé et pas aussi statistiquement valide mais assez bon pour de nombreuses utilisations.
Je vais le laisser ici (malgré les votes négatifs) pour la recherche!
la source