Алгоритм «хороших» интервалов линий сетки на графике - PullRequest
59 голосов
/ 12 декабря 2008

Мне нужен достаточно умный алгоритм, чтобы придумать «красивые» линии сетки для графика (диаграммы).

Например, предположим гистограмму со значениями 10, 30, 72 и 60. Вы знаете:

Минимальное значение: 10 Максимальное значение: 72 Диапазон: 62

Первый вопрос: с чего начать? В этом случае 0 будет интуитивно понятным значением, но оно не будет поддерживаться другими наборами данных, поэтому я предполагаю:

Минимальное значение сетки должно быть либо 0, либо "хорошим" значением, которое меньше минимального значения данных в диапазоне. В качестве альтернативы его можно указать.

Максимальное значение сетки должно быть «хорошим» значением выше максимального значения в диапазоне. В качестве альтернативы его можно указать (например, вы можете захотеть от 0 до 100, если вы показываете проценты, независимо от фактических значений).

Количество линий сетки (отметок) в диапазоне должно быть либо указано, либо число в данном диапазоне (например, 3-8), чтобы значения были «хорошими» (т. Е. Круглыми числами), и вы максимально используете область диаграммы. В нашем примере 80 было бы разумным максимумом, поскольку при этом использовалось бы 90% высоты диаграммы (72/80), тогда как 100 создавало бы больше потерянного пространства.

Кто-нибудь знает хороший алгоритм для этого? Язык не имеет значения, так как я буду реализовывать его так, как мне нужно.

Ответы [ 14 ]

0 голосов
/ 08 апреля 2016

Если вы хотите, чтобы шкалы выглядели правильно на диаграммах VB.NET, то я использовал пример от Адама Лисса, но убедитесь, что при установке значений шкалы min и max вы передаете их из переменной типа decimal (не типа single или double), в противном случае значения отметки в конечном итоге устанавливаются равными 8 десятичным знакам. Так, в качестве примера, у меня был 1 график, где я установил значение минимальной оси Y на 0,0001, а максимальное значение оси Y на 0,002. Если я передаю эти значения объекту диаграммы в виде синглов, я получаю значения отметок 0.00048000001697801, 0.000860000036482233 .... Принимая во внимание, что если я передаю эти значения объекту диаграммы в виде десятичных дробей, я получаю хорошие значения отметок 0,00048, 0,00086 ......

0 голосов
/ 07 февраля 2016

Вот функция javascript, которую я написал для интервалов круглой сетки (max-min)/gridLinesNumber для красивых значений. Он работает с любыми номерами, см. gist с подробными комментариями, чтобы узнать, как он работает и как его вызывать.

var ceilAbs = function(num, to, bias) {
  if (to == undefined) to = [-2, -5, -10]
  if (bias == undefined) bias = 0
  var numAbs = Math.abs(num) - bias
  var exp = Math.floor( Math.log10(numAbs) )

    if (typeof to == 'number') {
        return Math.sign(num) * to * Math.ceil(numAbs/to) + bias
    }

  var mults = to.filter(function(value) {return value > 0})
  to = to.filter(function(value) {return value < 0}).map(Math.abs)
  var m = Math.abs(numAbs) * Math.pow(10, -exp)
  var mRounded = Infinity

  for (var i=0; i<mults.length; i++) {
    var candidate = mults[i] * Math.ceil(m / mults[i])
    if (candidate < mRounded)
      mRounded = candidate
  }
  for (var i=0; i<to.length; i++) {
    if (to[i] >= m && to[i] < mRounded)
      mRounded = to[i]
  }
  return Math.sign(num) * mRounded * Math.pow(10, exp) + bias
}

При вызове ceilAbs(number, [0.5]) для разных номеров округляются такие числа:

301573431.1193228 -> 350000000
14127.786597236991 -> 15000
-63105746.17236853 -> -65000000
-718854.2201183736 -> -750000
-700660.340487957 -> -750000
0.055717507097870114 -> 0.06
0.0008068701205775142 -> 0.00085
-8.66660070605576 -> -9
-400.09256079792976 -> -450
0.0011740548815578223 -> 0.0015
-5.3003294346854085e-8 -> -6e-8
-0.00005815960629843176 -> -0.00006
-742465964.5184875 -> -750000000
-81289225.90985894 -> -85000000
0.000901771713513881 -> 0.00095
-652726598.5496342 -> -700000000
-0.6498901364393532 -> -0.65
0.9978325804695487 -> 1
5409.4078950583935 -> 5500
26906671.095639467 -> 30000000

Проверьте скрипку , чтобы поэкспериментировать с кодом. Код в ответе, суть и скрипка немного отличаются. Я использую тот, который приведен в ответе.

0 голосов
/ 04 октября 2013

В R используйте

tickSize <- function(range,minCount){
    logMaxTick <- log10(range/minCount)
    exponent <- floor(logMaxTick)
    mantissa <- 10^(logMaxTick-exponent)
    af <- c(1,2,5) # allowed factors
    mantissa <- af[findInterval(mantissa,af)]
    return(mantissa*10^exponent)
}

где аргумент диапазона - это максимум-мин домена.

0 голосов
/ 05 августа 2013

Используя много вдохновения от ответов, уже доступных здесь, вот моя реализация на C. Обратите внимание, что в массив ndex встроена некоторая расширяемость.

float findNiceDelta(float maxvalue, int count)
{
    float step = maxvalue/count,
         order = powf(10, floorf(log10(step))),
         delta = (int)(step/order + 0.5);

    static float ndex[] = {1, 1.5, 2, 2.5, 5, 10};
    static int ndexLenght = sizeof(ndex)/sizeof(float);
    for(int i = ndexLenght - 2; i > 0; --i)
        if(delta > ndex[i]) return ndex[i + 1] * order;
    return delta*order;
}
...