Quiero calcular el promedio de un conjunto de datos circulares. Por ejemplo, podría tener varias muestras de la lectura de una brújula. El problema, por supuesto, es cómo lidiar con el entorno. El mismo algoritmo podría ser útil para una esfera de reloj.
La pregunta real es más complicada: qué significan las estadísticas en una esfera o en un espacio algebraico que "se envuelve", por ejemplo, el grupo aditivo mod n. La respuesta puede no ser única, por ejemplo, el promedio de 359 grados y 1 grado podría ser 0 grados o 180, pero estadísticamente 0 se ve mejor.
Este es un problema de programación real para mí y estoy tratando de que no se vea como un problema matemático.
Respuestas:
Calcule los vectores unitarios desde los ángulos y tome el ángulo de su promedio.
fuente
Esta pregunta se examina en detalle en el libro: "Estadísticas sobre esferas", Geoffrey S. Watson, Notas de la Conferencia de la Universidad de Arkansas en Ciencias Matemáticas, 1983 John Wiley & Sons, Inc., como se menciona en http: //catless.ncl. ac.uk/Risks/7.44.html#subj4 por Bruce Karsh.
Una buena manera de estimar un ángulo promedio, A, a partir de un conjunto de mediciones de ángulos a [i] 0 <= i
El método dado por starblue es computacionalmente equivalente, pero sus razones son más claras y probablemente programáticamente más eficientes, y también funcionan bien en el caso cero, así que felicitaciones a él.
El tema ahora se explora con más detalle en Wikipedia y con otros usos, como partes fraccionarias.
fuente
Veo el problema: por ejemplo, si tiene un ángulo de 45 'y un ángulo de 315', el promedio "natural" sería 180 ', pero el valor que desea es en realidad 0'.
Creo que Starblue está en algo. Simplemente calcule las coordenadas cartesianas (x, y) para cada ángulo y sume esos vectores resultantes. El desplazamiento angular del vector final debe ser el resultado requerido.
Estoy ignorando por ahora que el rumbo de una brújula comienza en el norte y va en el sentido de las agujas del reloj, mientras que las coordenadas cartesianas "normales" comienzan con cero a lo largo del eje X y luego van en sentido antihorario. Las matemáticas deberían funcionar de la misma manera independientemente.
fuente
cos()
,sin()
yatan2()
dan aproximaciones (buenos, pero todavía fuera por 1 o 2 ULP) así que cuanto más promedio, el más errores que incluyen.PARA EL CASO ESPECIAL DE DOS ÁNGULOS:
La respuesta ((a + b) mod 360) / 2 es INCORRECTA . Para los ángulos 350 y 2, el punto más cercano es 356, no 176.
El vector unitario y las soluciones trigonométricas pueden ser demasiado caras.
Lo que tengo de un pequeño retoque es:
fuente
ackb tiene razón en que estas soluciones basadas en vectores no pueden considerarse promedios reales de ángulos, son solo un promedio de las contrapartidas de vectores unitarios. Sin embargo, la solución sugerida de ackb no parece ser matemáticamente sólida.
La siguiente es una solución matemáticamente derivada del objetivo de minimizar (ángulo [i] - avgAngle) ^ 2 (donde la diferencia se corrige si es necesario), lo que la convierte en una verdadera media aritmética de los ángulos.
Primero, necesitamos ver exactamente qué casos la diferencia entre ángulos es diferente a la diferencia entre sus contrapartes numéricas normales. Considere los ángulos x e y, si y> = x - 180 e y <= x + 180, entonces podemos usar la diferencia (xy) directamente. De lo contrario, si no se cumple la primera condición, debemos usar (y + 360) en el cálculo en lugar de y. En consecuencia, si no se cumple la segunda condición, entonces debemos usar (y-360) en lugar de y. Dado que la ecuación de la curva estamos minimizando solo los cambios en los puntos donde estas desigualdades cambian de verdadero a falso o viceversa, podemos separar el rango completo [0,360] en un conjunto de segmentos, separados por estos puntos. Entonces, solo necesitamos encontrar el mínimo de cada uno de estos segmentos, y luego el mínimo del mínimo de cada segmento, que es el promedio.
Aquí hay una imagen que demuestra dónde ocurren los problemas al calcular las diferencias de ángulo. Si x se encuentra en el área gris, entonces habrá un problema.
Para minimizar una variable, dependiendo de la curva, podemos tomar la derivada de lo que queremos minimizar y luego encontramos el punto de inflexión (que es donde la derivada = 0).
Aquí aplicaremos la idea de minimizar la diferencia al cuadrado para derivar la fórmula de la media aritmética común: suma (a [i]) / n. La curva y = suma ((a [i] -x) ^ 2) se puede minimizar de esta manera:
Ahora aplicándolo a curvas con nuestras diferencias ajustadas:
b = subconjunto de a donde la diferencia (angular) correcta a [i] -xc = subconjunto de a donde la diferencia (angular) correcta (a [i] -360) -x cn = tamaño de cd = subconjunto de a donde diferencia (angular) correcta (a [i] +360) -x dn = tamaño de d
Esto por sí solo no es suficiente para obtener el mínimo, aunque funciona para valores normales, que tienen un conjunto ilimitado, por lo que el resultado definitivamente estará dentro del rango del conjunto y, por lo tanto, es válido. Necesitamos el mínimo dentro de un rango (definido por el segmento). Si el mínimo es menor que el límite inferior de nuestro segmento, entonces el mínimo de ese segmento debe estar en el límite inferior (porque las curvas cuadráticas solo tienen 1 punto de inflexión) y si el mínimo es mayor que el límite superior de nuestro segmento, entonces el mínimo del segmento está en el límite superior Después de tener el mínimo para cada segmento, simplemente encontramos el que tiene el valor más bajo para lo que estamos minimizando (sum ((b [i] -x) ^ 2) + sum (((c [i] -360 ) -b) ^ 2) + sum (((d [i] +360) -c) ^ 2)).
Aquí hay una imagen de la curva, que muestra cómo cambia en los puntos donde x = (a [i] +180)% 360. El conjunto de datos en cuestión es {65,92,230,320,250}.
Aquí hay una implementación del algoritmo en Java, que incluye algunas optimizaciones, su complejidad es O (nlogn). Se puede reducir a O (n) si reemplaza la ordenación basada en la comparación con una ordenación no basada en la comparación, como la ordenación por radix.
La media aritmética de un conjunto de ángulos puede no coincidir con su idea intuitiva de cuál debería ser el promedio. Por ejemplo, la media aritmética del conjunto {179,179,0,181,181} es 216 (y 144). La respuesta en la que piensa de inmediato es probablemente 180, sin embargo, es bien sabido que la media aritmética se ve muy afectada por los valores de borde. También debe recordar que los ángulos no son vectores, a pesar de lo atractivo que puede parecer a veces cuando se trata de ángulos.
Por supuesto, este algoritmo también se aplica a todas las cantidades que obedecen a la aritmética modular (con un ajuste mínimo), como la hora del día.
También me gustaría enfatizar que a pesar de que este es un promedio real de ángulos, a diferencia de las soluciones vectoriales, eso no significa necesariamente que sea la solución que debería usar, el promedio de los vectores unitarios correspondientes puede ser el valor que realmente debería estar usando
fuente
Tienes que definir el promedio con mayor precisión. Para el caso específico de dos ángulos, puedo pensar en dos escenarios diferentes:
Sin embargo, no veo cómo se puede generalizar la segunda alternativa para el caso de más de dos ángulos.
fuente
Como todos los promedios, la respuesta depende de la elección de la métrica. Para una métrica M dada, el promedio de algunos ángulos a_k en [-pi, pi] para k en [1, N] es ese ángulo a_M que minimiza la suma de las distancias al cuadrado d ^ 2_M (a_M, a_k). Para una media ponderada, uno simplemente incluye en la suma los pesos w_k (tal que sum_k w_k = 1). Es decir,
a_M = arg min_x sum_k w_k d ^ 2_M (x, a_k)
Dos opciones comunes de métrica son las métricas de Frobenius y Riemann. Para la métrica de Frobenius, existe una fórmula directa que corresponde a la noción habitual de demora promedio en estadísticas circulares. Consulte "Medios y promedios en el grupo de rotaciones", Maher Moakher, SIAM Journal on Matrix Analysis and Applications, Volumen 24, Número 1, 2002, para más detalles.
http://link.aip.org/link/?SJMAEL/24/1/1
Aquí hay una función para GNU Octave 3.2.4 que hace el cálculo:
fuente
Me gustaría compartir un método que utilicé con un microcontrolador que no tenía capacidades de trigonometría o coma flotante. Todavía necesitaba "promediar" 10 lecturas de rodamiento sin procesar para suavizar las variaciones.
No es ideal; Se puede romper. Me salí con la suya en este caso porque el dispositivo solo gira muy lentamente. Lo publicaré en caso de que alguien más se encuentre trabajando bajo restricciones similares.
fuente
En inglés:
En python:
Un conjunto de ángulos #numpy NX1
fuente
Aquí está la solución completa: (la entrada es una matriz de demora en grados (0-360)
fuente
En pitón, con ángulos entre [-180, 180)
Detalles:
Para el promedio de dos ángulos hay dos promedios separados 180 °, pero es posible que deseemos el promedio más cercano.
Visualmente, el promedio del azul ( b ) y el verde ( a ) produce el punto verde azulado:
Los ángulos se ajustan (por ejemplo, 355 + 10 = 5), pero la aritmética estándar ignorará este punto de ramificación. Sin embargo, si el ángulo b es opuesto al punto de ramificación, entonces ( b + g ) / 2 da el promedio más cercano: el punto verde azulado.
Para cualquiera de los dos ángulos, podemos rotar el problema para que uno de los ángulos esté opuesto al punto de ramificación, realizar un promedio estándar y luego girar hacia atrás.
fuente
Yo seguiría el camino del vector usando números complejos. Mi ejemplo está en Python, que tiene números complejos incorporados:
Tenga en cuenta que Python no necesita construir una nueva lista temporal de vectores, todo lo anterior se puede hacer en un solo paso; Acabo de elegir esta forma de aproximar el pseudocódigo aplicable a otros idiomas también.
fuente
Aquí hay una solución completa de C ++:
Toma los ángulos en forma de un vector de dobles y devuelve el promedio simplemente como un doble. Los ángulos deben estar en grados y, por supuesto, el promedio también está en grados.
fuente
avgCos
es el promedio de los componentes x, yavgSin
es el promedio de los componentes y. Los parámetros para la función arcotangente sonatan2( y, x )
. Entonces, ¿no debería su código ser:atan2( avgSin, avgCos )
??Basado en la respuesta de Alnitak , he escrito un método Java para calcular el promedio de ángulos múltiples:
Si sus ángulos están en radianes:
Si tus ángulos están en grados:
fuente
He aquí una idea: construya el promedio de forma iterativa calculando siempre el promedio de los ángulos más cercanos entre sí, manteniendo un peso.
Otra idea: encontrar la brecha más grande entre los ángulos dados. Encuentre el punto que lo divide y luego elija el punto opuesto en el círculo como el cero de referencia para calcular el promedio.
fuente
Representemos estos ángulos con puntos en la circunferencia del círculo.
¿Podemos suponer que todos estos puntos caen en la misma mitad del círculo? (De lo contrario, no hay una forma obvia de definir el "ángulo promedio". Piense en dos puntos en el diámetro, por ejemplo, 0 grados y 180 grados --- ¿es el promedio de 90 grados o 270 grados? ¿Qué sucede cuando tenemos 3 o más? puntos distribuidos uniformemente?)
Con esta suposición, elegimos un punto arbitrario en ese semicírculo como el "origen", y medimos el conjunto de ángulos dado con respecto a este origen (llamemos a esto el "ángulo relativo"). Tenga en cuenta que el ángulo relativo tiene un valor absoluto estrictamente inferior a 180 grados. Finalmente, tome la media de estos ángulos relativos para obtener el ángulo promedio deseado (en relación con nuestro origen, por supuesto).
fuente
No hay una sola "respuesta correcta". Recomiendo leer el libro, KV Mardia y PE Jupp, "Directional Statistics" (Wiley, 1999), para un análisis exhaustivo.
fuente
(Solo quiero compartir mi punto de vista desde la teoría de la estimación o la inferencia estadística)
La prueba de Nimble es obtener la estimación MMSE ^ de un conjunto de ángulos, pero es una de las opciones para encontrar una dirección "promediada"; también se puede encontrar una estimación MMAE ^, o alguna otra estimación para ser la dirección "promediada", y depende de su error de cuantificación métrico de dirección; o más generalmente en la teoría de la estimación, la definición de función de costo.
^ MMSE / MMAE corresponde a la media mínima al cuadrado / error absoluto.
ackb dijo "El ángulo promedio phi_avg debería tener la propiedad de que sum_i | phi_avg-phi_i | ^ 2 se vuelve mínimo ... promedian algo, pero no ángulos"
---- cuantifica los errores en sentido cuadrado medio y es una de las formas más comunes, sin embargo, no es la única. La respuesta preferida por la mayoría de las personas aquí (es decir, la suma de los vectores unitarios y obtener el ángulo del resultado) es en realidad una de las soluciones razonables. Es (se puede probar) el estimador de ML que sirve como la dirección "promedio" que queremos, si las direcciones de los vectores se modelan como distribución de von Mises. Esta distribución no es elegante, y es solo una distribución muestreada periódicamente de un Guassian 2D. Ver ec. (2.179) en el libro de Bishop "Reconocimiento de patrones y aprendizaje automático". Nuevamente, de ninguna manera es la única mejor para representar la dirección "promedio", sin embargo, es bastante razonable que tenga una buena justificación teórica y una implementación simple.
Nimble dijo que "ackb tiene razón en que estas soluciones basadas en vectores no pueden considerarse promedios reales de ángulos, son solo un promedio de las contrapartidas de vectores unitarios"
----esto no es verdad. Las "contrapartes del vector unitario" revelan la información de la dirección de un vector. El ángulo es una cantidad sin tener en cuenta la longitud del vector, y el vector unitario es algo con información adicional de que la longitud es 1. Puede definir que su vector "unidad" sea de longitud 2, en realidad no importa.
fuente
Aquí hay una solución completamente aritmética que usa promedios móviles y se encarga de normalizar los valores. Es rápido y ofrece respuestas correctas si todos los ángulos están en un lado del círculo (dentro de 180 ° entre sí).
Es matemáticamente equivalente a sumar el desplazamiento que desplaza los valores al rango (0, 180), calculando la media y luego restando el desplazamiento.
Los comentarios describen qué rango puede tomar un valor específico en un momento dado
fuente
Bueno, llego muy tarde a la fiesta, pero pensé en agregar mis 2 centavos de dólar, ya que realmente no pude encontrar ninguna respuesta definitiva. Al final, implementé la siguiente versión Java del método Mitsuta que, espero, proporcione una solución simple y robusta. Particularmente porque la desviación estándar proporciona una dispersión de medida y, si sd == 90, indica que los ángulos de entrada dan como resultado una media ambigua.
EDITAR: En realidad, me di cuenta de que mi implementación original se puede simplificar aún más, de hecho, es preocupantemente simple teniendo en cuenta toda la conversación y la trigonometría en las otras respuestas.
... y para todos los geeks (Java) que hay, puede usar el enfoque anterior para obtener el ángulo medio en una línea.
fuente
Alnitak tiene la solución correcta. La solución de Nick Fortescue es funcionalmente la misma.
Para el caso especial de donde
(sum (x_component) = 0.0 && sum (y_component) = 0.0) // ej. 2 ángulos de 10. y 190. grados ea.
usar 0.0 grados como la suma
Computacionalmente debe probar este caso ya que atan2 (0., 0.) no está definido y generará un error.
fuente
El ángulo promedio phi_avg debería tener la propiedad de que sum_i | phi_avg-phi_i | ^ 2 se vuelve mínimo, donde la diferencia debe estar en [-Pi, Pi) (¡porque podría ser más corto al revés!). Esto se logra fácilmente normalizando todos los valores de entrada a [0, 2Pi), manteniendo un promedio de ejecución phi_run y eligiendo normalizar | phi_i-phi_run | a [-Pi, Pi) (sumando o restando 2Pi). La mayoría de las sugerencias anteriores hacen algo más que no tiene esa propiedad mínima, es decir, promedian algo , pero no ángulos.
fuente
Resolví el problema con la ayuda de la respuesta de @David_Hanak. Como él dice:
Entonces, lo que hice fue calcular el promedio de todos los ángulos. Y luego, todos los ángulos que sean menores que este, increméntelos en 360. Luego, vuelva a calcular el promedio al sumarlos todos y dividirlos por su longitud.
Funciona perfectamente.
fuente
Función de Python:
fuente
Puede usar esta función en Matlab:
fuente
Puede ver una solución y una pequeña explicación en el siguiente enlace, para CUALQUIER lenguaje de programación: https://rosettacode.org/wiki/Averages/Mean_angle
Por ejemplo, la solución C ++ :
Salida:
O la solución de Matlab :
fuente
Si bien la respuesta de starblue da el ángulo del vector unitario promedio, es posible extender el concepto de la media aritmética a los ángulos si acepta que puede haber más de una respuesta en el rango de 0 a 2 * pi (o 0 ° a 360 °). Por ejemplo, el promedio de 0 ° y 180 ° puede ser 90 ° o 270 °.
La media aritmética tiene la propiedad de ser el valor único con la suma mínima de distancias cuadradas a los valores de entrada. La distancia a lo largo del círculo unitario entre dos vectores unitarios se puede calcular fácilmente como el coseno inverso de su producto escalar. Si elegimos un vector unitario minimizando la suma del coseno inverso cuadrado del producto punto de nuestro vector y cada vector unitario de entrada, entonces tenemos un promedio equivalente. Nuevamente, tenga en cuenta que puede haber dos o más mínimos en casos excepcionales.
Este concepto podría extenderse a cualquier cantidad de dimensiones, ya que la distancia a lo largo de la esfera unitaria se puede calcular exactamente de la misma manera que la distancia a lo largo del círculo unitario: el coseno inverso del producto escalar de dos vectores unitarios.
Para los círculos podríamos resolver este promedio de varias maneras, pero propongo el siguiente algoritmo O (n ^ 2) (los ángulos están en radianes, y evito calcular los vectores unitarios):
Si todos los ángulos están dentro de 180 ° entre sí, entonces podríamos usar un algoritmo más simple de O (n) + O (ordenar) (nuevamente usando radianes y evitando el uso de vectores unitarios):
Para usar grados, simplemente reemplace pi con 180. Si planea usar más dimensiones, lo más probable es que tenga que usar un método iterativo para resolver el promedio.
fuente
El problema es extremadamente simple. 1. Asegúrese de que todos los ángulos estén entre -180 y 180 grados. 2. a Suma todos los ángulos no negativos, toma su promedio y CUENTA cuántos 2. b. Agrega todos los ángulos negativos, toma su promedio y CUENTA cuántos. 3. Tome la diferencia de pos_average menos neg_average Si la diferencia es mayor que 180, cambie la diferencia a 360 menos la diferencia. De lo contrario, simplemente cambie el signo de la diferencia. Tenga en cuenta que la diferencia siempre es no negativa. Average_Angle es igual al pos_average más la diferencia multiplicada por el "peso", recuento negativo dividido por la suma del recuento negativo y positivo
fuente
Aquí hay un código de Java para ángulos promedio, creo que es razonablemente robusto.
fuente
Tengo un método diferente a @Starblue que da respuestas "correctas" a algunos de los ángulos dados anteriormente. Por ejemplo:
Utiliza una suma sobre las diferencias entre ángulos consecutivos. El código (en Matlab):
fuente
[-90,90,40]
y[90,-90,40]
; No creo que un promedio no conmutativo sea muy útil.