Bokeh NumerFormatter Swiss Francs - python

I am currently working on a Webapp with Bokeh which contains a table of Swiss Franc values. I cannot figure out how to format the numbers in the following style
1'000'000
so with dahes instead of commas as the 1000 separator. Going down the documentation Bokeh seems to be limited to the options given here
https://docs.bokeh.org/en/latest/docs/reference/models/widgets.tables.html?highlight=numberfor#bokeh.models.widgets.tables.NumberFormatter
but the numbro documentation seems to hint that there is more to it.
http://numbrojs.com/format.html
I still cannot find a "de-CH" option or something like this, and even if I did, I do not see how to hand it over in bokeh. Does anyone have any experience?
Best regards.

Bokeh gives you the option to define your own tick-formatter using FuncTickFormatter. With this you can define you own rule in JavaScript.
Below you can see on example adapting your rule.
from bokeh.plotting import figure, output_notebook, show
from bokeh.models import FuncTickFormatter
output_notebook()
# create a new plot with the toolbar below
p = figure(plot_width=400, plot_height=400,
title=None, toolbar_location="below", )
x = [1e3, 3e3, 5e3, 1e4, 3e4]
y = [2, 5, 8, 2, 7]
p.circle(x, y, size=10)
p.xaxis.formatter = FuncTickFormatter(code=
'''
var num = tick.toFixed(2).toString()
var splitted = num.split(".")
for (var i=splitted[0].length-3; 0<i; i+=-3){
num = num.slice(0,i)+"'"+num.slice(i)
}
return num
'''
)
The Output looks like this for a different set of x and y
Ticks < 30k
Ticks 2e6<1e7
x = [1e3, 3e3, 5e3, 1e4, 3e4]
x = [1e6, 5e6, 9e6]
y = [2, 5, 8, 2, 7]
y = [2, 8, 2]

Related

Change x- and y-numbering in imshow

I would like to plot a function of two variables in python. Similar to this article, we can obtain an output like
using this code:
from numpy import exp,arange
from pylab import meshgrid,cm,imshow,contour,clabel,colorbar,axis,title,show
from matplotlib import pyplot
# the function that I'm going to plot
def z_func(x,y):
return (1-(x**2+y**3))*exp(-(x**2+y**2)/2)
x = arange(-3.0,3.0,0.1)
y = arange(-3.0,3.0,0.1)
z = [[0] * y.__len__() for i in range(x.__len__())]
for i in range(0, x.__len__()):
for j in range(0, y.__len__()):
z[j][i] = z_func(x[i], y[j])
im = imshow(z,cmap=cm.RdBu, extent = [-3, 3, -3, 3], interpolation = "none", origin='lower') # drawing the function
# adding the Contour lines with labels
cset = contour(z,arange(-1,1.5,0.2),linewidths=2,cmap=cm.Set2)
clabel(cset,inline=True,fmt='%1.1f',fontsize=10)
colorbar(im) # adding the colobar on the right
# latex fashion title
title('$z=(1-x^2+y^3) e^{-(x^2+y^2)/2}$')
show()
As you can see, the x- and y-labels go from 0 to 59 (which is the count of elements in x and y). How can I correct these values such that they range from -3 to 3?
A minor sub-question: Why do I need to "transpose" in z[j][i] = z_func(x[i], y[j])? Does Python treat the first dimension as "column" and the second as "row"?
You're trying to plot both the z-function and the countour plots. You need to add the "extent" parameter to matplotlib.pyplot.countour plot too.
cset = contour(z, arange(-1,1.5,0.2),
extent = [-3, 3, -3, 3],
linewidths = 2,
cmap = cm.Set2)

How to plot recs and circles with two legends with Altair?

