Actualmente estoy trabajando en un algoritmo para implementar un filtro de mediana variable (análogo a un filtro de media variable) en C. A partir de mi búsqueda en la literatura, parece haber dos formas razonablemente eficientes de hacerlo. La primera es ordenar la ventana inicial de valores, luego realizar una búsqueda binaria para insertar el nuevo valor y eliminar el existente en cada iteración.
El segundo (de Hardle y Steiger, 1995, JRSS-C, algoritmo 296) construye una estructura de montón de dos extremos, con un maxheap en un extremo, un minheap en el otro y la mediana en el medio. Esto produce un algoritmo de tiempo lineal en lugar de uno que es O (n log n).
Aquí está mi problema: implementar el primero es factible, pero necesito ejecutarlo en millones de series de tiempo, por lo que la eficiencia importa mucho. Este último está resultando muy difícil de implementar. Encontré código en el archivo Trunmed.c del código del paquete de estadísticas de R, pero es bastante indescifrable.
¿Alguien sabe de una implementación de C bien escrita para el algoritmo de la mediana móvil del tiempo lineal?
Editar: enlace al código de 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
Respuestas:
He mirado las R
src/library/stats/src/Trunmed.c
varias veces porque también quería algo similar en una subrutina de clase / C independiente de C ++. Tenga en cuenta que en realidad se trata de dos implementaciones en una, consultesrc/library/stats/man/runmed.Rd
(la fuente del archivo de ayuda) que diceSería bueno ver esto reutilizado de una manera más independiente. ¿Eres voluntario? Puedo ayudar con algunos de los bits R.
Edición 1 : además del enlace a la versión anterior de Trunmed.c anterior, aquí hay copias actuales de SVN de
Srunmed.c
(para la versión Stuetzle)Trunmed.c
(para la versión Turlach)runmed.R
para la función R llamando a estosEdición 2 : Ryan Tibshirani tiene algo de código C y Fortran en el binning mediano rápido que puede ser un punto de partida adecuado para un enfoque de ventana.
fuente
No pude encontrar una implementación moderna de una estructura de datos c ++ con orden-estadística, así que terminé implementando ambas ideas en el enlace de codificadores superiores sugerido por MAK ( Match Editorial : desplácese hacia abajo hasta FloatingMedian).
Dos conjuntos múltiples
La primera idea divide los datos en dos estructuras de datos (montones, conjuntos múltiples, etc.) con O (ln N) por inserción / eliminación no permite que el cuantil se cambie dinámicamente sin un gran costo. Es decir, podemos tener una mediana móvil o un 75% móvil, pero no ambos al mismo tiempo.
Árbol de segmentos
La segunda idea utiliza un árbol de segmentos que es O (ln N) para insertar / eliminar / consultas, pero es más flexible. Lo mejor de todo es que la "N" es el tamaño de su rango de datos. Entonces, si su mediana móvil tiene una ventana de un millón de elementos, pero sus datos varían de 1..65536, ¡entonces solo se requieren 16 operaciones por movimiento de la ventana móvil de 1 millón!
El código c ++ es similar a lo que Denis publicó anteriormente ("Aquí hay un algoritmo simple para datos cuantificados")
Árboles de estadísticas de orden GNU
Justo antes de rendirme, descubrí que stdlibc ++ contiene árboles de estadísticas de orden.
Estos tienen dos operaciones críticas:
Consulte el manual de libstdc ++ policy_based_data_structures_test (busque "dividir y unir").
He envuelto el árbol para usarlo en un encabezado de conveniencia para compiladores que admiten definiciones de tipo parciales de estilo c ++ 0x / c ++ 11:
fuente
He hecho una implementación de C aquí . Algunos detalles más se encuentran en esta pregunta: Mediana móvil en C - Implementación de Turlach .
Uso de muestra:
fuente
Utilizo este estimador de mediana incremental:
que tiene la misma forma que el estimador de medias más común:
Aquí eta es un pequeño parámetro de tasa de aprendizaje (por ejemplo
0.001
), ysgn()
es la función signum que devuelve uno de{-1, 0, 1}
. (Use una constanteeta
como esta si los datos no son estacionarios y desea realizar un seguimiento de los cambios a lo largo del tiempo; de lo contrario, para fuentes estacionarias use algo comoeta = 1 / n
converger, donden
está la cantidad de muestras vistas hasta ahora).Además, modifiqué el estimador de la mediana para que funcione para cuantiles arbitrarios. En general, una función de cuantiles le dice el valor que divide los datos en dos fracciones:
p
y1 - p
. Lo siguiente estima este valor de forma incremental:El valor
p
debe estar dentro[0, 1]
. Esto esencialmente desplaza lasgn()
salida simétrica de la función{-1, 0, 1}
para inclinarse hacia un lado, dividiendo las muestras de datos en dos contenedores de tamaño desigual (las fraccionesp
y1 - p
los datos son menores / mayores que la estimación cuantílica, respectivamente). Tenga en cuenta que parap = 0.5
, esto se reduce al estimador mediano.fuente
Aquí hay un algoritmo simple para datos cuantificados (meses después):
fuente
La mediana móvil se puede encontrar manteniendo dos particiones de números.
Para mantener las particiones, use Min Heap y Max Heap.
Max Heap contendrá números menores que iguales a la mediana.
Min Heap contendrá números mayores que iguales a la mediana.
Restricción de equilibrio: si el número total de elementos es par, ambos montones deben tener elementos iguales.
si el número total de elementos es impar, Max Heap tendrá un elemento más que Min Heap.
Elemento mediano: si ambas particiones tienen el mismo número de elementos, la mediana será la mitad de la suma del elemento máximo de la primera partición y el elemento mínimo de la segunda partición.
De lo contrario, la mediana será el elemento máximo de la primera partición.
fuente
Quizás valga la pena señalar que hay un caso especial que tiene una solución exacta simple: cuando todos los valores en la secuencia son números enteros dentro de un rango definido (relativamente) pequeño. Por ejemplo, suponga que todos deben estar entre 0 y 1023. En este caso, simplemente defina una matriz de 1024 elementos y un recuento, y borre todos estos valores. Para cada valor en la secuencia, incremente el contenedor correspondiente y el recuento. Una vez finalizada la transmisión, busque el contenedor que contenga el valor más alto de count / 2, lo que se logra fácilmente agregando contenedores sucesivos comenzando desde 0. Usando el mismo método, se puede encontrar el valor de un orden de rango arbitrario. (Existe una complicación menor si se necesita detectar la saturación del contenedor y "actualizar" el tamaño de los contenedores de almacenamiento a un tipo más grande durante una ejecución).
Este caso especial puede parecer artificial, pero en la práctica es muy común. También se puede aplicar como una aproximación de números reales si se encuentran dentro de un rango y se conoce un nivel de precisión "suficientemente bueno". Esto sería válido para prácticamente cualquier conjunto de medidas en un grupo de objetos del "mundo real". Por ejemplo, las alturas o pesos de un grupo de personas. ¿No es un conjunto lo suficientemente grande? Funcionaría igual de bien para la longitud o el peso de todas las bacterias (individuales) del planeta, ¡asumiendo que alguien pudiera proporcionar los datos!
Parece que leí mal el original, que parece que quiere una mediana de ventana deslizante en lugar de solo la mediana de una secuencia muy larga. Este enfoque todavía funciona para eso. Cargue los primeros N valores de flujo para la ventana inicial, luego, para el valor N + 1 ° flujo, incremente el contenedor correspondiente mientras disminuye el contenedor correspondiente al valor 0 de flujo. En este caso, es necesario retener los últimos valores de N para permitir la disminución, que se puede hacer de manera eficiente direccionando cíclicamente una matriz de tamaño N. Dado que la posición de la mediana solo puede cambiar en -2, -1,0,1 , 2 en cada paso de la ventana deslizante, no es necesario sumar todos los bins hasta la mediana en cada paso, simplemente ajuste el "puntero mediano" dependiendo de qué lado (s) se modificaron los bins. Por ejemplo, si tanto el nuevo valor como el que se está eliminando caen por debajo de la mediana actual, entonces no cambia (compensación = 0). El método falla cuando N se vuelve demasiado grande para guardarlo convenientemente en la memoria.
fuente
Si tiene la capacidad de hacer referencia a valores en función de puntos en el tiempo, puede muestrear valores con reemplazo, aplicando bootstrapping para generar un valor medio de bootstrap dentro de intervalos de confianza. Esto puede permitirle calcular una mediana aproximada con mayor eficiencia que ordenar constantemente los valores entrantes en una estructura de datos.
fuente
Para aquellos que necesitan una mediana en ejecución en Java ... PriorityQueue es su amigo. O (log N) insertar, O (1) mediana actual y O (N) eliminar. Si conoce la distribución de sus datos, puede hacerlo mucho mejor.
fuente
}), higher = new PriorityQueue<Integer>();
onew PriorityQueue<Integer>(10,
. No pude ejecutar el código.Aquí hay uno que se puede usar cuando la salida exacta no es importante (con fines de visualización, etc.). Necesita totalcount y lastmedian, más el newvalue.
Produce resultados bastante exactos para cosas como page_display_time.
Reglas: el flujo de entrada debe ser fluido en el orden del tiempo de visualización de la página, contar con un gran recuento (> 30, etc.) y tener una mediana distinta de cero.
Ejemplo: tiempo de carga de la página, 800 elementos, 10 ms ... 3000 ms, promedio de 90 ms, mediana real: 11 ms
Después de 30 entradas, el error de mediana es generalmente <= 20% (9ms..12ms) y es cada vez menor. Después de 800 entradas, el error es + -2%.
Otro pensador con una solución similar está aquí: Median Filter Implementación súper eficiente
fuente
Aquí está la implementación de Java
fuente
Si solo necesita un promedio suavizado, una forma rápida / fácil es multiplicar el último valor por x y el valor promedio por (1-x) y luego agregarlos. Este se convierte entonces en el nuevo promedio.
editar: No es lo que pidió el usuario y no es estadísticamente válido, pero lo suficientemente bueno para muchos usos.
¡Lo dejaré aquí (a pesar de los votos negativos) para buscar!
fuente