I have a Bokeh App that contains a Line Plot. This plot has 10 lines in it. How do I color the lines with different shades of the same color?
Is there any way I can generate a list of different shades of the same color without hardcoding it?
You could use a palette from bokeh.palettes:
from bokeh.plotting import figure, output_file, show
from bokeh.palettes import Blues9
output_file('palette.html')
p = figure()
p.scatter(x=[0,1,2,3,4,5,6,7,8], y=[0,1,2,3,4,5,6,7,8], radius=1, fill_color=Blues9)
show(p)
https://docs.bokeh.org/en/latest/docs/reference/palettes.html
Related
I would like to add multiple y axes to a bokeh plot (similar to the one achieved using matplotlib in the attached image).
Would this also be possible using bokeh? The resources I found demonstrate a second y axis.
Thanks in advance!
Best Regards,
Pranit Iyengar
Yes, this is possible. To add a new axis to the figure p use p.extra_y_ranges["my_new_axis_name"] = Range1d(...). Do not write p.extra_y_ranges = {"my_new_axis_name": Range1d(...)} if you want to add multiple axis, because this will overwrite and not extend the dictionary. Other range objects are also valid, too.
Minimal example
from bokeh.plotting import figure, show, output_notebook
from bokeh.models import LinearAxis, Range1d
output_notebook()
data_x = [1,2,3,4,5]
data_y = [1,2,3,4,5]
color = ['red', 'green', 'magenta', 'black']
p = figure(plot_width=500, plot_height=300)
p.line(data_x, data_y, color='blue')
for i, c in enumerate(color, start=1):
name = f'extra_range_{i}'
lable = f'extra range {i}'
p.extra_y_ranges[name] = Range1d(start=0, end=10*i)
p.add_layout(LinearAxis(axis_label=lable, y_range_name=name), 'left')
p.line(data_x, data_y, color=c, y_range_name=name)
show(p)
Output
Official example
See also the twin axis example (axis) on the official webpage. This example uses the same syntax with only two axis. Another example is the twin axis example for models.
The problem which traps me is that I want to enlarge the font size in the ticker on both x and y-axis.
I am using the Bokeh as the tool for plotting. I can generate a neat plot now. But the ticker is way too small. As I went through google, I hardly find the solution. Huge thank. (Enlarge the font size within the red box)
You need the major_label_text_font_size attribute:
from bokeh.io import show
from bokeh.plotting import figure
p = figure()
p.circle(0, 0)
p.xaxis.major_label_text_font_size = "20px"
show(p)
Is it possible to have something similar to edgecolor and facecolor for plt.plot()? I need to plot a curve with a different colored boundary around it for e.g. a segment of the line would look like ||| with the outer lines in a different color and the inner line in a different color. Can this be done using a single plot command rather than plotting three plots?
You can do this using the matplotlib.patheffects module. You can set the path_effect of a line by using the path_effects kwarg.
Specifically in this case, we can use the Stroke class for the outline, and the Normal class for the inner part of the line (this just uses the linewidth and color specified by plt.plot). See the example below.
import matplotlib.pyplot as plt
import matplotlib.patheffects as path_effects
import numpy as np
fig, ax = plt.subplots(1)
ax.plot(np.random.rand(5), linewidth=4, color='r', path_effects=[
path_effects.Stroke(linewidth=8, foreground='black'),
path_effects.Normal()
])
plt.show()
I am making a plot following the example found here
Unfortunately, I have 17 curves I need to display, and the legend overlaps them. I know I can create a legend object that can be displayed outside the plot area like here, but I have 17 curves so using a loop is much more convenient.
Do you know how to combine both methods?
Ok, I found the solution. See the code below where I have just modified the interactive legend example:
import pandas as pd
from bokeh.palettes import Spectral4
from bokeh.plotting import figure, output_file, show
from bokeh.sampledata.stocks import AAPL, IBM, MSFT, GOOG
from bokeh.models import Legend
from bokeh.io import output_notebook
output_notebook()
p = figure(plot_width=800, plot_height=250, x_axis_type="datetime", toolbar_location='above')
p.title.text = 'Click on legend entries to mute the corresponding lines'
legend_it = []
for data, name, color in zip([AAPL, IBM, MSFT, GOOG], ["AAPL", "IBM", "MSFT", "GOOG"], Spectral4):
df = pd.DataFrame(data)
df['date'] = pd.to_datetime(df['date'])
c = p.line(df['date'], df['close'], line_width=2, color=color, alpha=0.8,
muted_color=color, muted_alpha=0.2)
legend_it.append((name, [c]))
legend = Legend(items=legend_it)
legend.click_policy="mute"
p.add_layout(legend, 'right')
show(p)
I'd like to expand on joelostbloms answer.
It is also possible to pull out the legend from an existing plot and add it
somewhere else after the plot has been created.
from bokeh.palettes import Category10
from bokeh.plotting import figure, show
from bokeh.sampledata.iris import flowers
# add a column with colors to the data
colors = dict(zip(flowers['species'].unique(), Category10[10]))
flowers["color"] = [colors[species] for species in flowers["species"]]
# make plot
p = figure(height=350, width=500)
p.circle("petal_length", "petal_width", source=flowers, legend_group='species',
color="color")
p.add_layout(p.legend[0], 'right')
show(p)
It is also possible to place legends outside the plot areas for auto-grouped, indirectly created legends. The trick is to create an empty legend and use add_layout to place it outside the plot area before using the glyph legend_group parameter:
from bokeh.models import CategoricalColorMapper, Legend
from bokeh.palettes import Category10
from bokeh.plotting import figure, show
from bokeh.sampledata.iris import flowers
color_mapper = CategoricalColorMapper(
factors=[x for x in flowers['species'].unique()], palette=Category10[10])
p = figure(height=350, width=500)
p.add_layout(Legend(), 'right')
p.circle("petal_length", "petal_width", source=flowers, legend_group='species',
color=dict(field='species', transform=color_mapper))
show(p)
A note on visibility as the above answers, while useful, didn't see me successfully place the legend below the plot and others may come across this too.
Where the plot_height or height are set for the figure as so:
p = figure(height=400)
But the legend is created as in Despee1990's answer and then placed below the plot as so:
legend = Legend(items=legend_it)
p.add_layout(legend, 'below')
Then the legend is not displayed, nor the plot.
If the location is changed to the right:
p.add_layout(legend, 'right')
...then the legend is only displayed where the items fit within the figure plot height. I.e. if you have a plot height of 400 but the legend needs a height of 800 then you won't see the items that don't fit within the plot area.
To resolve this either remove the plot height from the figure entirely or specify a height sufficient to include the height of the legend items box.
i.e. either:
p = figure()
or if Legend required height = 800 and glyph required height is 400:
p = figure(plot_height=800)
p.add_layout(legend, 'below')
I am extending this question to figure out how to make each of the lines a different shade of red or black. This might require making a custom color map or somehow tweaking the colormap "RdGy".
Using the data and packages from the last question, this is what I have so far:
df0.groupby([df0.ROI.str.split('_').str[0],'Band']).Mean.plot.kde(colormap='RdGy')
plt.legend()
plt.show()
And the figure looks like this:
But I want the 'bcs' lines to be shades of black and the 'red' lines to be shades of red. How is this possible?
It would also be great to customize the names of the lines in the legend, such as "BCS Band 1", etc.. but not sure how to do this either.
In principle #Robbies answer to the linked question gives you all the tools needed to create lines of any color and label you want.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
df = pd.DataFrame()
nam = ["red", "bcs"]
for i in range(8):
df['{}_{}'.format(nam[i//4],i%4)] = np.random.normal(i, i%4+1, 100)
nam = {"red":plt.cm.Reds, "bcs": plt.cm.gray_r}
fig, ax = plt.subplots()
for i, s in enumerate(df.columns):
df[s].plot(kind='density', color=nam[s.split("_")[0]]((i%4+1)/4.),
label=" Band ".join(s.split("_")))
plt.legend()
plt.show()
Of course you can also just use a list of strings as legend entries. Either by supplying them to the label argument of the plotting function,
labels = "This is a legend with some custom entries".split()
for i, s in enumerate(df.columns):
df[s].plot(kind='density', color=nam[s.split("_")[0]]((i%4+1)/4.),
label=labels[i])
or
by using the labels argument of the legend,
labels = "This is a legend with some custom entries".split()
for i, s in enumerate(df.columns):
df[s].plot(kind='density', color=nam[s.split("_")[0]]((i%4+1)/4.) )
plt.legend(labels=labels)