I would like to create two charts that are superimposed, but with two legends. One chart uses rects with one color palette, and the second chart displays circles with a second color palette. This should be very straightforward, but something is wrong. I only get a single legend. I also want the legends to be selectable. Here is a self-contained MWE, representative of a more complex use case. Below the code, I show an image of what the code produces: single legend, single color palette. Is this expected behavior or some kind of bug? Any insight is appreciated. Thanks!
streamimport pandas as pd
import altair as alt
import streamlit as st
# Demonstrate two categorical legends with selection_multi.
# There appears to be a bug when using shift-click on one menu, then the other.
def drawPlot():
x1 = [1, 2, 3]
y1 = [1, 2, 3]
x2 = [4, 5, 6]
y2 = [4, 5, 6]
df = pd.DataFrame({'x1':x1, 'y1':y1, 'x2':x2, 'y2':y2})
palette1 = alt.Color('x1:N',
scale=alt.Scale(
domain=[1, 2, 3],
range=['lightgreen', 'darkgreen', 'yellow'],
)
)
palette2 = alt.Color('x2:N',
scale=alt.Scale(
domain=[4, 5, 6],
range=['lightblue', 'darkblue', 'purple'],
)
)
select1 = alt.selection_multi(fields=['x1'], bind='legend')
select2 = alt.selection_multi(fields=['x2'], bind='legend')
nodes1 = alt.Chart(df).mark_rect(
width=20, height=20,
).encode(
x = 'x1:N',
y = 'y1:N',
color = palette1,
).add_selection(
select1
)
nodes2 = alt.Chart(df).mark_circle(
width=20, height=20, size=1200,
).encode(
x = 'x2:N',
y = 'y2:N',
color = palette2,
).add_selection(
select2
)
full_chart = (nodes1 + nodes2).properties(
height=500,
width=1000,
)
return full_chart
#----------------------------------------------------------------
if __name__ == "__main__":
chart = drawPlot()
st.altair_chart(chart, use_container_width=True)
Altair/Vega-Lite combine existing scales among charts into a single legend by default when possible for a more compact layout. When scales are independent of each other and should be represented in separate legends, you would need to resolve them manually, in your case it would look like this
chart.resolve_scale(color='independent')
You can read more on this page in the docs.

Bokeh: How can I directly access the colors in a ColorMapper?

I use a LinearColorMapper in Bokeh 2.3.0 to map values to certain colors in my plot. Nothing fancy, as shown in the minimal working example:
import pandas as pd
from bokeh.plotting import figure, curdoc
from bokeh.models import ColumnDataSource, LinearColorMapper
from bokeh.layouts import layout
plot1 = figure(plot_width=1000, plot_height=250)
df = pd.DataFrame({"ID":[0, 1, 2, 3, 4, 5, 6, 7],
"Value1":[0, 100, 200, 300, 400, 500, 600, 700],
"Color": [0, 1, 0, 1, 2, 2, 3, 3] # Those somehow correspond to the categories, but will change during runtime
})
source = ColumnDataSource(df)
possible_categories = ["A", "B", "C", "D"]
cmap = LinearColorMapper(palette="Turbo256", low = 0, high = len(possible_categories))
circle = plot1.circle(x='ID', y='Value1', source=source,
fill_color={"field":'Color', "transform":cmap},
line_color={"field":'Color', "transform":cmap})
layout_ = layout([[plot1]])
curdoc().add_root(layout_)
But I wonder if there is a possibility to 'manually' access the colors directly from the cmap that I created? I would like to pass a value to the cmap and get the color or Hex-Color-Code or something similar back. Somehow like this (which of course does not work, but illustrates the idea):
for categ, num in enumerate(possible_categories):
color = cmap.get_colorcode_to_the_following_value(num)
print(f"Color of Category {possible_categories[categ]}: {color}")
with the output:
Color of Category A: ColorCodeXXX
Color of Category B: ColorCodeXXX
Color of Category C: ColorCodeXXX
Color of Category D: ColorCodeXXX
I´m sure this somehow works, but I extensively looked up the reference of Bokeh and did not find anything like this.
The color mapping done by LinearColorMapper is actually performed on the JavaScript side, in the browser. The information is never computed or available in Python. If you require this information, you would need to map and set the colors on the glyph manually, without using Bokeh's LinearColorMapper.

