2010-09-23 47 views
15

Tengo un programa que necesita calcular repetidamente el percentil aproximado (estadística de orden) de un conjunto de datos para eliminar valores atípicos antes del procesamiento posterior. Actualmente lo estoy haciendo ordenando la matriz de valores y seleccionando el elemento apropiado; esto es factible, pero es un problema notorio en los perfiles a pesar de ser una parte bastante menor del programa.Algoritmo rápido para calcular percentiles para eliminar valores atípicos

Más información:

  • El conjunto de datos contiene del orden de hasta 100.000 números de punto flotante, y asume que es "razonablemente" distribuida - es poco probable que sea una duplicación ni enormes picos de densidad de cerca de concreto valores; y si por alguna extraña razón la distribución es impar, está bien que una aproximación sea menos precisa, ya que los datos probablemente estén en mal estado y el procesamiento sea dudoso. Sin embargo, los datos no son necesariamente distribuidos uniforme o uniformemente; es muy poco probable que sea degenerado.
  • Una solución aproximada estaría bien, pero necesito entender cómo la aproximación introduce un error para garantizar que sea válida.
  • Dado que el objetivo es eliminar valores atípicos, estoy calculando dos percentiles sobre los mismos datos en todo momento: p. uno al 95% y uno al 5%.
  • La aplicación está en C# con cargas pesadas en C++; pseudocódigo o una biblioteca preexistente en cualquiera estaría bien.
  • Una forma completamente diferente de eliminar valores atípicos también estaría bien, siempre que sea razonable.
  • Actualización: Parece que estoy buscando un selection algorithm aproximado.

A pesar de todo esto se hace en un bucle, los datos es (ligeramente) diferente cada vez, así que no es fácil de reutilizar una estructura de datos como se hizo for this question.

implementado la solución

Usando el algoritmo de selección de Wikipedia como sugiere Gronim reduce esta parte del tiempo de ejecución en un factor 20.

Como no podía encontrar una aplicación C#, esto es lo que se le ocurrió. Es más rápido incluso para entradas pequeñas que Array.Sort; y en 1000 elementos es 25 veces más rápido.

public static double QuickSelect(double[] list, int k) { 
    return QuickSelect(list, k, 0, list.Length); 
} 
public static double QuickSelect(double[] list, int k, int startI, int endI) { 
    while (true) { 
     // Assume startI <= k < endI 
     int pivotI = (startI + endI)/2; //arbitrary, but good if sorted 
     int splitI = partition(list, startI, endI, pivotI); 
     if (k < splitI) 
      endI = splitI; 
     else if (k > splitI) 
      startI = splitI + 1; 
     else //if (k == splitI) 
      return list[k]; 
    } 
    //when this returns, all elements of list[i] <= list[k] iif i <= k 
} 
static int partition(double[] list, int startI, int endI, int pivotI) { 
    double pivotValue = list[pivotI]; 
    list[pivotI] = list[startI]; 
    list[startI] = pivotValue; 

    int storeI = startI + 1;//no need to store @ pivot item, it's good already. 
    //Invariant: startI < storeI <= endI 
    while (storeI < endI && list[storeI] <= pivotValue) ++storeI; //fast if sorted 
    //now storeI == endI || list[storeI] > pivotValue 
    //so elem @storeI is either irrelevant or too large. 
    for (int i = storeI + 1; i < endI; ++i) 
     if (list[i] <= pivotValue) { 
      list.swap_elems(i, storeI); 
      ++storeI; 
     } 
    int newPivotI = storeI - 1; 
    list[startI] = list[newPivotI]; 
    list[newPivotI] = pivotValue; 
    //now [startI, newPivotI] are <= to pivotValue && list[newPivotI] == pivotValue. 
    return newPivotI; 
} 
static void swap_elems(this double[] list, int i, int j) { 
    double tmp = list[i]; 
    list[i] = list[j]; 
    list[j] = tmp; 
} 

Performance Graph

Gracias, Gronim, para mí apuntando en la dirección correcta!

Respuesta

8

