Pregunta

Estoy buscando un " buenos números " algoritmo para determinar las etiquetas en un eje de valor de fecha / hora. Estoy familiarizado con Algoritmo de Números agradables de Paul Heckbert .

Tengo un gráfico que muestra la hora / fecha en el eje X y el usuario puede acercar y mirar un marco de tiempo más pequeño. Estoy buscando un algoritmo que elija fechas agradables para mostrar en las garrapatas.

Por ejemplo:

  • Mirando un día más o menos: 1/1 12:00, 1/1 4:00, 1/1 8:00 ...
  • Mirando una semana: 1/1, 1/2, 1/3 ...
  • Mirando un mes: 1/09, 2/09, 3/09 ...

Las buenas marcas de etiquetas no necesitan corresponder al primer punto visible, sino estar cerca de él.

¿Alguien está familiarizado con tal algoritmo?

¿Fue útil?

Solución

El artículo de 'buenos números' al que se vinculó mencionó que

  

los números más bonitos en decimal son 1, 2, 5 y todos los múltiplos de potencia de 10 de estos números

Así que creo que para hacer algo similar con la fecha / hora, debe comenzar desglosando de manera similar los componentes. Así que tome los factores agradables de cada tipo de intervalo:

  • Si muestra segundos o minutos, use 1, 2, 3, 5, 10, 15, 30 (Me salteé 6, 12, 15, 20 porque no se sienten `` bien '').
  • Si muestra horas use 1, 2, 3, 4, 6, 8, 12
  • para días use 1, 2, 7
  • durante semanas use 1, 2, 4 (13 y 26 se ajustan al modelo pero me parecen demasiado extraños)
  • durante meses use 1, 2, 3, 4, 6
  • durante años use 1, 2, 5 y múltiplos de potencia de 10

Ahora, obviamente, esto comienza a descomponerse a medida que ingresa en grandes cantidades. Ciertamente, no desea mostrar 5 semanas de minutos, incluso en "bonito" intervalos de 30 minutos o algo así. Por otro lado, cuando solo tiene 48 horas de valor, no desea mostrar intervalos de 1 día. El truco, como ya ha señalado, es encontrar puntos de transición decentes.

Solo por presentimiento, diría que un punto de cruce razonable sería aproximadamente el doble que el siguiente intervalo. Eso le daría lo siguiente (número mínimo y máximo de intervalos que se muestran después)

  • use segundos si tiene menos de 2 minutos (1-120)
  • use minutos si tiene menos de 2 horas (2-120)
  • use horas si tiene menos de 2 días (2-48)
  • use días si tiene menos de 2 semanas (2-14)
  • use semanas si tiene menos de 2 meses (2-8 / 9)
  • use meses si tiene menos de 2 años (2-24)
  • de lo contrario, use años (aunque podría continuar con décadas, siglos, etc. si sus rangos pueden ser tan largos)

Desafortunadamente, nuestros intervalos de tiempo inconsistentes significan que terminas con algunos casos que pueden tener más de cien intervalos, mientras que otros tienen como máximo 8 o 9. Por lo tanto, querrás elegir el tamaño de tus intervalos tal como no lo haces ' t tiene más de 10-15 intervalos como máximo (o menos de 5 para el caso). Además, puede romper con una definición estricta de 2 veces el siguiente intervalo más grande si cree que es fácil realizar un seguimiento. Por ejemplo, puede usar horas de hasta 3 días (72 horas) y semanas de hasta 4 meses. Puede ser necesario un poco de prueba y error.

Entonces, para volver, elija el tipo de intervalo basado en el tamaño de su rango, luego elija el tamaño de intervalo seleccionando uno de los "agradables" números que lo dejarán con entre 5 y aproximadamente 15 marcas de verificación. O si conoce y / o puede controlar el número real de píxeles entre las marcas de verificación, puede poner límites superior e inferior sobre cuántos píxeles son aceptables entre las marcas (si están espaciados demasiado, el gráfico puede ser difícil de leer, pero si hay demasiados tics, el gráfico estará abarrotado y sus etiquetas pueden superponerse).

Otros consejos

Todavía no hay respuesta a esta pregunta ... ¡Lanzaré mi primera idea! Supongo que tiene el rango del eje visible.

Probablemente así sea como lo haría.

Pseudo rudo:

// quantify range
rangeLength = endOfVisiblePart - startOfVisiblePart;