Python Bokeh: Restart X axis to 0 on Zoom

I have code below that creates a simple line x-y plot.
When I zoom in, I want the x-axis ticker to start at 0 again instead of 3.9/whatever the x point of the zoom was as in the image.
No Zoom:
After Zooming:
How do I do that?
Code:
from bokeh.io import output_file, show, save
from bokeh.layouts import column
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource
data = []
x = list(range(11))
y0 = x
y1 = [10 - xx for xx in x]
y2 = [abs(xx - 5) for xx in x]
source = ColumnDataSource(data=dict(x=x, y0=y0, y1=y1, y2=y2))
for i in range(3):
p = figure(title="Title " + str(i), plot_width=300, plot_height=300)
if len(data):
p.x_range = data[0].x_range
p.y_range = data[0].y_range
p.circle('x', 'y0', size=10, color="navy", alpha=0.5, legend_label='line1', source=source)
p.legend.location = 'top_right'
p.legend.click_policy = "hide"
data.append(p)
plot_col = column(data)
# show the results
show(plot_col)
This is an unusual requirement, and none of the built-in things behave this way. If you zoom in to the interval [4,7], the the range will be updated [4, 7], and so then the axis will display labels for [4, 7]. If it will suffice to simply display different tick labels, even while the underlying range start/end remain their usual values, then you could use a Custom Extension to generate whatever customized labels you want. There is an example in the User's Guide that already does almost exactly what you want already:
https://docs.bokeh.org/en/latest/docs/user_guide/extensions_gallery/ticking.html#userguide-extensions-examples-ticking
You might also be able to do something even more simply with a FuncTickFormatter, e.g. (untested)
p.xaxis.formatter = FuncTickFormatter(code="""
return tick - ticks[0]
""")

For loop for bokeh in databricks

Hi i would like to loop through for loop eg. few times code below to get few times the same graph in one databricks cell:
I imported library:
from bokeh.plotting import figure
from bokeh.embed import components, file_html
from bokeh.resources import CDN
x = [1, 2, 3, 4, 5]
y = [6, 7, 2, 4, 5]
for i in range(5):
p = figure(title='test', x_axis_label = 'x values', y_axis_label='y values')
p.line(x,y, line_width =2)
html = file_html(p,CDN,'plot')
displayHTML(html)
I was trying to use for loop but still i am obtaining only one single graph in a cell.
Also tried with different modules, like here:
from bokeh.io import output_file, show
from bokeh.plotting import figure
x = [1, 2, 3, 4, 5]
y = [6, 7, 2, 4, 5]
for i in range(5):
p = figure(title='test', x_axis_label = 'x values')
p.line(x,y, line_width =2)
output_file("line"+str(i)+".html")
show(p)
But here i am not getting any result, nothing is plotted. Can you tell me why?
Tried this as well:
d={}
for i in range(5):
p = figure(title='test', x_axis_label = 'x values')
p.line(x,y, line_width =2)
d["html{0}".format(i)]=file_html(p,CDN, 'plot' + str(i))
for j in d:
displayHTML(j)
It looks like with bokeh in databricks it is possible to display only one graph per cell. Anyone knows if it is true?
Could you help me with the syntax for for loop to get it multiple times?
It seems from other answers that there may be issues with Databricks using old notebook versions that Bokeh does not fully support. If the standard output_notebook is not working, but the displayHTML thing does, then I'd say your best bet is to collect the plots you want to show in a column layout, then show them all at once with a single call to show at the end:
from bokeh.layouts import column
plots = []
for i in range(5):
p = figure(...)
p.line(...)
plots.append(p)
layout = column(*plots)
# only use this way for databricks
html = file_html(layout, CDN, 'plot')
displayHTML(html)

Categories

Resources