2009-09-14 30 views
21

Estoy buscando un algoritmo de "buenos números" para determinar las etiquetas en un eje de valores de fecha/hora. Estoy familiarizado con Paul Heckbert's Nice Numbers algorithm.Algoritmo para buenas etiquetas de gráfico para el eje de hora/fecha?

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 buenas fechas para mostrar en los tics.

Por ejemplo:

  • En cuanto a un día más o menos: 1/1 12:00, 04:00 1/1, 1/1 ...
  • 8:00
  • Mirando a la semana : 1/1, 1/2, 1/3 ...
  • en cuanto a un mes: 1/09, 2/09, 3/09 ...

Las garrapatas agradables etiqueta no lo hacen necesidad de corresponder al primer punto visible, pero cerca de él.

¿Alguien está familiarizado con tal algoritmo?

Respuesta

6

El artículo 'buenos números' se ha vinculado a mencionado que

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

So Creo que para hacer algo similar con la fecha/hora, debe comenzar por descomponer de manera similar las piezas componentes. Así que toma los factores agradables de cada tipo de intervalo:

  • Si usted está mostrando segundos o minutos utilizar 1, 2, 3, 5, 10, 15, 30 (I saltado 6, 12, 15, 20 porque ellos no "sienten" bien).
  • Si está mostrando horas de uso 1, 2, 3, 4, 6, 8, 12
  • para días utilizar 1, 2, 7
  • para semana utilizar 1, 2, 4 (13 y 26 ajuste el modelo, pero parece demasiado extraño para mí)
  • durante meses utilizar 1, 2, 3, 4, 6
  • de años de uso 1, 2, 5 y múltiplos de potencia-de-10

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

Por un presentimiento, yo diría que un punto de cruce razonable sería aproximadamente el doble que el intervalo siguiente. Eso le daría la siguiente (min y número máximo de intervalos que se indican después)

  • uso segundos si tiene menos de 2 minutos por valor de (1-120)
  • uso minutos si tiene menos de 2 horas de valor (2-120)
  • uso horas Si usted tiene menos de 2 días el valor (2-48)
  • días de usarlo si tiene menos de 2 semanas por valor de (2-14)
  • semanas de uso si usted tiene menos de 2 meses (2-8/9)
  • usa meses si tienes menos de 2 años el valor (2-24)
  • lo contrario utilizan años (aunque se podría continuar con décadas, siglos, etc si sus rangos pueden ser tanto tiempo)

Desafortunadamente, nuestros intervalos de tiempo inconsistentes quiere decir que usted termina con algunos casos que pueden tener más de 100 intervalos, mientras que otros tienen como máximo 8 o 9. Por lo tanto, querrás elegir el tamaño de tus intervalos de forma que no tengas más de 10-15 intervalos como máximo (o menos de 5). para esa materia). Además, puedes saltarte de una definición estricta de 2 veces el siguiente intervalo más grande si crees que es fácil seguirlo. Por ejemplo, puede usar horas hasta 3 días (72 horas) y semanas hasta 4 meses. Un poco de prueba y error puede ser necesario.

Para volver, elija el tipo de intervalo en función del tamaño de su rango, luego elija el tamaño del intervalo eligiendo uno de los números "agradables" que le dejarán entre 5 y 15 marcas. O bien, si conoce y/o puede controlar el número real de píxeles entre las marcas, puede poner límites superiores e inferiores sobre cuántos píxeles son aceptables entre los ticks (si están muy separados, el gráfico puede ser difícil de leer, pero si hay demasiados tics que el gráfico estará desordenado y sus etiquetas pueden superponerse).

1

Todavía no hay respuesta para esta pregunta ... ¡Entonces lanzaré mi primera idea! Supongo que tienes el rango del eje visible.

Esto es probablemente cómo lo haría.

seudo áspera:

// 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, se debe (dependiendo de lo que tiene fácil acceso a) ser bastante fácil determinar el valor de cada una garrapata agradable etiqueta. Según la "resolución", la formatee de forma diferente. Por ejemplo: MM/DD para "semana", MM: SS para "minuto", etc., tal como lo dijo.

+0

Cosas como "1.5 días", "9 días", etc. son altamente dependientes del idioma en términos de implementación (para mí). Por ejemplo, en C o incluso en C++, usaría una longitud sin signo para mantener la diferencia en milisegundos entre ambas veces, mientras que en Java, probablemente crearía una clase de Tiempo o Momento, y probablemente ya existan algunas en algún lugar. .. – Joanis

0

Le sugiero que agarre el código fuente a gnuplot o RRDTool (o incluso Flot) y examine cómo abordan este problema. El caso general es probable que sean N etiquetas aplicadas basadas en el ancho de su parcela, que es una especie de 'ajuste' al número 'bueno' más cercano.

Cada vez que escribo tal algoritmo (demasiadas veces realmente), he usado una tabla de 'preferencias' ... es decir: basado en el rango de tiempo de la trama, decido si estoy usando Semanas, días, horas, minutos, etc. como 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.

Estaría contento pero sorprendido de encontrar a alguien usando una fórmula (como Heckbert) para encontrar 'buena', ya que la variación en unidades de tiempo entre minutos, horas, días y semanas no es tan lineal.

0

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

Una extensión ingenua de los "números agradables" algoritmo parece funcionar para base 12 y 60, lo que da buenos intervalos de horas y minutos.Este es el código que acabo hackeado:

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()) 

Así, por ejemplo, si desea mostrar los segundos de 0 a 60,

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

o las horas de 0 a 5:

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

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 conozca 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 el escalado solo a estas escalas, lo que implica eliminar el concepto de escalado libre.

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

Cuestiones relacionadas