I am trying to make a dashboard with holoviews/bokeh and some numbers are rather large. This triggers the scientific notation ticks (ex.1.00e+4 ... 2.00e+5). I don't like the looks of that so I would like to implement one of 2 things and can't seem to get either to work.
Option 1: Display ticks in the form of 2.0x105, or even 2.0E5.
Option 2: I can divide my data by 104 and change my axis labels to say "X x104.
For option 1, I can't figure out how to do this with a Bokeh tickformatter to give to holoviews. It looks like I need to use the CustomJSTickFormatter, but I do not know javascript so this doesn't help me.
For option 2: Every time I try to use LaTeX or MathML as a label string, it truncates the label and fails to render it.
Here's my minimal reproducible code:
bokeh.__version__ = 3.6.1
holoviews.__version__ = 1.20.0
x = np.linspace(start=0, stop=100000, num=25)
y = np.linspace(start=0, stop=35, num=25)
df_test = pd.DataFrame({'x':x, 'y': y, 'x2':x/10000})
hv.Points(df_test, ['x2', 'y']).opts(xlabel=r'$$\text{ This is messed up } \times 10^6$$')
Option 2 is probably my preferred solution if I can get it to render correctly.
Well, given James's answer, I came up with a workaround for my needs...Unicode!
import holoviews as hv
# create a dictionary to convert numbers to superscripts
super_dict = {1:'\u00b1',
2:'\u00b2',
3:'\u00b2',
4:'\u2074',
5:'\u2075',
6:'\u2076',
7:'\u2077',
8:'\u2078',
9:'\u2079',
0:'\u2070'}
# use some math to find the closest power of 10 that is below your
# desired max. In my case, I wanted 10 rather than 1. One could also
# probably modify this use %3 to cut off at some reasonable thousand
def simplify_data(data, col):
import math
import numpy as np
logscale = math.floor(np.log10(data[col].max()))
new_data = data[col]/(10**(logscale-1))
new_label = f'{col}/10{super_dict[logscale-1]}'
data.loc[:, [new_label]] = new_data
return data, new_label
# create synthetic data
x = np.linspace(start=0, stop=200000, num=25)
y = np.linspace(start=0, stop=35, num=25)
df_test = pd.DataFrame({'x':x, 'y': y})
# run my new function to generate my scaled variable
df_test, new_col = simplify_data(df_test, 'x')
hv.Points(df_test, [new_col, 'y'])
# the .opts(xlabel=...) is now not needed either