質問

「いい数字」を探しています。日付/時刻値軸のラベルを決定するアルゴリズム。 Paul HeckbertのNice Numbersアルゴリズム

X軸に時刻/日付を表示するプロットがあり、ユーザーはズームインしてより小さな時間枠を見ることができます。ティックに表示する素敵な日付を選択するアルゴリズムを探しています。

例:

  • 1日程度で見る:1/1 12:00、1 / 1 4:00、1 / 1 8:00 ...
  • 一週間を見る:1 / 1、1 / 2、1 / 3 ...
  • 月を見る:1 / 09、2 / 09、3 / 09 ...

ナイスラベルの目盛りは、最初の可視ポイントに対応する必要はありませんが、それに近い必要があります。

そのようなアルゴリズムに精通している人はいますか

役に立ちましたか?

解決

リンクした「いい数字」の記事で言及されている

  

10進数で最も適切な数値は、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倍になると思います。これにより、次のようになります(後で表示される間隔の最小数と最大数)

  • 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の目盛りが付いた数字。または、目盛り間の実際のピクセル数を知っている、および/または制御できる場合は、目盛りの間で許容されるピクセル数に上限と下限を設定できます(間隔が広すぎるとグラフが読みにくくなりますが、ティックが多すぎると、グラフが乱雑になり、ラベルが重なる可能性があります)。

他のヒント

この質問に対する答えはまだありません...最初のアイデアを投げます!可視軸の範囲があると思います。

これはおそらく私が行う方法です。

大まかな擬似:

// 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など、前述のとおりです。

ご覧ください

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

これにはnice.py(python / netsa / data / nice.py)がありますが、これはスタンドアロンであり、正常に動作するはずです。

ソースコードを取得してgnuplotまたはRRDTool(またはFlot)に取り込み、この問題へのアプローチを検討することをお勧めします。一般的なケースは、プロットの幅に基づいて適用されるN個のラベルです。これは、ある種の最も近い「いい」数に「スナップ」します。

このようなアルゴリズムを作成するたびに(実際には何度も)、「プリファレンス」のテーブルを使用しました。つまり、プロットの時間範囲に基づいて、週を使用するかどうかを決定します、主軸ポイントとしての日、時間、分など。グラフにプロットする1分ごとの日付を表示することはめったにないので、通常はいくつかの優先書式を含めました。

私は幸せですが、分、時間、日、週の間の時間単位の変化がそれほど線形ではないため、「Heckbertのように」数式を使用して「いい」を見つける人を見つけるのは驚きです。

[編集-これを 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']

理論的には、概念を変更することもできます。ビジュアライゼーションの中心にあるデータではなく、中心にスケールがある場合。

データの日付の開始と終了がわかったら、すべての日付でスケールを作成し、このスケールでデータをディスパッチできます。固定スケールのように。

年、月、日、時間などの種類のスケールを設定し、これらのスケールだけにスケールを制限することができます。これは、フリースケールの概念を削除することを意味します。

利点は、日付のギャップを簡単に表示できることです。しかし、多くのギャップがある場合、それも役に立たなくなる可能性があります。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top