2009-09-14 22 views
21

我正在尋找用於確定日期/時間值軸上的標籤的「漂亮數字」算法。我熟悉Paul Heckbert's Nice Numbers algorithm針對時間/日期軸的漂亮圖形標籤的算法?

我有一個在X軸上顯示時間/日期的圖表,用戶可以放大並查看較小的時間範圍。我正在尋找一種算法,可以在日誌中顯示出好日期。

例如:

  • 望着天左右:1/1 12:00,下午4:00 1/1,1/1 8:00 ...
  • 綜觀一週:1/1,1/2,1/3 ...
  • 縱觀一個月:1/09,2/09,3/09 ......

的漂亮標籤蜱不需要對應於第一個可見點,但靠近它。

是否有人熟悉這樣的算法?

回答

6

您連接到「漂亮的數字」的文章提到,

十進制最好的數字是1,2,5和這些數字

所以所有冪的倍數爲:10我認爲,爲了做類似的日期/時間,你需要以類似的方式分解組件。因此,採取每種類型的間隔的nice因素:

  • 如果您表示秒鐘或幾分鐘使用1,2,3,5,10,15,30 (I跳過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-8/9)
  • 使用月份(如果您少於2個月)年價值(2-24)
  • 以其它方式使用多年(儘管你可以用幾十年,幾百年,等繼續,如果你的範圍可以是長)

不幸的是,我們的不一致的時間間隔意味着你最終一些情況下可能有超過100個間隔,而其他情況下最多有8個或9個。因此,您需要選擇間隔的大小,因爲您最多不超過10-15個間隔(或少於5個間隔對於這個問題)。此外,如果您認爲易於追蹤,您可以從下一個最大間隔的2倍的嚴格定義中突破。例如,您可以使用長達3天(72小時)和長達4個月的小時。一些試驗和錯誤可能是必要的。

所以要回過頭來,根據您的範圍的大小選擇間隔類型,然後通過選擇其中一個「好」數字來選擇間隔大小,這會在5到大約15個刻度線之間留下空間。或者,如果您知道和/或可以控制刻度線之間的實際像素數,則可以在刻度線之間放置多少個像素的上限和下限(如果間距太遠,圖形可能難以讀取,但如果圖表過多,圖表混亂,標籤可能重疊)。

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等等,就像你說的那樣。

+0

像「1.5天」,「9天」等事情在實施方面(對我而言)高度依賴於語言。例如,在C甚至C++中,我只是使用unsigned long來保存兩次之間的差值,而在Java中,我可能會創建一個Time或Moment類,並且可能已經有一些地方存在這些類。 .. – Joanis 2010-01-22 04:41:42

0

我建議你抓住源代碼gnuplot或RRDTool(甚至Flot),並檢查他們如何解決這個問題。一般情況下,可能會根據您的情節的寬度應用N個標籤,這種情況會將某種「貼緊」到最近的「好」數字。我每次寫這樣的算法(真的太多次了),我已經使用了一個'首選項'表...即:根據圖上的時間範圍,決定我是否使用周,天,小時,分鐘等作爲主軸點。我通常會包含一些首選的格式,因爲我很少想看到圖表上每分鐘的日期。

我很高興但很驚訝地發現有人使用公式(像Heckbert那樣)找到'好',因爲分鐘,小時,天和星期之間的時間單位變化不是線性的。

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'] 
0

理論上你也可以改變你的概念。如果數據不在可視化的中心,但在中心,則可以獲得您的規模。

當您知道數據日期的開始和結束時,您可以創建一個包含所有日期的比例,並以此比例分派您的數據。像一個固定的秤。

您可以使用年,月,日,小時等類型的比例尺,並將縮放比例限制爲這些比例尺,這意味着您可以刪除自由比例尺的概念。

優點是可以很容易地顯示日期的差距。但是如果你有很多差距,那也可能變得沒用。