27

表示する範囲、表示する幅、目盛りの文字列幅を測定する関数を指定して、軸に目盛りを配置するアルゴリズムを探しています。

たとえば、1e-6から5e-6の間に表示する必要があり、ピクセル単位で表示する幅がある場合、アルゴリズムは、1e-6、2e-6、3e-6に(たとえば)目盛りを付ける必要があると判断します。 、4e-6、および5e-6。幅が狭い場合、最適な配置は偶数の位置、つまり2e-6と4e-6のみであると判断される場合があります(より多くの目盛りを付けるとオーバーラップするため)。

スマートアルゴリズムは、10、5、および2の倍数の目盛りを優先します。また、スマートアルゴリズムは、ゼロを中心に対称になります。

4

4 に答える 4

12

これまでに見つけた解決策はどれも気に入らなかったので、独自の解決策を実装しました。これは C# ですが、他の言語に簡単に翻訳できます。

基本的に、可能なステップのリストから、すべての値を表示する最小のものを選択し、エッジに値を正確に残すことなく、使用する可能なステップを簡単に選択できます (醜いif-else ifブロックを編集する必要はありません)。任意の範囲をサポートします。値の。Tuple簡単なデモンストレーションのために、C# を使用して3 つの値を返しました。

private static Tuple<decimal, decimal, decimal> GetScaleDetails(decimal min, decimal max)
{
    // Minimal increment to avoid round extreme values to be on the edge of the chart
    decimal epsilon = (max - min) / 1e6m;
    max += epsilon;
    min -= epsilon;
    decimal range = max - min;

    // Target number of values to be displayed on the Y axis (it may be less)
    int stepCount = 20;
    // First approximation
    decimal roughStep = range / (stepCount - 1);

    // Set best step for the range
    decimal[] goodNormalizedSteps = { 1, 1.5m, 2, 2.5m, 5, 7.5m, 10 }; // keep the 10 at the end
    // Or use these if you prefer:  { 1, 2, 5, 10 };

    // Normalize rough step to find the normalized one that fits best
    decimal stepPower = (decimal)Math.Pow(10, -Math.Floor(Math.Log10((double)Math.Abs(roughStep))));
    var normalizedStep = roughStep * stepPower;
    var goodNormalizedStep = goodNormalizedSteps.First(n => n >= normalizedStep);
    decimal step = goodNormalizedStep / stepPower;

    // Determine the scale limits based on the chosen step.
    decimal scaleMax = Math.Ceiling(max / step) * step;
    decimal scaleMin = Math.Floor(min / step) * step;

    return new Tuple<decimal, decimal, decimal>(scaleMin, scaleMax, step);
}

static void Main()
{
    // Dummy code to show a usage example.
    var minimumValue = data.Min();
    var maximumValue = data.Max();
    var results = GetScaleDetails(minimumValue, maximumValue);
    chart.YAxis.MinValue = results.Item1;
    chart.YAxis.MaxValue = results.Item2;
    chart.YAxis.Step = results.Item3;
}
于 2018-04-19T01:09:49.963 に答える
2

ゼロ (ゼロが範囲外の場合はグラフ全体) 付近の最長のセグメントを取得します。たとえば、[-5, 1] の範囲に何かがある場合は、[-5,0] を取得します。

このセグメントのおおよその長さをティックで計算します。これは、長さを目盛りの幅で割っただけです。したがって、メソッドが -5 から 0 までの 11 ティックを入れることができると言っているとします。これが上限です。短辺については、長辺の結果をミラーリングします。

ここで、各ティックのマーカーが i*10*10^n、i*5*10^n、i*2*10^n の形式になるように、できるだけ多く (最大 11) ティックを挿入してみてください。 n は整数で、i はティックのインデックスです。これは最適化の問題です。入力できるティックの数を最大化すると同時に、最後のティックと結果の終わりの間の距離を最小限に抑えたいと考えています。したがって、上限よりも少ない、できるだけ多くのティックを取得するためのスコアを割り当て、n に近い最後のティックを取得するためのスコアを割り当てます。ここで実験する必要があります。

上記の例では、n = 1 を試してください。1 ティック (i=0) が得られます。n = 2 は 1 ティックを与え、下限から離れているので、逆方向に行かなければならないことがわかります。n = 0 では、各整数ポイントで 6 ティックが得られます。n = -1 では、12 ティック (0、-0.5、...、-5.0) が得られます。n = -2 は 24 ティック、などです。スコアリング アルゴリズムはそれぞれにスコアを与えます。スコアが高いほど、より良い方法を意味します。

i * 5 * 10^n と i*2*10^n に対してこれを繰り返し、最高のスコアを取得します。

(スコアリングアルゴリズムの例として、スコアは最後のティックまでの距離にティックの最大数から必要な数を引いたものであるとします。これはおそらく悪いことですが、適切な出発点として機能します)。

于 2008-10-27T04:09:01.600 に答える
1

jQuery flotグラフ ライブラリを使用しています。これはオープン ソースであり、軸/目盛りの生成が非常にうまく行われます。そのコードを見て、そこからいくつかのアイデアをつまむことをお勧めします。

于 2008-10-27T05:43:05.780 に答える