La solución de histograma de Henrik funcionará. También puede usar un algoritmo de selección para encontrar eficientemente los k elementos más grandes o más pequeños en una matriz de n elementos en O (n). Para usar esto para el percentil 95, establece k = 0.05n y encuentra los k elementos más grandes.

Referencia:

http://en.wikipedia.org/wiki/Selection_algorithm#Selecting_k_smallest_or_largest_elements

+0

Correcto, eso es lo que estaba buscando, ¡un algoritmo de selección! –

3

Divida el intervalo entre el mínimo y el máximo de sus datos en (digamos) 1000 bandejas y calcule un histograma. Luego construya sumas parciales y vea dónde superan en primer lugar 5000 o 95000.

+0

Bonito ... quicksort, y corte la parte superior e inferior de 5000. Sin saber la distribución, no sé cómo podría hacerlo mejor. – John

+0

El ordenamiento del cubo es más apropiado para esto. – Brian

+1

Esto suena eminentemente práctico, aunque no siempre efectivo. Algunos valores atípicos extremos podrían distorsionar realmente sus contenedores ... –

0
No

un experto, pero mi memoria sugiere:

  • para determinar puntos porcentuales exactamente lo que necesita para clasificar y contar
  • tomar una muestra a partir de los datos y calcular los valores del percentil suena como un buen plan para aproximación decente si se puede obtener una buena muestra
  • si no, como se sugiere por Henrik, se puede evitar el tipo completo si lo hace los cubos y contarlos
4

usted podría calcule sus percentiles de solo una parte de su conjunto de datos, como los primeros miles de puntos.

El Glivenko–Cantelli theorem asegura que esto sería una estimación bastante buena, si puede asumir que sus puntos de datos son independientes.

+0

Lamentablemente, los puntos de datos no son independientes, están ordenados por criterios externos, pero podría iterar en orden aleatorio. No entiendo cómo el teorema vinculado prácticamente me permitiría estimar los percentiles. ¿Puedes dar un ejemplo, por ej. para la distribución normal? –

+0

@Eamon: el teorema vinculado simplemente establece que la función de distribución empírica (que usaría implícitamente al calcular los percentiles en función de los datos) es una buena estimación para la distribución real. No tiene que usarlo en realidad =) – Jens

+0

Ahh, OK, entiendo lo que quiere decir :-) –

1

Hay un par de enfoques básicos que se me ocurren. Primero es calcular el rango (encontrando los valores más altos y más bajos), proyectar cada elemento a un percentil ((x - min)/rango) y descartar cualquiera que evalúe a menos de .05 o más alto que .95.

El segundo es calcular la media y la desviación estándar. Un lapso de 2 desviaciones estándar de la media (en ambas direcciones) abarcará el 95% de un espacio de muestra distribuido normalmente, lo que significa que sus valores atípicos estarían en el < 2.5 y> 97.5 percentiles. El cálculo de la media de una serie es lineal, como lo es el dev estándar (raíz cuadrada de la suma de la diferencia de cada elemento y la media). Luego, resta 2 sigmas de la media y agrega 2 sigmas a la media, y obtienes tus límites de valores atípicos.

Ambos cálculos se calcularán en un tiempo aproximadamente lineal; el primero requiere dos pases, el segundo toma tres (una vez que tienes tus límites, tienes que descartar los valores atípicos). Como se trata de una operación basada en listas, no creo que encuentre nada con una complejidad logarítmica o constante; cualquier ganancia de rendimiento adicional requeriría optimizar la iteración y el cálculo, o introducir un error al realizar los cálculos en una submuestra (como cada tercer elemento).

+0

La primera sugerencia es no descartar los percentiles externos, pero hacer algo basado en los extremos atípicos que es altamente inestable . La segunda sugerencia supone que los datos se distribuyen normalmente, lo que explícitamente no es. –

4

Solía ​​identificar valores atípicos calculando el standard deviation. Todo con una distancia más de 2 (o 3) veces la desviación estándar del avarage es un valor atípico. 2 veces = aproximadamente 95%.

