What i basically want is instead of plotting and having offsets 10^8 10^5 or 1e8 1e5 etc to have them in stadard units such as 10^3 10^6 1e3 1e6 etc.
Is this possible? and if so how?
Thanks in advance
What i basically want is instead of plotting and having offsets 10^8 10^5 or 1e8 1e5 etc to have them in stadard units such as 10^3 10^6 1e3 1e6 etc.
Is this possible? and if so how?
Thanks in advance
plt.gca().get_xaxis().get_major_formatter().set_useOffset(False)
Do you mean this? (same goes for y axis). This is a possible duplicate of this question in which the second answer gives a more canonical description than me. Upvote him if that's what you're looking for.
EDIT
I've played around with this on and off for the whole afternoon and it seems that setting ax.yaxis.set_scientific(True)
automatically trumps any ax.yaxis.setOffset = False
. No matter in which way you do it. I couldn't work around that and then I got aggravated.
Here's a workaround though, Py3.4, mpl 1-3-1 win7 tested.
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
from decimal import Decimal
x_large = np.arange(10, 20000, 100)
y_large = np.arange(10, 20000, 100)
x_small = np.arange(-1, 1, 0.000001)
y_small = np.arange(-1, 1, 0.000001)
metric = [0,1,2,3,6,9, 12, 16]
def format_large(x):
x = float(x)
sx = str(x)
sx = sx.split(".")[0]
if len(sx)-1 in metric:
return "%se%s" % (sx[0], len(sx)-1)
add = - max(exp-len(sx)-1 for exp in metric if exp<len(sx)-1)
return "%se%s" % (sx[0:add], len(sx)-add)
#love thy DRY principle
def check_metric_small(num, exp):
if exp in metric:
return "%se-%s" % (num[0], exp)
add = min(mexp-exp for mexp in metric if mexp>exp)
num = num.ljust(add+1, "0")
return "%se-%s" % (num[0:add+1], exp+add)
def format_small(x):
sx = str(x)
#string representation of numbers after e-4 is
#done in scientific manner 1e-5, 1e-6 ...
if sx.find("e") != -1:
num, exp = sx.split("e")
exp = -int(exp)
return check_metric_small(num, exp)
#numbers up to e-4 are represented in string form
#as is, therefore 0.1, 0.01
s = sx.split(".")[-1]
num = s.strip("0")
exp = len(s)-len(num)
return check_metric_small(num, exp+1)
def formatter(x, p):
if x<0: #make sure we don't send a negative number
res = "-" #rather just tack on the minus sign
x = -x
else: res = ""
if abs(x)<1 and x!=0.0:
return res+format_small(x)
return res+format_large(x)
fig, ax = plt.subplots()
y_formatter = mpl.ticker.FuncFormatter(formatter)
ax.yaxis.set_major_formatter(y_formatter)
ax.plot(x_large,y_large)
plt.show()
Where the important functions are contained within the format_large
and format_small
. They will round the numbers to the next closest smaller exponent defined by the metric
list. By themselves, they aren't able to handle negative numbers so use the formatter
function.
Change between a large zoom and un-zoom doesn't seem to leave behind any weird effects and neither does passing over zero. Bellow is the gist of those functions:
for i in [0, 10, 50, ......]:
print(format_large(i), float(format_large(i)) == i)
0e0 True 5e6 True
1e1 True 5e6 True
5e1 True 50e6 True
5e2 True 500e6 True
5e3 True 50e9 True
50e3 True 500e9 True
500e3 True 5e12 True
for i in [0.05, 0.005, ......]:
print(format_small(i), float(format_small(i)) == i)
5e-2 True 50e-9 True
5e-3 True 5e-9 True
500e-6 True 500e-12 True
50e-6 True 50e-12 True
5e-6 True 50e-12 True
500e-9 True 5000e-16 True
Be careful though, because I round the numbers, you can only test with rounded numbers.
What happens for large numbers, is that I take their string representation i.e. "500.0"
. Lenght of the number subtracted by one gives me the number of zeroes\order of the number. If it's a metric order I return the simplest string containing only the leading digit of the number and its exponent.
If the order of the number isn't metric, I find the first lower metric exponent: max(exp-len(sx) for exp in metric if exp<len(sx))
(this means, find me the largest difference between an exponent of metric system and my current number order for all metric exponents that are lower than the current number order). This guarantees that all the differences will be negative, and the largest number will always point to the closest metric exponent. Return string where "main" number has more than 1 digit, how much more is determined by the difference between the closest metric exponent and current number order.
Small numbers get really complex because there are 2 cases to consider. When a number is larger than 0.0004
its representation will not be changed. However for numbers smaller than that, their representation will change to scientific.
I split the scientific representation on the letter "e" to get the actual number i.e. 1234
and the exponent. If the exponent belongs to the metric system, return the string. Otherwise get me the next smaller metric index and pad the number until it fits: mexp-exp for mexp in metric if mexp>exp
(this means: for all exponents that are larger than current number order, and remember I changed current order to a positive number therefore larger metric exponent correlates to a smaller actual number, find me the minimal difference of current and metric exponent). Before I return the string I pad the right hand side of my number with enough zeroes with ljust
so I don't risk raising a index out of bounds error.
For non scientifically represented small numbers, i.e. 0.000432
, I split them at the .
, strip the leading zeroes to get just the number 432
. Length of that subtracted from length of the whole split number 000432
determines how many zeroes I have. From there I repeat the procedure for finding the next closest metric exponent, or return a string immediately.
Functions don't have a number of decimals option, but it should be trivial to add that. An if
statement before returning the result and amending the return statements to something like i.e. "%s%se%s" % (sx[0:add], sx[add:ndecimals], len(sx)-add)
Should take care of that problem.
I'd like to say this was fun. But it wasn't :D it was msotly clobbering through the manual until I gave up. But I said I'd get back to you so there you go.