// qualify range resolution
if (range < "1.5 day") {
    resolution = "day";  // it can be a number, e.g.: ..., 3 for day, 4 for week, ...
} else if (range < "9 days") {
    resolution = "week";
} else if (range < "35 days") {
    resolution = "month";
} // you can expand this in both ways to get from nanoseconds to geological eras if you wish

Después de eso, debería (dependiendo de a qué tenga fácil acceso) ser bastante fácil determinar el valor de cada tick de etiqueta agradable. Dependiendo de la 'resolución', la formatea de manera diferente. Por ejemplo: MM / DD para " semana " ;, MM: SS para " minuto " ;, etc., tal como usted dijo.

Echa un vistazo a

http://tools.netsa.cert.org/netsa -python / doc / index.html

Tiene un nice.py (python / netsa / data / nice.py) que creo que es independiente y debería funcionar bien.

Te sugiero que tomes el código fuente para gnuplot o RRDTool (o incluso Flot) y examines cómo abordan este problema. Es probable que el caso general sea N etiquetas aplicadas en función del ancho de su trama, que algún tipo de 'ajuste' al número 'agradable' más cercano.

Cada vez que he escrito un algoritmo de este tipo (demasiadas veces en realidad), he usado una tabla de 'preferencias' ... es decir: en función del rango de tiempo en la trama, decido si estoy usando Weeks , Días, Horas, Minutos, etc. como el punto del eje principal. Por lo general, incluí algunos formatos preferidos, ya que rara vez quiero ver la fecha de cada minuto que trazo en el gráfico.

Me alegraría pero me sorprendería encontrar a alguien usando una fórmula (como lo hace Heckbert) para encontrar 'agradable', ya que la variación en las unidades de tiempo entre minutos, horas, días y semanas no es tan lineal.

[Editar - Expandí esto un poco más en http://www.acooke.org /cute/AutoScalin0.html ]

Una extensión ingenua de los "buenos números" El algoritmo parece funcionar para las bases 12 y 60, lo que proporciona buenos intervalos de horas y minutos. Este es el código que acabo de piratear juntos:

LIM10 = (10, [(1.5, 1), (3, 2), (7, 5)], [1, 2, 5])
LIM12 = (12, [(1.5, 1), (3, 2), (8, 6)], [1, 2, 6])
LIM60 = (60, [(1.5, 1), (20, 15), (40, 30)], [1, 15, 40])


def heckbert_d(lo, hi, ntick=5, limits=None):
    '''
    Heckbert's "nice numbers" algorithm for graph ranges, from "Graphics Gems".
    '''
    if limits is None:
        limits = LIM10
    (base, rfs, fs) = limits
    def nicenum(x, round):
        step = base ** floor(log(x)/log(base))
        f = float(x) / step
        nf = base
        if round:
            for (a, b) in rfs:
                if f < a:
                    nf = b
                    break
        else:
            for a in fs:
                if f <= a:
                    nf = a
                    break
        return nf * step
    delta = nicenum(hi-lo, False)
    return nicenum(delta / (ntick-1), True)


def heckbert(lo, hi, ntick=5, limits=None):
    '''
    Heckbert's "nice numbers" algorithm for graph ranges, from "Graphics Gems".
    '''
    def _heckbert():
        d = heckbert_d(lo, hi, ntick=ntick, limits=limits)
        graphlo = floor(lo / d) * d
        graphhi = ceil(hi / d) * d
        fmt = '%' + '.%df' %  max(-floor(log10(d)), 0)
        value = graphlo
        while value < graphhi + 0.5*d:
            yield fmt % value
            value += d
    return list(_heckbert())

Entonces, por ejemplo, si desea mostrar segundos de 0 a 60,

>>> heckbert(0, 60, limits=LIM60)
['0', '15', '30', '45', '60']

u horas de 0 a 5:

>>> heckbert(0, 5, limits=LIM12)
['0', '2', '4', '6']

En teoría, también puede cambiar su concepto. Donde no están sus datos en el centro de la visualización, pero en el centro tiene su escala.

Cuando conoce el inicio y el final de las fechas de sus datos, puede crear una escala con todas las fechas y enviar sus datos en esta escala. Como una balanza fija.

Puede tener una escala de tipo año, mes, día, horas, ... y limitar la escala solo a estas escalas, lo que implica que elimina el concepto de escala libre.

La ventaja es que puede mostrar fácilmente las brechas de fechas. Pero si tiene muchas lagunas, eso también puede volverse inútil.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top