Estimación en línea de cuartiles sin almacenar observaciones

13

Necesito calcular cuartiles (Q1, mediana y Q3) en tiempo real en un gran conjunto de datos sin almacenar las observaciones. Primero probé el algoritmo P cuadrado (Jain / Chlamtac) pero no estaba satisfecho (demasiado uso de CPU y no me convenció la precisión al menos en mi conjunto de datos).

Ahora uso el algoritmo FAME ( Feldman / Shavitt ) para estimar la mediana sobre la marcha e intento derivar el algoritmo para calcular también Q1 y Q3:

M = Q1 = Q3 = first data value 
step =step_Q1 = step_Q3 = a small value
for each new data :
        # update median M 
        if M > data:
            M = M - step
        elif M < data:
            M = M + step
        if abs(data-M) < step:
            step = step /2

        # estimate Q1 using M
        if data < M:
            if Q1 > data:
                Q1 = Q1 - step_Q1
            elif Q1 < data:
                Q1 = Q1 + step_Q1
            if abs(data - Q1) < step_Q1:
                step_Q1 = step_Q1/2
        # estimate Q3 using M
        elif data > M:
            if Q3 > data:
                Q3 = Q3 - step_Q3
            elif Q3 < data:
                Q3 = Q3 + step_Q3
            if abs(data-Q3) < step_Q3:
                step_Q3 = step_Q3 /2

Para resumir, simplemente usa la mediana M obtenida sobre la marcha para dividir el conjunto de datos en dos y luego reutilizar el mismo algoritmo para Q1 y Q3.

Esto parece funcionar de alguna manera, pero no puedo demostrarlo (no soy matemático). ¿Es defectuoso? Agradecería cualquier sugerencia o eventual otra técnica que se ajuste al problema.

Muchas gracias por su ayuda !

==== EDITAR =====

Para aquellos que estén interesados ​​en tales preguntas, después de algunas semanas, finalmente terminé simplemente usando Reservoir Sampling con un revervoir de 100 valores y me dio resultados muy satisfactorios (para mí).

Louis Hugues
fuente
¿Está buscando una prueba de que Q1 y Q2 convergen con los verdaderos cuantiles a medida que aumenta el número de ejemplos de manera similar al análisis de la cadena de Markov en las diapositivas que vinculó? En términos de implementación, el algoritmo anterior no parece defectuoso (probé los cuantiles aproximados para normal normal en R y el algoritmo funciona bien).
Theja
1
@Theja gracias, no estoy buscando una prueba (demasiado trabajo) sino meramente consejos y comentarios. El principal problema que veo es basar el cálculo en la estimación de la mediana, como ha señalado Whuber.
Louis Hugues

Respuestas:

3

La mediana es el punto en el que la mitad de las observaciones caen debajo y la mitad arriba. Del mismo modo, el vigésimo quinto percentil es la mediana de los datos entre el mínimo y la mediana, y el percentil 75 es la mediana entre la mediana y el máximo, así que sí, creo que estás en tierra firme aplicando cualquier algoritmo de mediana que uses primero todo el conjunto de datos para particionarlo, y luego en las dos piezas resultantes.

Actualización :

Esta pregunta sobre stackoverflow lleva a este artículo: Raj Jain, Imrich Chlamtac: El algoritmo P² para el cálculo dinámico de cuantiiles e histogramas sin almacenar observaciones. Commun. ACM 28 (10): 1076-1085 (1985) cuyo resumen indica que probablemente sea de gran interés para usted:

Se propone un algoritmo heurístico para el cálculo dinámico de la mediana y otros cuantiles. Las estimaciones se producen dinámicamente a medida que se generan las observaciones. Las observaciones no se almacenan; por lo tanto, el algoritmo tiene un requisito de almacenamiento muy pequeño y fijo, independientemente del número de observaciones. Esto lo hace ideal para implementar en un chip cuantil que se puede usar en controladores y grabadoras industriales. El algoritmo se extiende aún más al trazado del histograma. Se analiza la precisión del algoritmo.

Abraham
fuente
44
Esta respuesta pasa por alto dos puntos sutiles, uno sin importancia pero el otro posiblemente muy importante. La sin importancia es que la técnica de doble división calcula las bisagras superior e inferior que pueden diferir ligeramente de la mediana, dependiendo del tamaño de la muestra. La importante es que la división doble parece estar basada en una estimación continua de la mediana. Cualquier variación entre esta estimación y la mediana real hará que las bisagras varíen también. Intuitivamente, esto no debería ser un problema ya que la cantidad de datos aumenta, pero es un problema que necesita un análisis.
whuber
¿Estimar directamente los cuartiles no estaría sujeto a problemas similares? La estimación directa dividiría los puntos de datos en una relación . Esto divide los elementos en y luego toma uno de esos "2" y lo divide en . No soy un teórico, cierto, pero, en general, ¿la diferencia entre los dos no sería diferente en un punto a la izquierda o la derecha y convergería a medida que aumenta? Sí, se podría crear una distribución patológica, pero eso también sufriría una estimación mediana directa. Obviamente, almacenar todos los valores es mejor, por supuesto. 1 : 3 2 : 2 1 : 1 nn1:32:21:1n
Avraham
2
@Avraham, gracias por señalar el documento, como mencioné, ya probé el algoritmo P-cuadrado de Chain y Chlamtac. en mi conjunto de datos, el algo que describí da un mejor resultado (MSE) y es más rápido. Así que, sin embargo, estaba cuestionando si podría tener algún problema. Como whuber comentó que el hecho de que usa una estimación corriente es un problema potencial; pero no sé si es realmente importante o no.
Louis Hugues
Vaya, lo vi y lo olvidé. Mis disculpas.
Avraham
0

Un cambio muy leve en el método que publicó y puede calcular cualquier percentil arbitrario, sin tener que calcular todos los cuantiles. Aquí está el código de Python:

class RunningPercentile:
    def __init__(self, percentile=0.5, step=0.1):
        self.step = step
        self.step_up = 1.0 - percentile
        self.step_down = percentile
        self.x = None

    def push(self, observation):
        if self.x is None:
            self.x = observation
            return

        if self.x > observation:
            self.x -= self.step * self.step_up
        elif self.x < observation:
            self.x += self.step * self.step_down
        if abs(observation - self.x) < self.step:
            self.step /= 2.0

y un ejemplo:

import numpy as np
import matplotlib.pyplot as plt

distribution = np.random.normal
running_percentile = RunningPercentile(0.841)
observations = []
for _ in range(1000000):
    observation = distribution()
    running_percentile.push(observation)
    observations.append(observation)

plt.figure(figsize=(10, 3))
plt.hist(observations, bins=100)
plt.axvline(running_percentile.x, c='k')
plt.show()

Distribución normal con 1 percentil de ETS

parrowdice
fuente