Dado que usted está calculando el avarage, también es muy fácil calcular que la desviación estándar es muy rápida.

También podría usar solo un subconjunto de sus datos para calcular los números.

+2

Los datos no se distribuyen normalmente. –

6

According a su creador un SoftHeap se puede utilizar para:

compute exacta o aproximada medianas y percentiles de manera óptima.También es útil para clasificación aproximada ...

+0

+1 Hmm, ¡suena interesante! –

+0

@Eamon toda la idea detrás de SoftHeap y sus aplicaciones son realmente geniales. –

+0

@EugenConstantinDinca: ¡Gracias por la gran idea! ¿Existe una implementación real de esto en alguna parte o el documento/wiki son las únicas fuentes? – Legend

1

Una buena respuesta general a su problema parece ser RANSAC. Dado un modelo, y algunos datos ruidosos, el algoritmo recupera eficientemente los parámetros del modelo.
Tendrás que elegir un modelo simple que pueda mapear tus datos. Cualquier cosa sin problemas debería estar bien. Digamos una mezcla de pocos gaussianos. RANSAC establecerá los parámetros de su modelo y calculará un conjunto de inlines al mismo tiempo. Luego tire todo lo que no se ajuste al modelo correctamente.

+0

Tengo un conjunto de números, no un modelo complejo, parece que RANSAC sería lento y propenso a errores, y que para un caso tan simple existen mejores soluciones. –

0

Un conjunto de datos de 100k elementos toma casi ningún tiempo para ordenar, así que supongo que tiene que hacer esto repetidamente. Si el conjunto de datos es el mismo conjunto que acaba de actualizarse ligeramente, es mejor que construya un árbol (O(N log N)) y luego quite y agregue nuevos puntos a medida que ingresen (O(K log N) donde K es la cantidad de puntos modificados). De lo contrario, la k la solución de elemento más grande ya mencionada le da O(N) para cada conjunto de datos.

1

Puede filtrar 2 o 3 desviaciones estándar incluso si los datos no se distribuyen normalmente; al menos, se hará de manera consistente, eso debería ser importante.

A medida que elimina los valores atípicos, el desarrollador std cambiará, puede hacer esto en un bucle hasta que el cambio en std dev sea mínimo. Si desea o no hacer esto depende de por qué está manipulando los datos de esta manera. Hay grandes reservas por parte de algunos estadísticos para eliminar valores atípicos. Pero algunos eliminan los valores atípicos para demostrar que los datos se distribuyen con bastante normalidad.

+0

Si los datos se encuentran principalmente en los extremos, es decir, lo contrario de lo normal, si lo hace, este enfoque puede eliminar grandes conjuntos de datos. Realmente no quiero eliminar más que una pequeña fracción de los datos, y preferiblemente solo eso cuando estos son valores atípicos. Estoy suprimiendo los valores atípicos porque distraen: están recortados de la visualización, no de los datos reales. –

+0

Por definición, solo una pequeña fracción de sus datos puede estar en los extremos. Según la desigualdad de Chebyshev, solo 1/9 de su distribución puede estar a más de 3 desviaciones estándar de distancia; solo 1/16 puede estar a 4 desviaciones de distancia. Y esos límites solo se alcanzan en el caso degenerado donde su distribución es solo dos picos. Entonces, calcular la desviación en O (N) es una forma válida y eficiente de filtrar valores atípicos. – MSalters

+0

@MSalters: (sí, respondiendo a un comentario de hace 3 años): la desigualdad de chebyshev no es lo suficientemente precisa como para ser práctica. Para recortar al menos el 95% del conjunto de datos, necesitaría hacer 4.5 sigmas; pero si los datos pasaran a ser normales, mostraría el 99,999% de los datos, muy lejos del objetivo. Dicho de otra manera, me alejaría demasiado de un factor de 2.25, es decir, mostraría 5 veces más área de la necesaria, dejando los bits interesantes en minúscula. Y si los datos son más intensos de lo normal, es incluso peor. Entonces, claro, esto podría ser un mínimo absoluto, pero no es una gran aproximación. –

Cuestiones relacionadas