具有最小价格变动的图表的尼斯标签算法

2022-09-01 04:04:19

我需要手动计算图表的 Ticklabels 和 Tickrange。

我知道漂亮价格变动的“标准”算法(见 http://books.google.de/books?id=fvA7zLEFWZgC&pg=PA61&lpg=PA61&redir_esc=y#v=onepage&q&f=false),我也知道这个Java实现

问题是,使用这种算法,分时“太聪明”了。这意味着,该算法决定应该显示多少价格变动。我的要求是,总是有5个蜱虫,但这些当然应该是“漂亮的”。幼稚的方法是获得最大值,除以5,然后乘以刻度数。当然,这里的值不是最佳的,并且刻度非常丑陋。

有没有人知道问题的解决方案,或者有正式算法描述的提示?


答案 1

我是“图表轴上最佳缩放算法”的作者。它曾经托管在 trollop.org 上,但我最近移动了域/博客引擎。无论如何,我会在这里发布内容,以便于访问。

我一直在为一项任务开发Android图表应用程序,并且在以精美缩放的格式呈现图表时遇到了一些问题。我花了一些时间试图自己创建这个算法,并且非常接近,但最终我在Andrew S. Glassner的一本名为“Graphics Gems,Volume 1”的书中发现了一个伪代码示例。在“图形标签的漂亮数字”一章中给出了这个问题的精彩描述:

当通过计算机创建图形时,需要用“nice”数字标记x轴和y轴:简单的十进制数。例如,如果数据范围是 105 到 543,我们可能希望绘制从 100 到 600 的范围,并每隔 100 个单位放置刻度线。或者,如果数据范围是 2.04 到 2.16,我们可能会绘制一个从 2.00 到 2.20 的范围,刻度间距为 0.05。人类擅长选择这种“好”的数字,但简单的算法却不擅长。朴素的标签选择算法获取数据范围并将其划分为 n 个相等的区间,但这通常会导致丑陋的刻度标签。我们在这里描述了一种生成漂亮图形标签的简单方法。

主要观察结果是,十进制中“最好”的数字是 1、2 和 5,以及这些数字的所有 10 次方的倍数。我们将仅使用这样的数字作为刻度线间距,并将刻度线放在刻度线间距的倍数处...

我使用本书中的伪代码示例在Java中创建以下类:

public class NiceScale {

  private double minPoint;
  private double maxPoint;
  private double maxTicks = 10;
  private double tickSpacing;
  private double range;
  private double niceMin;
  private double niceMax;

  /**
   * Instantiates a new instance of the NiceScale class.
   *
   * @param min the minimum data point on the axis
   * @param max the maximum data point on the axis
   */
  public NiceScale(double min, double max) {
    this.minPoint = min;
    this.maxPoint = max;
    calculate();
  }

  /**
   * Calculate and update values for tick spacing and nice
   * minimum and maximum data points on the axis.
   */
  private void calculate() {
    this.range = niceNum(maxPoint - minPoint, false);
    this.tickSpacing = niceNum(range / (maxTicks - 1), true);
    this.niceMin =
      Math.floor(minPoint / tickSpacing) * tickSpacing;
    this.niceMax =
      Math.ceil(maxPoint / tickSpacing) * tickSpacing;
  }

  /**
   * Returns a "nice" number approximately equal to range Rounds
   * the number if round = true Takes the ceiling if round = false.
   *
   * @param range the data range
   * @param round whether to round the result
   * @return a "nice" number to be used for the data range
   */
  private double niceNum(double range, boolean round) {
    double exponent; /** exponent of range */
    double fraction; /** fractional part of range */
    double niceFraction; /** nice, rounded fraction */

    exponent = Math.floor(Math.log10(range));
    fraction = range / Math.pow(10, exponent);

    if (round) {
      if (fraction < 1.5)
        niceFraction = 1;
      else if (fraction < 3)
        niceFraction = 2;
      else if (fraction < 7)
        niceFraction = 5;
      else
        niceFraction = 10;
    } else {
      if (fraction <= 1)
        niceFraction = 1;
      else if (fraction <= 2)
        niceFraction = 2;
      else if (fraction <= 5)
        niceFraction = 5;
      else
        niceFraction = 10;
    }

    return niceFraction * Math.pow(10, exponent);
  }

  /**
   * Sets the minimum and maximum data points for the axis.
   *
   * @param minPoint the minimum data point on the axis
   * @param maxPoint the maximum data point on the axis
   */
  public void setMinMaxPoints(double minPoint, double maxPoint) {
    this.minPoint = minPoint;
    this.maxPoint = maxPoint;
    calculate();
  }

  /**
   * Sets maximum number of tick marks we're comfortable with
   *
   * @param maxTicks the maximum number of tick marks for the axis
   */
  public void setMaxTicks(double maxTicks) {
    this.maxTicks = maxTicks;
    calculate();
  }
}

然后,我们可以像这样使用上面的代码:

NiceScale numScale = new NiceScale(-0.085, 0.173);

System.out.println("Tick Spacing:\t" + numScale.getTickSpacing());
System.out.println("Nice Minimum:\t" + numScale.getNiceMin());
System.out.println("Nice Maximum:\t" + numScale.getNiceMax());

然后,它将输出格式良好的数字,以便在您需要为其创建漂亮比例的任何应用程序中使用。=D

Tick Spacing: 0.05
Nice Minimum: -0.1
Nice Maximum: 0.2

答案 2

这是一个javascript版本:

var minPoint;
var maxPoint;
var maxTicks = 10;
var tickSpacing;
var range;
var niceMin;
var niceMax;

/**
 * Instantiates a new instance of the NiceScale class.
 *
 *  min the minimum data point on the axis
 *  max the maximum data point on the axis
 */
function niceScale( min, max) {
    minPoint = min;
    maxPoint = max;
    calculate();
    return {
        tickSpacing: tickSpacing,
        niceMinimum: niceMin,
        niceMaximum: niceMax
    };
}



/**
 * Calculate and update values for tick spacing and nice
 * minimum and maximum data points on the axis.
 */
function calculate() {
    range = niceNum(maxPoint - minPoint, false);
    tickSpacing = niceNum(range / (maxTicks - 1), true);
    niceMin =
      Math.floor(minPoint / tickSpacing) * tickSpacing;
    niceMax =
      Math.ceil(maxPoint / tickSpacing) * tickSpacing;
}

/**
 * Returns a "nice" number approximately equal to range Rounds
 * the number if round = true Takes the ceiling if round = false.
 *
 *  localRange the data range
 *  round whether to round the result
 *  a "nice" number to be used for the data range
 */
function niceNum( localRange,  round) {
    var exponent; /** exponent of localRange */
    var fraction; /** fractional part of localRange */
    var niceFraction; /** nice, rounded fraction */

    exponent = Math.floor(Math.log10(localRange));
    fraction = localRange / Math.pow(10, exponent);

    if (round) {
        if (fraction < 1.5)
            niceFraction = 1;
        else if (fraction < 3)
            niceFraction = 2;
        else if (fraction < 7)
            niceFraction = 5;
        else
            niceFraction = 10;
    } else {
        if (fraction <= 1)
            niceFraction = 1;
        else if (fraction <= 2)
            niceFraction = 2;
        else if (fraction <= 5)
            niceFraction = 5;
        else
            niceFraction = 10;
    }

    return niceFraction * Math.pow(10, exponent);
}

/**
 * Sets the minimum and maximum data points for the axis.
 *
 *  minPoint the minimum data point on the axis
 *  maxPoint the maximum data point on the axis
 */
function setMinMaxPoints( localMinPoint,  localMaxPoint) {
    minPoint = localMinPoint;
    maxPoint = localMaxoint;
    calculate();
}

/**
 * Sets maximum number of tick marks we're comfortable with
 *
 *  maxTicks the maximum number of tick marks for the axis
 */
function setMaxTicks(localMaxTicks) {
    maxTicks = localMaxTicks;
    calculate();
}

享受!