【发布时间】:2011-12-21 20:00:00
【问题描述】:
我想创建一个折线图,但数字的范围有时会有所不同:
1,2,5,8,10
有时像这样:
-5 , -1 , 4 , 5, 15 , 8 ,20 ,...
通过研究 excel 和 openoffice,我了解到它们可以指示一个范围,例如:
minNumber - k 到 maxNumber + k
它们将 Y 轴分成相等的区域。
有什么具体的公式可以做这些事情吗?
【问题讨论】:
我想创建一个折线图,但数字的范围有时会有所不同:
1,2,5,8,10
有时像这样:
-5 , -1 , 4 , 5, 15 , 8 ,20 ,...
通过研究 excel 和 openoffice,我了解到它们可以指示一个范围,例如:
minNumber - k 到 maxNumber + k
它们将 Y 轴分成相等的区域。
有什么具体的公式可以做这些事情吗?
【问题讨论】:
当我看到这个问题时,我发现商业产品在扩展范围方面做得很好,足以为其刻度线实现令人愉悦的漂亮整数。如果您对此感兴趣,请查看我为尝试不同算法而编写的这段 C# 代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TickPicker
{
class TickPicker
{
double tickLow;
double tickHigh;
double tickUnit;
double tickCount;
//static double[] targetUnits = { 1, 2, 5, 10 };
static double[] targetUnits = { 1, 2, 2.5, 5, 10 };
static int[] roundFactors = { 1, 5, 4, 2, 1 };
//static double[] targetUnits = { 1, 1.5, 2.5, 5, 10 };
//static double[] targetUnits = { 1, 1.25, 2, 2.5, 5, 10 };
//static double[] targetUnits = { 1, 1.25, 1.5, 2, 2.5, 5, 10 };
//static double[] targetUnits = { 1, 1.25, 2, 5, 10 };
//static double[] targetUnits = { 1, 1.25, 1.5, 2, 5, 10 };
static double[] targetLogs = arrayLog(targetUnits);
TickPicker(double low, double high, int tickCountGoal)
{
double range = high - low;
double divGoal = range / (tickCountGoal - 2);
double logGoal = Math.Log10(divGoal);
double powerFactor = Math.Floor(logGoal);
logGoal = logGoal - powerFactor;
int closestIndex = findClosest(targetLogs, logGoal);
tickUnit = targetUnits[closestIndex] * (Math.Pow(10, powerFactor));
// Ensure the actual range encompasses the intended range
// The roundFactor discourages the .5 on the low and high range
int roundFactor = roundFactors[closestIndex];
tickLow = Math.Floor(low / tickUnit / roundFactor) * tickUnit * roundFactor;
tickHigh = Math.Ceiling(high / tickUnit / roundFactor) * tickUnit * roundFactor;
tickCount = (tickHigh - tickLow) / tickUnit;
}
static double[] arrayLog(double[] inputs)
{
double[] retVal = new double[inputs.Length];
int x = 0;
foreach (double input in inputs)
{
retVal[x] = Math.Log10(inputs[x]);
x++;
}
return retVal;
}
static int findClosest(double[] candidates, double input)
{
int low = 0;
for(int i = 1; i < candidates.Length && input > candidates[i]; i++)
{
low = i;
}
int high = low + 1;
return candidates[high] - input < input - candidates[low] ? high : low;
}
static void testPicker(double low, double high, int tickCountGoal)
{
TickPicker picker = new TickPicker(low, high, tickCountGoal);
System.Console.WriteLine("[{0}:{1}]/{2} gives [{3}:{4}] with {5} ticks of {6} units each.", low, high, tickCountGoal, picker.tickLow, picker.tickHigh, picker.tickCount, picker.tickUnit);
}
static void Main(string[] args)
{
testPicker(4.7, 39.2, 13);
testPicker(4.7, 39.2, 16);
testPicker(4.7, 39.2, 19);
testPicker(4.7, 39.2, 21);
testPicker(4.7, 39.2, 24);
testPicker(1967, 2011, 20);
testPicker(1967, 2011, 10);
testPicker(2.71, 3.14, 5);
testPicker(.0568, 13, 20);
}
}
}
【讨论】:
我认为你的意思是 minNumber 而不是 meanNumber。
公式和你说的很接近。您的范围是 maxNumber - minNumber。如果你想要两边的空间加 2k,k 是两边的空间。你知道你的图表是 Y 像素宽。 Y/范围是您的图表每单位有多少像素。
您基本上是在绘制图形时应用此调整。根据您的最小值应用移位,然后根据您的像素/单位应用拉伸。
所以绘制点 X 意味着您实际上是在 ((X - min) / 像素/单位) 绘制。
【讨论】:
我不是 100% 相信这是一个编程问题(尽管可以使用的图形库屈指可数),但总体思路是这样的。
你有这些分数,比如说-5。这是我们收到的第 0 个值,所以对于 0 的 x 值,我们将 -5 放在 y 轴上。我们重复 -1、4 等。
所以,你会得到一个看起来像这样的列表(形象地):
X | Y
0 | -5
1 | -1
2 | 4
3 | 5
4 | 15
结果是一个散点图(不是一条线),但 Excel 中有工具可以为您获取。
[EDIT] 要将其作为一个实际函数,您可以使用以下形式:
y-y_1=m(x-x_1)
其中 m = (y_2-y_1)/(x_2-x_1),y_2 是最高 y 值,y_1 是最低值,x_2 是最高 x 值,x_1 是最低值。
【讨论】: