时间/日期轴的漂亮图标签算法?

时间:2009-09-14 00:16:21

标签: algorithm graph label

我正在寻找一个“好数字”算法来确定日期/时间值轴上的标签。我熟悉Paul Heckbert's Nice Numbers algorithm

我有一个在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 个答案:

答案 0 :(得分:8)

您链接的“好数字”文章提到了

  

十进制中最好的数字是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.所以你要选择你的间隔大小,而不是你不要t最多有10-15个间隔(或者少于5个间隔)。此外,如果您认为很容易跟踪,那么您可以从严格定义中断下一个最大间隔的2倍。例如,您可以使用最多3天(72小时)和数周(最多4个月)的工作时间。可能需要进行一些试验和错误。

所以要重新选择,根据范围的大小选择间隔类型,然后通过选择一个“漂亮”数字来选择间隔大小,这将使您留下5到15个刻度线。或者,如果您知道和/或可以控制刻度线之间的实际像素数,则可以在刻度线之间设置可接受的像素的上限和下限(如果它们间隔太远,则图形可能难以读取,但如果有太多的刻度线图形将会混乱,并且您的标签可能会重叠。)

答案 1 :(得分:1)

仍然没有回答这个问题......我会在那时抛出我的第一个想法!我假设你有可见轴的范围。

这可能就是我的意思。

粗伪:

// 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为“分钟”等,就像你说的那样。

答案 2 :(得分:1)

看看

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

它有一个nice.py(python / netsa / data / nice.py),我认为它是独立的,应该可以正常工作。

答案 3 :(得分:0)

我建议你把源代码抓到gnuplot或RRDTool(甚至是Flot),然后检查他们如何处理这个问题。一般情况可能是根据你的情节宽度应用了N个标签,这种情况会“捕捉”到最近的“漂亮”数字。

每次我写这样的算法(真的太多次),我都会使用一个'偏好'表...即:根据情节的时间范围,决定我是否使用周,Days,Hours,Minutes等作为主轴点。我通常包括一些首选格式,因为我很少想看到我在图表上绘制的每一分钟的日期。

我很高兴但很惊讶地发现有人使用公式(像Heckbert那样)找到'好',因为时间单位在分钟,小时,天和周之间的变化不是线性的。

答案 4 :(得分:0)

[编辑 - 我在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']

答案 5 :(得分:0)

理论上你也可以改变你的观念。如果不是您的数据位于可视化的中心,而是位于中心,则表示您的数据。

当您知道数据日期的开始和结束时,您可以创建包含所有日期的比例,并按此比例分配数据。像一个固定的尺度。

您可以使用年,月,日,小时等类型的比例,并将比例缩放到这些比例,这意味着您删除了自由缩放的概念。

优点是可以轻松显示日期差距。但是如果你有很多差距,那也会变得毫无用处。