Style part of glyph - python

In the minimal example below, how can I highlight, say, the highest point by changing its fill color to red? I know in this case it's easy enough just to draw over the old glyph: p.circle(3, 8, fill_color='red'), but my actual plot is more complicated. So I'm hoping to change something inside the variable r instead, if this is possible.
from bokeh.plotting import figure, output_file, show
output_file("dimensions.html")
p = figure(plot_width=700)
p.plot_height = 300
r = p.circle([1, 2, 3, 4, 5], [2, 5, 8, 2, 7], size=10)
show(p)

If you want just one circle to be a different color, then there are only two options:
send all the colors:
p.circle(x, y, color=["blue", "blue", "red", "blue", "blue"], size=10)
Have separate renderers:
p.circle([1, 2, 4, 5], [2, 5, 2, 7], color="blue", size=10)
p.circle(x=3, y=8, color="red", size=10)
There was some exciting new work recently added to GH master to make a start for adding "computed transforms" so in the near future it should be possible to define a custom color mapper for a single glyph, but that functionality does not exist yet as of version 0.11.1

Related

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.

Altair mark_line plots noisier than matplotlib?

I am learning altair to add interactivity to my plots. I am trying to recreate a plot I do in matplotlib, however altair is adding noise to my curves.
this is my dataset
df1
linked here from github: https://raw.githubusercontent.com/leoUninova/Transistor-altair-plots/master/df1.csv
This is the code:
fig, ax = plt.subplots(figsize=(8, 6))
for key, grp in df1.groupby(['Name']):
y=grp.logabsID
x=grp.VG
ax.plot(x, y, label=key)
plt.legend(loc='best')
plt.show()
#doing it directly from link
df1='https://raw.githubusercontent.com/leoUninova/Transistor-altair-plots/master/df1.csv'
import altair as alt
alt.Chart(df1).mark_line(size=1).encode(
x='VG:Q',
y='logabsID:Q',
color='Name:N'
)
Here is the image of the plots I am generating:
matplotlib vs altair plot
How do I remove the noise from altair?
Altair sorts the x axis before drawing lines, so if you have multiple lines in one group it will often lead to "noise", as you call it. This is not noise, but rather an accurate representation of all the points in your dataset shown in the default sort order. Here is a simple example:
import numpy as np
import pandas as pd
import altair as alt
df = pd.DataFrame({
'x': [1, 2, 3, 4, 5, 5, 4, 3, 2, 1],
'y': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
'group': [0, 0, 0, 0, 0, 1, 1, 1, 1, 1]
})
alt.Chart(df).mark_line().encode(
x='x:Q',
y='y:Q'
)
The best way to fix this is to set the detail encoding to a column that distinguishes between the different lines that you would like to be drawn individually:
alt.Chart(df).mark_line().encode(
x='x:Q',
y='y:Q',
detail='group:N'
)
If it is not the grouping that is important, but rather the order of the points, you can specify that by instead providing an order channel:
alt.Chart(df.reset_index()).mark_line().encode(
x='x:Q',
y='y:Q',
order='index:Q'
)
Notice that the two lines are connected on the right end. This is effectively what matplotlib does by default: it maintains the index order even if there is repeated data. Using the order channel for your data produces the result you're looking for:
df1 = pd.read_csv('https://raw.githubusercontent.com/leoUninova/Transistor-altair-plots/master/df1.csv')
alt.Chart(df1.reset_index()).mark_line(size=1).encode(
x='VG:Q',
y='logabsID:Q',
color='Name:N',
order='index:Q'
)
The multiple lines in each group are drawn in order connected at the ends, just as they are in matplotlib.

Show all lines in matplotlib line plot

