Commented here a bit:
def scale(n):
"""
returns the value truncated to one digit
23452345 -> 200000000
"""
d = 0
if n == 0 : return 0
while n < 1:
n *= 10.0
d -= 1
while n > 10:
n /= 10.0
d += 1
return int(n) * (10 ** d)
def scale_with(number,scale_range):
"""
returns a number scaled as much as it
has to with the given range
"""
exponent = 0
if number == 0 : return 0
# find the exponent of number
while scale_range < 1:
scale_range *= 10.0
number *= 10.0
exponent -= 1
while scale_range > 10:
scale_range /= 10.0
number /= 10.0
exponent += 1
# put all the zero's back
return int(number) * (10 ** exponent)
def stackable(number):
"""
finds closes nice number to given number
that can be stacked nicely as scale.
One of 1 2 2.5 5 + enough 0's to make it
match number's size.
"""
number *= 2
exponent = 0
if number == 0 :
# scale of zero? return 1 as best guess
return 1
# find the exponent of number
while number < 1:
number *= 10.0
exponent -= 1
while number > 10:
number /= 10.0
exponent += 1
# find best fitting digit
final_digit = 1
for good_digit in 1,2,2.5,5:
if number > good_digit : final_digit = good_digit
# put same number of 0's the same as the original number
return final_digit * (10 ** exponent)
def scale_range(a,b,ticks=11):
"""
given abounds a and b and number of ticks
returns new nicer range and tick size
nicer range should be bigger then a and b
if the range contains 0 it should fall on one of the ticks
tick size would stackable() number
"""
# find nice tick scale
tick_scale = stackable(abs(a-b)/ticks)
# find 2 scaled with ranges around tick_size
a1,b1 = scale_with(a,tick_scale),scale_with(b,tick_scale)
# find the zero_offset point
zero_offset = tick_scale*int((a+b)/2./tick_scale)
# use zero_offset as starting point and sub tract from there
a2 = zero_offset-tick_scale*int(ticks/2)
b2 = zero_offset+tick_scale*int(ticks/2)
while a2 > a or b2 < b:
# oops with all the computations we shrunk too much
# lets just expand and try again
a2,b2,tick_scale = scale_range(a-tick_scale,b+tick_scale)
return (a2,b2,tick_scale)
def nice_range(a,b,ticks=11):
"""
same as scale_range but only returns range not the
tick_scale
"""
return scale_range(a,b,ticks)[0:2]
def test():
assert scale(0.00734) == .007
assert scale(0.134) == .1
assert scale(2.123) == 2
assert scale(323) == 300
assert scale(1566) == 1000
assert scale(52345)== 50000
assert scale_with(0.00734,2) == 0
assert scale_with(0.134,500) == 0
assert scale_with(2.123,.01) == 2.12
assert scale_with(323,20) == 320
assert scale_with(1566,10) == 1566
assert scale_with(52345,1000000) == 0
assert stackable(0.00734) == 0.01
assert stackable(0.134) == 0.25
assert stackable(2.123) == 2.5
assert stackable(323) == 500
assert stackable(1566) == 2500
assert stackable(52345) == 100000
assert scale_range(0.00734,100) == (0, 100, 10)
assert scale_range(0.134,.4) == (0.0, 0.5, 0.05)
assert scale_range(2.123,400) == (-50, 450, 50)
assert scale_range(323,1234) == (-400, 1600, 200)
assert scale_range(1566,50034) == (-30000, 70000, 10000)
assert scale_range(52345,52348) == (52341, 52351, 1)
assert scale_range(59995,59998) == (59991, 60001, 1)
assert scale_range(0.151829983285, 10.1368263183) == (-6, 14, 2)
# lets test random scale ranges
from random import random,seed
seed(9)
for a in map(lambda x: (random()-.5) * 10 ** x ,xrange(10),):
for b in map(lambda x: (random()-.5) * 10 ** x ,xrange(10),):
a, b = min(a,b),max(a,b)
a1,b1,s = scale_range(a,b)
assert a1 < a
assert b1 > b
assert b1 - a1+s*10 > .000001
if a1 < 0 and b1 > 0 :
# if there is a zero in the range make sure we
# have a mark at zero too
r = [ int((a1+s*i)*1000) for i in range(11)]
assert 0 in r
test()
Some improvements to the algorithm might be having it start at 0 in
proper conditions. ( right now it only requires to have one of the
ticks at 0 ) because it looks kind of odd when you are counting kb of
memory used and you get negative memory and negative time because the
algo rescaled it that way.