Pergunta

Eu estou procurando um algoritmo "agradáveis ??números" para determinar os rótulos em um eixo de valor de data / hora. Estou familiarizado com agradáveis ??Números Paulo Heckbert algoritmo .

Eu tenho um enredo que exibe data / hora no eixo X e o usuário pode ampliar e olhar para um período de tempo menor. Eu estou procurando um algoritmo que pega datas agradáveis ??para exibição nos carrapatos.

Por exemplo:

  • Olhando para um ou dois dias: 1/1 12:00, 1/1 04:00, 1/1 08:00 ...
  • Olhando para uma semana: 1/1, 1/2, 1/3 ...
  • Olhando para um mês: 1/09, 2/09, 3/09 ...

Os agradáveis ??etiqueta carrapatos não precisa corresponder ao primeiro ponto visível, mas próximo a ele.

Tem alguém familiarizado com tal algoritmo?

Foi útil?

Solução

O artigo 'agradáveis ??números' é ligada ao mencionado que

os números mais agradáveis ??em decimal são 1, 2, 5 e todos os múltiplos de potência de 10 destes números

Então eu acho que para fazer algo semelhante com data / hora que você precisa para começar por semelhante quebrar as peças que o compõem. Portanto, tome os bons factores de cada tipo de intervalo:

  • Se você está mostrando segundos ou minutos usar 1, 2, 3, 5, 10, 15, 30 (Eu saltei 6, 12, 15, 20, porque eles não "sentir" a direita).
  • Se você está mostrando horas usar 1, 2, 3, 4, 6, 8, 12
  • para os dias usar 1, 2, 7
  • por semanas usar 1, 2, 4 (13 e 26 de ajustar o modelo, mas parece muito estranho para mim)
  • para meses de uso 1, 2, 3, 4, 6
  • durante anos usar 1, 2 múltiplos, 5 e potência de 10

Agora, obviamente, isso começa a quebrar como você entrar em quantidades maiores. Certamente você não quer fazer show de 5 semanas de minutos, mesmo em "bonito" intervalos de 30 minutos ou algo assim. Por outro lado, quando você só tem 48 horas de valor, você não quer mostrar intervalos de 1 dia. O truque, como você já têm apontado é encontrar pontos de transição decentes.

Apenas em um palpite, eu diria que um ponto de cruzamento razoável seria de cerca de duas vezes mais que o próximo intervalo. Que lhe daria o seguinte (min e número máximo de intervalos mostrados depois)

  • uso segundos se você tem menos de 2 minutos a pena (1-120)
  • uso minutos, se você tem menos de 2 horas de valor (2-120)
  • horas de uso se você tem menos de 2 dias no valor (2-48)
  • dias de uso se você tem menos de 2 semanas no valor (2-14)
  • uso semanas se você tem menos de 2 meses de valor (2-8 / 9)
  • Use meses, se você tem menos de 2 anos de valor (2-24)
  • caso contrário, use anos (embora você poderia continuar com décadas, séculos, etc se suas gamas pode ser tanto tempo)

Infelizmente, nossos intervalos de tempo inconsistentes significa que você acabar com alguns casos que podem ter mais de 1 centena de intervalos, enquanto outros têm, no máximo, 8 ou 9. Então você vai querer escolher o tamanho dos seus intervalos tais que você don' t tem mais do que 10-15 intervalos de no máximo (ou menos do que 5 para esse efeito). Além disso, você pode quebrar a partir de uma definição estrita de 2 vezes a próxima maior intervalo se você acha que a sua fácil de acompanhar. Por exemplo, você poderia usar horas até 3 dias (72 horas) e semanas até 4 meses. Um pouco de tentativa e erro pode ser necessário.

Assim que voltar mais, escolher o tipo de intervalo com base no tamanho da sua gama, em seguida, escolha o tamanho do intervalo, escolhendo um dos números "nice" que vai deixar você com entre 5 e cerca de 15 marcas de escala. Ou se você sabe e / ou pode controlar o número real de pixels entre as marcas que você poderia colocar limites superior e inferior de quantos pixels são aceitáveis ??entre carrapatos (se eles são espaçados muito longe do gráfico pode ser difícil de ler, mas se há muitos carrapatos o gráfico será desordenado e as etiquetas podem se sobrepor).

Outras dicas

Ainda não responder a esta pergunta ... Eu vou jogar a minha primeira idéia em seguida! Eu suponho que você tem a gama do eixo visível.

Este é provavelmente como eu faria.

áspero pseudo:

// 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

Depois disso, ele deve (dependendo do que você tem acesso fácil a) ser bastante fácil de determinar o valor para cada etiqueta tick agradável. Dependendo do 'resolução', você formatá-lo de forma diferente. Ex .: MM / DD para "semana", MM:. SS para "minuto", etc., assim como você disse

Tenha um olhar em

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

Tem uma nice.py (python / netsa / data / nice.py) que eu acho que é stand-alone, e deve funcionar bem.

Eu sugiro que você pegar o código fonte para gnuplot ou RRDTool (ou mesmo Flot) e examinar como eles abordam este problema. O caso geral é provável que seja rótulos N aplicado com base na largura do seu enredo, que algum tipo de 'tirando' para o número mais próximo 'agradável'.

Toda vez que eu escrevi um algoritmo tal (muitas vezes na verdade), eu usei uma tabela de 'preferências' ... ou seja: com base no intervalo de tempo na trama, decidir se eu estou usando Semanas , dias, horas, minutos, etc como o principal ponto de eixo. I normalmente incluídos alguma formatação preferido, como eu raramente querem ver a data de cada parcela minuto eu no gráfico.

Eu ficaria feliz, mas surpreso ao encontrar alguém usando uma fórmula (como Heckbert faz) para encontrar 'agradável', como a variação em unidades de tempo entre minutos, horas, dias e semanas não são assim tão linear.

[Edit - Eu expandiu este um pouco mais em http://www.acooke.org /cute/AutoScalin0.html ]

A extensão ingênuo do algoritmo "bons números" parece funcionar para base 12 e 60, o que dá bons intervalos de horas e minutos. Este é o código eu só cortei 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())

Assim, por exemplo, se você deseja exibir segundos de 0 a 60,

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

ou horas de 0 a 5:

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

teoria Em você também pode mudar o seu conceito. Caso não seja seus dados no centro da visualização, mas no centro você tem a sua escala.

Quando você sabe o início eo fim das datas de seus dados, você pode criar uma escala com todas as datas e despachar os seus dados nesta escala. Como um tabelas fixas.

Você pode ter uma escala de ano Tipo, mês, dia, hora, ... e limitar a escala apenas para essas escalas, o que implica que você remover o conceito de escala livre.

A vantagem é que pode facilmente mostrar datas lacunas. Mas se você tem um monte de lacunas, que pode se tornar também inútil.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top