How do I bring the other line to the front or show both the graphs together?
plot_yield_df.plot(figsize=(20,20))
If plot data overlaps, then one way to view both the data is increase the linewidth along with handling transparency, as shown:
plt.plot(np.arange(5), [5, 8, 6, 9, 4], label='Original', linewidth=5, alpha=0.5)
plt.plot(np.arange(5), [5, 8, 6, 9, 4], label='Predicted')
plt.legend()
Subplotting is other good way.
Problem
The lines are plotted in the order their columns appear in the dataframe. So for example
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
a = np.random.rand(400)*0.9
b = np.random.rand(400)+1
a = np.c_[a,-a].flatten()
b = np.c_[b,-b].flatten()
df = pd.DataFrame({"A" : a, "B" : b})
df.plot()
plt.show()
Here the values of "B" hide those from "A".
Solution 1: Reverse column order
A solution is to reverse their order
df[df.columns[::-1]].plot()
That has also changed the order in the legend and the color coding.
Solution 2: Reverse z-order
So if that is not desired, you can instead play with the zorder.
ax = df.plot()
lines = ax.get_lines()
for line, j in zip(lines, list(range(len(lines)))[::-1]):
line.set_zorder(j)

How to use a CDS column to set the "line_dash" of a Multiline glyph?

I am plotting Multilines with the method multi_line.
from bokeh.plotting import figure, show
from bokeh.models import ColumnDataSource
source = ColumnDataSource(data=dict(
x=[3, 3],
y=[4, 4],
xs1=[[1, 2, 3], [2, 3, 4]],
ys1=[[6, 7, 2], [4, 5, 7]],
xs2=[[8, 9], [10, 11, 12]],
ys2=[[6, 7], [7, 8, 9]],
color=['red', 'green'],
width=[5, 1],
dash=['solid', 'dashed']
)
)
p = figure(
plot_width=400,
plot_height=400,
tools='lasso_select,pan,wheel_zoom'
)
p.multi_line(
xs='xs1',
ys='ys1',
source=source,
color='color',
line_join='round',
line_width='width', # this is working with the column name, despite the documentatio say nothing
# line_dash='dash' # this is not working
)
show(p)
A column for color, alpha or line_width can be set on multilines source CDS in order to plot each line in a different way. But this cannot be applied to the line_dash attribute. I would like to make the main line solid and the rest dashed.
If I use the glyph line for this main lines then I will lose performance because I need to update more than one glyph at the same time on each plots.
On the other hand I think there are something missing in the documentation about line_width because a CDS column can be assigned to this method argument and it works:
alpha (float) – an alias to set all alpha keyword args at once
color (Color) – an alias to set all color keyword args at once
>> line_width (float) - should this be added because it works with CDS columns?
Is there a way to assign a column for the line_dash attribute?
I didn´t test the rest of attributes in depth.
Although you are asking in relation to MultiLine, this has the same answer as Can the line dash of a segment plot be defined by source data? which is that "vectorizing" the line_dash property is not currently supported (as of 0.12.16) If you want to have different line dashes for now you will have to make separate calls to line.

Python Bokeh: remove toolbar from chart

Note from maintainers: The specifics of this question concern the bokeh.charts API which is obsolete and was removed several years ago. In modern Bokeh, specify toolbar_location:
p = figure(toolbar_location=None)
OBSOLETE:
I don't seem to be able to remove the toolbar from a bokeh Bar chart. Despite setting the tools argument to None (or False or '') I always end up with the bokeh logo and a grey line, e.g. with this code:
from bokeh.charts import Bar, output_file, show
# prepare some data
data = {"y": [6, 7, 2, 4, 5], "z": [1, 5, 12, 4, 2]}
# output to static HTML file
output_file("bar.html")
# create a new line chat with a title and axis labels
p = Bar(data, cat=['C1', 'C2', 'C3', 'D1', 'D2'], title="Bar example",
xlabel='categories', ylabel='values', width=400, height=400,
tools=None)
# show the results
show(p)
However, when I try the same with a bokeh plot, it works perfectly fine and the toolbar is gone, e.g. with this code:
from bokeh.plotting import figure, output_file, show
output_file("line.html")
p = figure(plot_width=400, plot_height=400, toolbar_location=None)
# add a line renderer
p.line([1, 2, 3, 4, 5], [6, 7, 2, 4, 5], line_width=2)
show(p)
Does anyone know what I'm doing wrong?
If you want to remove the logo and the toolbar you can do:
p.toolbar.logo = None
p.toolbar_location = None
Hope this resolves your problem
On any Bokeh plot object you can set:
p.toolbar_location = None

Categories

Resources