Подтвердить что ты не робот

Алгоритм отметки для оси графика

Я ищу алгоритм, который помещает отметки на ось, учитывая диапазон отображения, ширину, чтобы отобразить его, и функцию для измерения ширины строки для отметки галочки.

Например, учитывая, что мне нужно отображать между 1е-6 и 5е-6 и шириной для отображения в пикселях, алгоритм определит, что я должен поставить отметки (например) на 1е-6, 2е-6, 3e-6, 4e-6 и 5e-6. Учитывая меньшую ширину, он может решить, что оптимальное размещение находится только в четных положениях, то есть 2e-6 и 4e-6 (поскольку добавление большего количества тиков приведет к их перекрытию).

Умный алгоритм будет отдавать предпочтение тиккерам в 10, 5 и 2. Кроме того, интеллектуальный алгоритм будет симметричным вокруг нуля.

4b9b3361

Ответ 2

Поскольку мне не понравилось ни одно из найденных решений, я реализовал собственное. Это в С#, но это может быть легко переведено на любой другой язык.

Он в основном выбирает из списка возможных шагов наименьший, который отображает все значения, не оставляя значения точно по краю, позволяет легко выбирать, какие возможные шаги вы хотите использовать (без необходимости редактировать уродливые блоки if-else if), и поддерживает любой диапазон значений. Я использовал С# Tuple для возврата трех значений просто для быстрой и простой демонстрации.

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;
}

Ответ 3

Возьмите самый длинный из сегментов вокруг нуля (или весь график, если нуль не находится в диапазоне) - например, если у вас что-то есть на диапазоне [-5, 1], возьмите [-5,0].

Примерно укажите, как долго будет длиться этот сегмент в тиках. Это просто деление длины на ширину тика. Предположим, что метод говорит, что мы можем поместить 11 тиков от -5 до 0. Это наша верхняя граница. Для более короткой стороны мы просто отразим результат на более длинной стороне.

Теперь попробуйте ввести столько (до 11) тиков, что маркер для каждого тика в форме я * 10 * 10 ^ n, я * 5 * 10 ^ n, я * 2 * 10 ^ n, где n - целое число, я - индекс тика. Теперь это проблема оптимизации - мы хотим максимизировать количество тиков, которые мы можем вставить, и в то же время минимизировать расстояние между последним тиком и окончанием результата. Поэтому назначьте оценку для получения как можно большего количества клещей, чем наша верхняя граница, и назначьте счет, чтобы получить последний тик рядом с n - вам придется экспериментировать здесь.

В приведенном выше примере попробуйте n = 1. Мы получим 1 тик (при я = 0). n = 2 дает нам 1 тик, и мы дальше от нижней границы, поэтому мы знаем, что нам нужно идти в другую сторону. n = 0 дает нам 6 тиков в каждой целочисленной точке. n = -1 дает нам 12 тиков (0, -0,5,..., -5,0). n = -2 дает нам 24 тика и так далее. Алгоритм подсчета даст каждому из них баллы - высшее означает лучший метод.

Сделайте это снова для я * 5 * 10 ^ n и я * 2 * 10 ^ n и возьмите тот, у которого лучший результат.

(в качестве примера алгоритм скоринга, скажем, что оценка - это расстояние до последнего тика, максимальное количество тиков минус необходимое число. Это, вероятно, будет плохо, но это будет достойной отправной точкой).

Ответ 4

Я использую библиотеку JQuery Flot Graph. Он с открытым исходным кодом и делает генерацию осей/тиков довольно хорошо. Я бы посоветовал взглянуть на код и уловить некоторые идеи оттуда.

Ответ 5

Этот простой алгоритм дает интервал, кратный 1, 2 или 5 раз мощности 10. И диапазон осей делится по меньшей мере на 5 интервалов. Пример кода представлен на языке Java:

protected double calculateInterval(double range) {
    double x = Math.pow(10.0, Math.floor(Math.log10(range)));
    if (range / x >= 5)
        return x;
    else if (range / (x / 2.0) >= 5)
        return x / 2.0;
    else
        return x / 5.0;
}

Это альтернатива, для минимум 10 интервалов:

protected double calculateInterval(double range) {
    double x = Math.pow(10.0, Math.floor(Math.log10(range)));
    if (range / (x / 2.0) >= 10)
        return x / 2.0;
    else if (range / (x / 5.0) >= 10)
        return x / 5.0;
    else
        return x / 10.0;
}

Ответ 6

Каков ваш язык разработки? У меня есть контроль графа на С++, который легко решает эту проблему, используя комбинацию логарифмов, celings и т.д. Если вы хотите, можете объяснить код для вас.