Maintainers note: this question is obsolete. Calling multiple glyph methods on a figure automatically combines (and has for many years). For information on modern Bokeh, see:
https://docs.bokeh.org/en/latest/docs/user_guide/plotting.html
OBSOLETE:
I am running the Bokeh tutorial in the IPython notebook. It only displays the scatter plot and not the line plot. From the command-line it renders both plots separately.
How do I get both graphs in the same chart, on top of each other?
import numpy as np
import bokeh.plotting as bplt
bplt.output_file("bokehtest.html")
#bplt.output_notebook(url=None)
x = np.linspace(-2*np.pi, 2*np.pi, 100)
y = np.cos(x)
bplt.line(x, y, color="red")
bplt.scatter(x, y, marker="square", color="blue")
bplt.show()
OBSOLETE ANSWER: see https://docs.bokeh.org/en/latest/docs/user_guide/plotting.html* for modern Bokeh
You just need to call bplt.hold() before any of the plotting commands, to toggle the "hold state". The following code works for me:
import numpy as np
import bokeh.plotting as bplt
bplt.output_file("bokehtest.html")
#bplt.output_notebook(url=None)
x = np.linspace(-2*np.pi, 2*np.pi, 100)
y = np.cos(x)
bplt.hold() # <--- The important line!!
bplt.line(x, y, color="red")
bplt.scatter(x, y, marker="square", color="blue")
bplt.show()
OBSOLETE ANSWER: see https://docs.bokeh.org/en/latest/docs/user_guide/plotting.html for modern Bokeh
Try using the figure command like in this example:
import numpy as np
import bokeh.plotting as bplt
bplt.output_file("bokehtest.html")
x = np.linspace(-2*np.pi, 2*np.pi, 100)
y = np.cos(x)
bplt.figure()
bplt.line(x, y, color="red")
bplt.scatter(x, y, marker="square", color="blue")
bplt.show()
Related
I am trying to plot 3d-coordinates from an array as a surface with plot_surface. My array contain x,y,z-data (each the same size). I have successfully managed to plot the data via the plot_trisurf function. This gives me the following plot:
plot_trisurf function
To add a fourth dimension in color I would like to plot the same surface with plot_surface. I have managed to get nearly the result I want with the following code:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
points = np.loadtxt(...)
x,y,z = points[:,0],points[:,1],points[:,2]
X, Y = np.meshgrid(x,y)
Z = np.outer(z.T, np.ones(z.size))
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_surface(X,Y,Z,ls='None')
ax.set_xlabel('x - Axis in mm')
ax.set_ylabel('y - Axis in mm')
ax.set_zlabel('z - Axis in mm')
ax.set_xlim([-np.max((np.abs(points))), np.max((np.abs(points)))])
ax.set_ylim([-np.max((np.abs(points))), np.max((np.abs(points)))])
ax.set_zlim([-np.max((np.abs(points))), np.max((np.abs(points)))])
which gave me the following plot:
plot_surface-function, view 1
plot_surface-function, view 2
How do I manage to eliminate the connection between the base contact points on the ground?
I want to place a label at the top left corner of each streaming plot, be it one plot, or two plots, etc. The plots are stretched in both directions. For now, I have to manually specify a y postion depending on how many plots are shown. (y=200 for two plots, and y=440 for one plot) One may resolve it by recording the total range of y values shown in the plot, but it feels too hacky. I'm wondering if there is a simple way to do this. Thanks for any help.
from bokeh.server.server import Server
from bokeh.models import ColumnDataSource, Label
from bokeh.plotting import figure
from bokeh.layouts import column
import numpy as np
import datetime as dt
from functools import partial
import time
def f_random():
data = np.random.rand()
data = (dt.datetime.now(), data)
return data
def f_sinewave():
data = np.sin(time.time()/1.)
data = (dt.datetime.now(), data)
return data
def make_document(doc, functions, labels):
def update():
for index, func in enumerate(functions):
data = func()
sources[index].stream(new_data=dict(time=[data[0]], data=[data[1]]), rollover=1000)
annotations[index].text = f'{data[1]: .3f}'
sources = [ColumnDataSource(dict(time=[], data=[])) for _ in range(len(functions))]
figs = []
annotations = []
for i in range(len(functions)):
figs.append(figure(x_axis_type='datetime', plot_width=800, plot_height=400, y_axis_label=labels[i]))
figs[i].line(x='time', y='data', source=sources[i])
annotations.append(Label(x=10, y=200, text='', text_font_size='20px', text_color='black',
x_units='screen', y_units='screen', background_fill_color='white'))
figs[i].add_layout(annotations[i])
doc.add_root(column([fig for fig in figs], sizing_mode='stretch_both'))
doc.add_periodic_callback(callback=update, period_milliseconds=100)
if __name__ == '__main__':
# list of functions and labels to feed into the scope
functions = [f_random, f_sinewave]
labels = ['random', 'sinewave']
server = Server({'/': partial(make_document, functions=functions, labels=labels)})
server.start()
server.io_loop.add_callback(server.show, "/")
try:
server.io_loop.start()
except KeyboardInterrupt:
print('keyboard interruption')
For now you could do:
Label(x=10, y=figs[i].plot_height-30, ...)
It seems like allowing negative values to implicitly position against the "opposite" side would be a nice feature (and a good first task for new contributors), so I would encourage you to file a GitHub issue about it.
Surprisingly nobody took the pain to make an example in the bokeh gallery for 2D histogram plotting
histogram2d of numpy gives the raw material, but would be nice to have an example as it happens for matplotlib
Any idea for a short way to make one?
Following up a proposed answer let me attach a case in which hexbin does not the job because exagons are not a good fit for the job. Also check out matplotlib result.
Of course I am not saying bokeh cannot do this, but it seem not straightfoward. Would be enough to change the hexbin plot into a square bin plot, but quad(left, right, top, bottom, **kwargs) seems not to do this, nor hexbin to have an option to change "tile" shapes.
You can make something close with relatively few lines of code (comapring with this example from the matplotib gallery). Note bokeh has some examples for hex binning in the gallery here and here. Adapting those and the example provided in the numpy docs you can get the below:
import numpy as np
from bokeh.plotting import figure, show
from bokeh.layouts import row
# normal distribution center at x=0 and y=5
x = np.random.randn(100000)
y = np.random.randn(100000) + 5
H, xe, ye = np.histogram2d(x, y, bins=100)
# produce an image of the 2d histogram
p = figure(x_range=(min(xe), max(xe)), y_range=(min(ye), max(ye)), title='Image')
p.image(image=[H], x=xe[0], y=ye[0], dw=xe[-1] - xe[0], dh=ye[-1] - ye[0], palette="Spectral11")
# produce hexbin plot
p2 = figure(title="Hexbin", match_aspect=True)
p.grid.visible = False
r, bins = p2.hexbin(x, y, size=0.1, hover_color="pink", hover_alpha=0.8, palette='Spectral11')
show(row(p, p2))
I am trying to add a permanent label on nodes for a networkx graph using spring_layout and bokeh library. I would like for this labels to be re-positioned as the graph scales or refreshed like what string layout does, re-positioning the nodes as the graph scales or refreshed.
I tried to create the graph, and layout, then got pos from the string_layout. However, as I call pos=nx.spring_layout(G), it will generated a set of positions for the nodes in graph G, which I can get coordinates of to put into the LabelSet. However, I have to call graph = from_networkx(G, spring_layout, scale=2, center=(0,0)) to draw the network graph. This will create a new set of position for the node. Therefore, the positions of the nodes and the labels will not be the same.
How to fix this issues?
Thanks for asking this question. Working through it, I've realized that it is currently more work than it should be. I'd very strongly encourage you to open a GitHub issue so that we can discuss what improvements can best make this kind of thing easier for users.
Here is a complete example:
import networkx as nx
from bokeh.io import output_file, show
from bokeh.models import CustomJSTransform, LabelSet
from bokeh.models.graphs import from_networkx
from bokeh.plotting import figure
G=nx.karate_club_graph()
p = figure(x_range=(-3,3), y_range=(-3,3))
p.grid.grid_line_color = None
r = from_networkx(G, nx.spring_layout, scale=3, center=(0,0))
r.node_renderer.glyph.size=15
r.edge_renderer.glyph.line_alpha=0.2
p.renderers.append(r)
So far this is all fairly normal Bokeh graph layout code. Here is the additional part you need to add permanent labels for each node:
from bokeh.transform import transform
# add the labels to the node renderer data source
source = r.node_renderer.data_source
source.data['names'] = [str(x*10) for x in source.data['index']]
# create a transform that can extract the actual x,y positions
code = """
var result = new Float64Array(xs.length)
for (var i = 0; i < xs.length; i++) {
result[i] = provider.graph_layout[xs[i]][%s]
}
return result
"""
xcoord = CustomJSTransform(v_func=code % "0", args=dict(provider=r.layout_provider))
ycoord = CustomJSTransform(v_func=code % "1", args=dict(provider=r.layout_provider))
# Use the transforms to supply coords to a LabelSet
labels = LabelSet(x=transform('index', xcoord),
y=transform('index', ycoord),
text='names', text_font_size="12px",
x_offset=5, y_offset=5,
source=source, render_mode='canvas')
p.add_layout(labels)
show(p)
Basically, since Bokeh (potentially) computes layouts in the browser, the actual node locations are only available via the "layout provider" which is currently a bit tedious to access. As I said, please open a GitHub issue to suggest making this better for users. There are probably some very quick and easy things we can do to make this much simpler for users.
The code above results in:
similar solution as #bigreddot.
#Libraries for this solution
from bokeh.plotting import figure ColumnDataSource
from bokeh.models import LabelSet
#Remove randomness
import numpy as np
np.random.seed(1337)
#Load positions
pos = nx.spring_layout(G)
#Dict to df
labels_df = pd.DataFrame.from_dict(pos).T
#Reset index + column names
labels_df = labels_df.reset_index()
labels_df.columns = ["names", "x", "y"]
graph_renderer = from_networkx(G, pos, center=(0,0))
.
.
.
plot.renderers.append(graph_renderer)
#Set labels
labels = LabelSet(x='x', y='y', text='names', source=ColumnDataSource(labels_df))
#Add labels
plot.add_layout(labels)
Fixed node positions
From the networkx.spring_layout() documentation: you can add a list of nodes with a fixed position as a parameter.
import networkx as nx
import matplotlib.pyplot as plt
g = nx.Graph()
g.add_edges_from([(0,1),(1,2),(0,2),(1,3)])
pos = nx.spring_layout(g)
nx.draw(g,pos)
plt.show()
Then you can plot the nodes at a fixed position:
pos = nx.spring_layout(g, pos=pos, fixed=[0,1,2,3])
nx.draw(g,pos)
plt.show()
How do I add grid at a specific location on the y axis in a matplotlib plot?
Yes. It's very simple. Use the set_[x|y]ticks methods of axes object and toggle the grid as normal:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.set_yticks([0.2, 0.6, 0.8], minor=False)
ax.set_yticks([0.3, 0.55, 0.7], minor=True)
ax.yaxis.grid(True, which='major')
ax.yaxis.grid(True, which='minor')
plt.show()
If you only want to put in a line or two you can use
ax.axhline(y, linestyle='--', color='k') # horizontal lines
ax.axvline(x, linestyle='--', color='k') # vertical lines
with line style and color (or all the rest of line/artist properties) set to what ever you want
To improve the answer of #tacaswell here's an example using the concept of axhline and tweaking it to look similar to a line grid. In this exapmle it's used a starting default grid only on the x-axis, but it's possible to add a grid also on the y-axis (or only on this axis) by simpy add ax.xaxis.grid(True) to the code.
First one simply start drawing a line at the desired position:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.xaxis.grid(True)
ynew = 0.3
ax.axhline(ynew)
plt.show()
obtaining the following result
that is not very similar to a line grid.
By changing color and line width like below:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.xaxis.grid(True)
ynew = 0.3
ax.axhline(ynew, color='gray', linewidth=0.5)
plt.show()
we obtain this, that now is in practice equal to a line grid.
If then we want also to add a tick and related label on the y-axis, in the position where the new line is:
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
ax.xaxis.grid(True)
ynew = 0.3
ax.axhline(ynew, color='gray', linewidth=0.5)
yt = ax.get_yticks()
yt=np.append(yt,ynew)
ax.set_yticks(yt)
ax.set_yticklabels(yt)
plt.show()
that leads to:
Oh no! Some approximation occurred and the label at 0.6 not represents exactly the number 0.6. Don't worry, we can fix that simply by rounding the label array like follow:
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
ax.xaxis.grid(True)
ynew = 0.3
ax.axhline(ynew, color='gray', linewidth=0.5)
yt = ax.get_yticks()
yt=np.append(yt,ynew)
ax.set_yticks(yt)
ax.set_yticklabels(np.round(yt,1))
plt.show()
and TA-DAAA :)