Domanda

Sto cercando un "bel numero" algoritmo per determinare le etichette su un asse valore data / ora. Conosco L'algoritmo di Numeri Nizza di Paul Heckbert .

Ho un grafico che mostra l'ora / la data sull'asse X e l'utente può ingrandire e guardare un intervallo di tempo più piccolo. Sto cercando un algoritmo che raccolga belle date da visualizzare sulle zecche.

Ad esempio:

  • Guardando circa un giorno: 1/1 12:00, 1/1 4:00, 1/1 8:00 ...
  • Guardando una settimana: 1/1, 1/2, 1/3 ...
  • Guardando un mese: 1/09, 2/09, 3/09 ...

I segni di spunta delle belle etichette non devono corrispondere al primo punto visibile, ma vicino ad esso.

Qualcuno ha familiarità con un tale algoritmo?

È stato utile?

Soluzione

L'articolo dei "bei numeri" a cui hai collegato lo ha menzionato

  

i numeri più belli in decimale sono 1, 2, 5 e tutti i multipli di potenza di 10 di questi numeri

Quindi penso che per fare qualcosa di simile con data / ora devi iniziare analizzando in modo simile i componenti. Quindi prendi i bei fattori di ogni tipo di intervallo:

  • Se stai mostrando secondi o minuti usa 1, 2, 3, 5, 10, 15, 30 (Ho saltato 6, 12, 15, 20 perché non "si sentono").
  • Se stai mostrando le ore usa 1, 2, 3, 4, 6, 8, 12
  • per giorni usa 1, 2, 7
  • per settimane usa 1, 2, 4 (13 e 26 si adattano al modello ma mi sembrano troppo strani)
  • per mesi usare 1, 2, 3, 4, 6
  • per anni usa 1, 2, 5 e multipli di potenza di 10

Ora, ovviamente, questo inizia a rompersi man mano che si ottengono importi maggiori. Certamente non vuoi mostrare 5 minuti di minuti, anche in "abbastanza". intervalli di 30 minuti o qualcosa del genere. D'altra parte, quando hai solo 48 ore, non vuoi mostrare intervalli di 1 giorno. Il trucco come hai già sottolineato è trovare punti di transizione decenti.

Solo per un sospetto, direi che un ragionevole punto di crossover sarebbe circa il doppio dell'intervallo successivo. Ciò ti darebbe quanto segue (numero minimo e massimo di intervalli mostrati in seguito)

  • usa i secondi se hai meno di 2 minuti (1-120)
  • usa i minuti se hai meno di 2 ore (2-120)
  • utilizza le ore se hai meno di 2 giorni (2-48)
  • usa i giorni se hai meno di 2 settimane (2-14)
  • usa le settimane se hai meno di 2 mesi (2-8 / 9)
  • usa i mesi se hai meno di 2 anni (2-24)
  • altrimenti usa anni (anche se potresti continuare con decenni, secoli, ecc. se le tue gamme possono essere così lunghe)

Sfortunatamente, i nostri intervalli di tempo incoerenti significano che si finiscono con alcuni casi che possono avere oltre 1 100 intervalli mentre altri ne hanno al massimo 8 o 9. Quindi ti consigliamo di scegliere la dimensione dei tuoi intervalli in modo che tu non ' hanno al massimo più di 10-15 intervalli (o meno di 5 per quella materia). Inoltre, potresti rompere da una definizione rigorosa di 2 volte l'intervallo più grande successivo se ritieni che sia facile tenerne traccia. Ad esempio, è possibile utilizzare ore fino a 3 giorni (72 ore) e settimane fino a 4 mesi. Potrebbe essere necessario un piccolo tentativo ed errore.

Quindi, per tornare indietro, scegli il tipo di intervallo in base alla dimensione del tuo intervallo, quindi scegli la dimensione dell'intervallo selezionando uno dei "simpatici" numeri che ti lasceranno tra 5 e circa 15 segni di spunta. Oppure, se conosci e / o riesci a controllare il numero effettivo di pixel tra i segni di spunta, potresti mettere limiti superiori e inferiori su quanti pixel sono accettabili tra i segni di spunta (se sono distanziati troppo lontano il grafico potrebbe essere difficile da leggere, ma se ci sono troppi segni di spunta il grafico sarà disordinato e le etichette potrebbero sovrapporsi).

Altri suggerimenti

Ancora nessuna risposta a questa domanda ... Poi lancerò la mia prima idea! Presumo che tu abbia la gamma dell'asse visibile.

Probabilmente è così che farei.

Pseudo ruvido:

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

Dopodiché, dovrebbe essere abbastanza facile (a seconda di ciò a cui si ha facile accesso) determinare il valore per ogni segno di spunta dell'etichetta. A seconda della "risoluzione", la si formatta diversamente. Ad esempio: MM / GG per "settimana", MM: SS per "minuto", ecc., Proprio come hai detto.

Dai un'occhiata a

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

Ha un nice.py (python / netsa / data / nice.py) che penso sia autonomo e dovrebbe funzionare bene.

Suggerirei di prendere il codice sorgente su gnuplot o RRDTool (o anche su Flot) ed esaminare come affrontano questo problema. È probabile che il caso generale siano N etichette applicate in base alla larghezza della trama, che una sorta di "aggancio" al numero "bello" più vicino.

Ogni volta che ho scritto un tale algoritmo (troppe volte in realtà), ho usato una tabella di 'preferenze' ... cioè: in base all'intervallo di tempo sulla trama, decidi se sto usando Weeks , Giorni, ore, minuti ecc. Come punto dell'asse principale. Di solito ho incluso una formattazione preferita, poiché raramente voglio vedere la data per ogni minuto che traccia sul grafico.

Sarei felice ma sorpreso di trovare qualcuno che usa una formula (come fa Heckbert) per trovare "bello", poiché la variazione delle unità di tempo tra minuti, ore, giorni e settimane non è così lineare.

[Modifica - L'ho ampliato un po 'di più su http://www.acooke.org /cute/AutoScalin0.html ]

Un'estensione ingenua dei "bei numeri" l'algoritmo sembra funzionare per le basi 12 e 60, che fornisce buoni intervalli per ore e minuti. Questo è il codice che ho appena violato insieme:

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

Quindi, ad esempio, se si desidera visualizzare i secondi da 0 a 60,

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

o ore da 0 a 5:

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

In teoria puoi anche cambiare il tuo concetto. Dove non sono i tuoi dati al centro della visualizzazione, ma al centro hai la tua scala.

Quando conosci l'inizio e la fine delle date dei tuoi dati, puoi creare una scala con tutte le date e inviare i tuoi dati in questa scala. Come una bilancia fissa.

Puoi avere una scala di tipo anno, mese, giorno, ore ... e limitare il ridimensionamento solo a queste scale, il che implica che rimuovi il concetto di ridimensionamento gratuito.

Il vantaggio è di mostrare facilmente le lacune nelle date. Ma se hai molte lacune, anche questo può diventare inutile.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top