Алгоритм хороших графических меток для оси времени / даты? - PullRequest
21 голосов
/ 14 сентября 2009

Я ищу алгоритм «хороших чисел» для определения меток на оси значений даты / времени. Я знаком с алгоритмом хороших чисел Пола Хекберта .

У меня есть график, который отображает время / дату по оси X, и пользователь может увеличить масштаб и посмотреть на меньшем временном интервале. Я ищу алгоритм, который выбирает хорошие даты для отображения на галочках.

Например:

  • Глядя на день или около того: 1/1 12:00, 1/1 4:00, 1/1 8:00 ...
  • Глядя на неделю: 1/1, 1/2, 1/3 ...
  • Глядя на месяц: 1/09, 2/09, 3/09 ...

Хорошие отметки на ярлыках не обязательно должны соответствовать первой видимой точке, но должны быть близки к ней.

Кто-нибудь знаком с таким алгоритмом?

Ответы [ 6 ]

8 голосов
/ 27 января 2010

В статье «Хорошие числа», на которую вы ссылались, упоминалось, что

самые хорошие числа в десятичном виде - 1, 2, 5 и все кратные степени этих 10

Так что я думаю, что для того, чтобы сделать что-то похожее с датой / временем, вам нужно начать с аналогичного разбивания частей компонента. Итак, возьмите хорошие факторы каждого типа интервалов:

  • Если вы показываете секунды или минуты, используйте 1, 2, 3, 5, 10, 15, 30 (Я пропустил 6, 12, 15, 20, потому что они не «чувствуют» себя правильно).
  • Если вы показываете часы, используйте 1, 2, 3, 4, 6, 8, 12
  • для дней использования 1, 2, 7
  • в течение недели используйте 1, 2, 4 (13 и 26 подходят для модели, но мне это кажется слишком странным)
  • для месяцев использования 1, 2, 3, 4, 6
  • для лет использовать 1, 2, 5 и кратные степени 10

Теперь, очевидно, это начинает разрушаться, когда вы получаете большие суммы. Конечно, вы не хотите показывать по 5 недель минут, даже с «красивыми» интервалами в 30 минут или около того. С другой стороны, когда у вас есть только 48 часов, вы не хотите показывать интервалы в 1 день. Уловка, как вы уже указали, заключается в поиске достойных точек перехода.

Просто догадываясь, я бы сказал, что разумная точка пересечения будет примерно вдвое больше, чем следующий интервал. Это даст вам следующее (минимальное и максимальное количество интервалов, показанных впоследствии)

  • используйте секунды, если у вас стоит менее 2 минут (1-120)
  • используйте минуты, если у вас меньше 2 часов (2-120)
  • используйте часы, если у вас стоит менее 2 дней (2-48)
  • используйте дни, если у вас стоит менее 2 недель (2-14)
  • используйте недели, если у вас стоит менее 2 месяцев (2-8 / 9)
  • используйте месяцы, если у вас стоит менее 2 лет (2-24)
  • в противном случае используйте годы (хотя вы можете продолжить с десятилетиями, веками и т. Д., Если ваши диапазоны могут быть такими длинными)

К сожалению, наши несовместимые временные интервалы означают, что в некоторых случаях у вас может быть более 100 интервалов, в то время как в других - не более 8 или 9. Поэтому вы захотите выбрать размер своих интервалов, такой как вы не делаете. Максимум 10-15 интервалов (или менее 5). Кроме того, вы можете отказаться от строгого определения в 2 раза на следующем самом большом интервале, если считаете, что его легко отслеживать. Например, вы можете использовать часы до 3 дней (72 часа) и недели до 4 месяцев. Может потребоваться небольшая проба и ошибка.

Итак, чтобы вернуться назад, выберите тип интервала, основанный на размере вашего диапазона, затем выберите размер интервала, выбрав одно из «хороших» чисел, которые оставят вас от 5 до около 15 отметок. Или, если вы знаете и / или можете контролировать фактическое количество пикселей между отметками, вы можете поставить верхнюю и нижнюю границы допустимого количества пикселей между отметками (если они расположены слишком далеко друг от друга, график может быть трудно читаемым, но если слишком много тиков, график будет загроможден, и ваши ярлыки могут перекрываться).

1 голос
/ 12 апреля 2010

Посмотрите на

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

У него есть nice.py (python / netsa / data / nice.py), который я считаю автономным и должен нормально работать.

1 голос
/ 22 января 2010

Все еще нет ответа на этот вопрос ... Тогда я добавлю свою первую идею! Я предполагаю, что у вас есть диапазон видимой оси.

Наверное, так бы и поступили.

Грубое псевдо:

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

После этого должно быть (в зависимости от того, к чему у вас есть легкий доступ) довольно легко определить значение для каждого красивого ярлыка. В зависимости от «разрешения» вы форматируете его по-разному. Например, MM / DD для «недели», MM: SS для «минуты» и т. Д., Как вы и сказали.

0 голосов
/ 13 февраля 2018

Теоретически вы также можете изменить свою концепцию. Если в центре визуализации находятся не ваши данные, а центр, то у вас есть шкала.

Когда вы знаете начало и конец дат ваших данных, вы можете создать шкалу со всеми датами и отправить вам данные в этом масштабе. Как фиксированные весы.

Вы можете иметь шкалу типа year, month, day, hours, ... и ограничивать масштабирование только этими масштабами, подразумевая, что вы удаляете понятие свободного масштабирования.

Преимущество состоит в том, что можно легко показывать пробелы в датах. Но если у вас много пробелов, это также может стать бесполезным.

0 голосов
/ 27 июля 2010

[Редактировать - я расширил это немного больше на http://www.acooke.org/cute/AutoScalin0.html]

Наивное расширение алгоритма "хороших чисел", кажется, работает для оснований 12 и 60, что дает хорошие интервалы для часов и минут. Это код, который я только что взломал:

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

Так, например, если вы хотите отобразить секунды от 0 до 60,

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

или часы от 0 до 5:

>>> heckbert(0, 5, limits=LIM12)
['0', '2', '4', '6']
0 голосов
/ 27 января 2010

Я бы посоветовал вам захватить исходный код для gnuplot или RRDTool (или даже для Flot) и изучить, как они подходят к этой проблеме. В общем случае, скорее всего, будет применено N меток, основанных на ширине вашего графика, что является своего рода «привязкой» к ближайшему «красивому» числу.

Каждый раз, когда я писал такой алгоритм (на самом деле слишком много раз), я использовал таблицу «предпочтений» ... то есть: основываясь на временном диапазоне на графике, решите, используете ли я недели , Дни, часы, минуты и т. Д. В качестве основной оси. Я обычно включал предпочтительное форматирование, так как редко хочу видеть дату каждой минуты, которую я наносил на график.

Я был бы рад, но удивился, обнаружив, что кто-то использует формулу (как это делает Гекберт), чтобы найти «хороший», поскольку изменение в единицах времени между минутами, часами, днями и неделями не столь линейно.

...