我在画一些图表,横轴上有时间,纵轴上有价格。
价格可以从0.23487到0.8746或20.47%到45.48%或1.4578到1.6859或9000到12000……你明白了吧,任何范围都可能在那里。数字的精度也可能不同(但通常是2位小数或4位小数)。
现在,在纵轴上,我需要显示价格,但不是所有的价格,只显示一些重要的水平。我需要显示尽可能多的重要级别,但这些级别彼此之间的距离不应超过30像素(。
因此,如果我有一个包含数据的图表,其价格范围从1.4567到1.6789,图表高度为500时,我可以显示最多16个显着水平。可见价格范围为1.6789-1.4567=0.2222。0.2222/16=0.0138,所以我可以显示级别1.4716,1.4854等等。但是我想将这个级别舍入到一些重要的数字,例如1.4600,1.4700,1.4800……或者1.4580,1.4590,1.4600..。或者1.4580,1.4585..。因此,我希望总是根据我有多少空间来显示尽可能多的显着性级别,但始终只显示一些有意义的值(我不是说舍入值,因为20.25也是重要的),它们是1、2、2.5、5和10或它们的乘数(10、20、25……或100、200、250...)或者他们的除法(0.1,0.2,0.25...或0.0001、0.0002、0.00025...)
我让它工作了,但我一点也不喜欢我的算法,它太长了,也不优雅。我希望有人能提出一些更优雅和通用的方式。我正在寻找算法,我可以实现不必要的代码。下面是我目前在objective-c中的算法。谢谢。
-(float) getPriceLineDenominator
{
NSArray *possVal = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:1.0],
[NSNumber numberWithFloat:2.0],
[NSNumber numberWithFloat:2.5],
[NSNumber numberWithFloat:5.0],
[NSNumber numberWithFloat:10.0],
[NSNumber numberWithFloat:20.0],
[NSNumber numberWithFloat:25.0],
[NSNumber numberWithFloat:50.0],
[NSNumber numberWithFloat:100.0],
nil];
float diff = highestPrice-lowestPrice;//range of shown values
double multiplier = 1;
if(diff<10)
{
while (diff<10)
{
multiplier/=10;
diff = diff*10;
}
}
else
{
while (diff>100)
{
multiplier*=10;
diff = diff/10;
}
}
float result = 0;
for(NSNumber *n in possVal)
{
float f = [n floatValue]*multiplier;
float x = [self priceY:highestPrice];
float y = [self priceY:highestPrice-f];
if((y-x)>=30)//30 is minimum distance between price levels shown
{
result = f;
break;
}
}
return result;
}发布于 2011-07-05 06:18:19
您可以使用对数来标识每个子范围的大小。
假设您知道数据中的最小值和最大值。你也知道你想要多少个级别。
最大值和最小值之间的差除以层数(略)小于每个子范围的大小
double diff = highestPrice - lowestPrice; // range of shown values
double range = diff / levels; // size of range
double logrange = log10(range); // log10
int lograngeint = (int)logrange; // integer part
double lograngerest = logrange - lograngeint; // fractional part
if (lograngerest < 0) { // adjust if negative
lograngerest += 1;
lograngeint -= 1;
}
/* now you can increase lograngerest to the boundaries you like */
if (lograngerest < log10(2)) lograngerest = log10(2);
else if (lograngerest < log10(2.5)) lograngerest = log10(2.5);
else if (lograngerest < log10(5)) lograngerest = log10(5);
else lograngerest = /* log10(10) */ 1;
/* and the size of each range is therefore */
return pow(10, lograngeint + lograngerest);第一个范围在数据中的最小值之前一点开始。使用fmod查找之前的确切数量。
发布于 2011-07-05 06:34:10
正如您所说,可用高度决定了最大分段数。为了便于讨论,让我们避免使用幻数,假设您有可用的height像素和最小间距closest
int maxDivs = height / closest;将范围划分为以下多个部分。你很可能会得到一些丑陋的值,但它提供了一个起点:
double minTickSpacing = diff/maxDivs;你需要从这个间隔开始,直到你以适当的数量级达到你的一个“有意义的”值。您可以使用一些数学函数来查找顺序,而不是循环和除/乘:
double multiplier = pow(10, -floor(log10(minTickSpacing)));从您的{2, 2.5, 5, 10}范围中选取下一个间距--为简单起见,我将使用常量和if-else:
double scaledSpacing = multiplier * minTickSpacing;
if ( scaledSpacing < 2 ) result = 2;
else if ( scaledSpacing < 2.5 ) result = 2.5;
else if ( scaledSpacing < 5 ) result = 5;
else result = 10;
return result/multiplier;或者类似的东西。完全未经测试,所以你需要检查标志和范围等等。而且肯定会有一些有趣的边缘情况。但我认为它应该在正确的范围内...
https://stackoverflow.com/questions/6575708
复制相似问题