Я ищу "хорошие числа" алгоритм для определения маркировок на оси даты/временной стоимости. Я знаком с Хорошим алгоритмом Чисел Paul Heckbert.
У меня есть график, который отображает время/дату на оси X, и пользователь может увеличить масштаб и посмотреть на меньший период времени. Я ищу алгоритм, который выбирает хорошие даты для отображения на галочках.
Например:
Хорошие галочки маркировки не должны соответствовать первой видимой точке, но близко к нему.
Действительно ли кто-либо знаком с таким алгоритмом?
В статье о "хороших числах", с которой вы связались, упоминалось, что
самыми хорошими числами в десятичной системе являются 1, 2, 5, и вся мощь 10 кратных этих чисел
Поэтому я думаю, что для того, чтобы сделать что-то похожее с датой/временем, вам нужно начать с аналогичного разбиения компонентных частей. Поэтому возьмите хорошие коэффициенты каждого типа интервала:
Теперь очевидно, что это начинает ломаться по мере того, как вы получаете в больших количествах. Конечно, вы не хотите показывать 5 недель, которые стоят минут, даже с "красивыми" интервалами в 30 минут или что-то в этом роде. С другой стороны, когда у вас всего 48 часов, вы не хотите показывать интервалы в 1 день. Хитрость, на которую вы уже обратили внимание, состоит в том, чтобы найти приличные точки перехода.
Просто по догадке, я бы сказал, что разумная точка перехода будет примерно в два раза больше, чем следующий интервал. Это даст вам следующее (минимальное и максимальное количество интервалов, показанных после)
К сожалению, наши непоследовательные временные интервалы означают, что в одних случаях вы заканчиваете с более чем 100 интервалами, в то время как в других - с более чем 8 или 9. Таким образом, вы захотите подобрать размер своих интервалов так, чтобы у вас было не более 10-15 интервалов максимум (или менее 5, если уж на то пошло). Кроме того, вы можете отступить от строгого определения 2-х кратного следующего наибольшего интервала, если считаете, что его легко отследить. Например, можно использовать часы до 3 дней (72 часа) и недели до 4 месяцев. Может понадобиться немного проб и ошибок.
Итак, чтобы вернуться назад, выберите тип интервала, исходя из размера вашего диапазона, затем выберите размер интервала, выбрав одно из "хороших" чисел, которое оставит вам от 5 до примерно 15 тиковых знаков. Или, если вы знаете и/или можете контролировать фактическое количество пикселей между тиковыми метками, вы можете поставить верхнюю и нижнюю границы допустимого количества пикселей между тиками (если они находятся слишком далеко друг от друга, граф может быть трудночитаемым, но при слишком большом количестве тиков граф будет загроможден, и ваши метки могут накладываться друг на друга).
Все еще нет ответа на этот вопрос ... Я брошу свою первую идею в то же время! Я предполагаю, что у вас есть ассортимент видимой оси.
Это, вероятно, как я бы сделал.
Грубый псевдоа:
// 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
После этого он должен (в зависимости от того, к чему у вас есть легкий доступ), довольно легко определить значение для каждой славной маркировки. В зависимости от «разрешения» вы отформатируете его по-разному. E.G.: MM / DD для «Неделя», мм: SS для «минуты» и т. Д. Так же, как вы сказали.
Я рекомендую вам взять исходный код GNUPLOT или RRDTOOL (или даже flot) и изучить, как они приближаются к этой проблеме. Общий корпус, скорее всего, будет N наклейки, применяемые на основе ширины вашего сюжета, который какой-то «привязанный» к ближайшему «приятному» числу.
Каждый раз, когда я написал такой алгоритм (слишком много раз на самом деле), я использовал таблицу «предпочтений» ... т. Е.: В зависимости от временного диапазона на сюжете, решите, используете ли я недели Дни, часы, минуты и т. Д. В качестве основной точки оси. Я обычно включаю в себя некоторые предпочтительные форматирования, так как я редко хочу увидеть дату каждую минуту, которую я замышляю на график.
Я был бы счастлив, но удивлен, чтобы найти кого-то, используя формулу (например, Хекберт делает), чтобы найти «Ницца», как вариация во временных единицах между протоколами, часами, днями и недельми, не в том числе линейным.
Взгляните на
http://tools.netsa.cert.org/netsa-python/doc/index.html
Там есть nice.py (python / netsa / data / nice.py ), который я считаю автономным и должен нормально работать.
[Edit - я немного расширил это на 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']