I'm banging my head on this one.
Bokeh's multip_line and HoverTool don't seem to want to play nice with each other. My issue is similar to this one: multi_line hover in bokeh. (side note: I've tried the solution code from that question and it's not working for me, which is probably not a good sign.)
I have my own reproducible example code here, condensed from a heatmap-like plot I'm working on:
from bokeh.plotting import figure, output_file, show
from bokeh.models.mappers import LinearColorMapper
from bokeh.models import ColumnDataSource, ColorBar, HoverTool
output_file("heatmap.html")
p = figure(title="multi_line hover failure example")
p.add_tools(HoverTool(
show_arrow=False,
line_policy="nearest",
tooltips=[
("color", "#color"),
("name", "#name")
]))
patch_xs = [[1, 2, 2, 3], [4, 5, 6, 5], [5, 5, 8, 8]]
patch_ys = [[1, 2, 4, 1], [3, 4, 3, 2], [5, 8, 8, 5]]
patch_colors = [1, 2, 3]
patch_names = ['robert', 'quinn', 'jessy']
line_xs = [[1, 2, 2, 3], [4, 5, 6, 5], [5, 5, 8, 8]]
line_ys = [[-1, -2, -4, -1], [-3, -4, -3, -2], [-5, -8, -8, -5]]
line_colors = [-1, -2, -3]
line_names = ['karen', 'louise', 'charles']
mapper = LinearColorMapper(
palette='Turbo256',
low=min(patch_colors + line_colors),
high=max(patch_colors + line_colors),
)
# patches included to confirm that hover is working
# commenting this out does not make hover work
p.patches('xs', 'ys', line_width=1,
color={'field': 'color', 'transform': mapper},
source=ColumnDataSource(dict(
xs=patch_xs,
ys=patch_ys,
color=patch_colors,
name=patch_names
))
)
p.multi_line('xs', 'ys', line_width=8,
color={'field': 'color', 'transform': mapper},
source=ColumnDataSource(dict(
xs=line_xs,
ys=line_ys,
color=line_colors,
name=line_names
)))
p.add_layout(
ColorBar(
color_mapper=mapper,
location=(0, 0)
),
'left')
show(p)
My code produces the graph below, in which the hover tool works fine for the filled in shapes (as can be seen in the screen shot) but not the lines. Trust me, I've tried mousing all over the lines like a deranged Aladdin trying to coax a genie to come out of them.
Removing the filled in shapes (patches) does not fix it so a left them in to show how the hover tool should be working.
Funnily enough, according to the documentation (https://docs.bokeh.org/en/latest/docs/reference/models/tools.html#bokeh.models.tools.HoverTool) the HoverTool does not work with patches but does work with multi_line. (perhaps it has something to do with using glyphs instead of figure elements?)
Any help would be appreciated
It's a bug. It was fixed in this commit and should be available in Bokeh 2.3.
Alternatively, you could try Bokeh 2.1 - IIRC it was working for me there.
Related
Code to reproduce
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtWidgets
from pyqtgraph import PolyLineROI
app = pg.mkQApp("Arrow Example")
w = QtWidgets.QMainWindow()
cw = pg.GraphicsLayoutWidget()
w.show()
w.resize(1600, 900)
w.setCentralWidget(cw)
p = cw.addPlot(row=0, col=0)
ls = PolyLineROI([[0, 0], [1, 1], [2, 0]])
p.addItem(ls)
print(ls.getState())
ls.rotate(angle=90, center=[1, 1])
print(ls.getState())
p.setRange(QtCore.QRectF(-20, -10, 60, 20))
ls.setPoints([[0, 0], [1, 1], [2, 9]])
if __name__ == '__main__':
pg.exec()
Expected behavior
the result of getState should update after rotate. And the follow-up setPoints should be functional.
Real behavior
the ROI become strange after setPoints
image
I am trying to create a custom hover tool using which takes the y-value of the plot and maps the value to different value.
The code I could come up with so far to achieve this functionality is
from bokeh.models import HoverTool
import holoviews as hv
df = pd.DataFrame(
{
"zero": [0, 0, 0, 0, 0, 0, 0],
"one": [1, 1, 1, 1, 1, 1, 1],
"two": [2, 2, 2, 2, 2, 2, 2],
}
)
mapping = {i: c for i, c in enumerate(df.columns)}
def col_mapping(num):
return mapping[int(num)]
hover = HoverTool(tooltips=[("x", "$x"), ("y", "$y")])
img = hv.Image((df.index, np.arange(df.shape[1]), df.T)).opts(tools=[hover])
img
x and y will be float values. So the idea is to map the y coordinates to its corresponding value in the mapping dictionary
Let me know how I can get a new value in the hover tool so that when the value is b/w 0 and 1 it will be
Thanks
Here's how I'd do it:
code = f"return ({json.dumps(mapping)})[Math.floor(special_vars.y)];"
hover = HoverTool(tooltips=[("x", "$x"), ("y", "$y"), ('mapped_y', '$y{0}')],
formatters={'$y': CustomJSHover(code=code)})
If you need a some more complicated code than that of col_mapping, then you'd have to use a ColumnDataSource and just add to it the fully transformed column.
Recently, multi-gesture edit tools have been added to Bokeh. For example, using the script below, I can interactively draw points in a jupyter notebook using the PointDrawTool. My question is, how do I get the updated data for the points that I generate or edit into a numpy array or a similar data structure?
from bokeh.plotting import figure, output_file, show, Column
from bokeh.models import DataTable, TableColumn, PointDrawTool, ColumnDataSource
from bokeh.io import output_notebook
# Direct output to notebook
output_notebook()
p = figure(x_range=(0, 10), y_range=(0, 10), tools=[],
title='Point Draw Tool')
p.background_fill_color = 'lightgrey'
source = ColumnDataSource({
'x': [1, 5, 9], 'y': [1, 5, 9], 'color': ['red', 'green', 'yellow']
})
renderer = p.scatter(x='x', y='y', source=source, color='color', size=10)
columns = [TableColumn(field="x", title="x"),
TableColumn(field="y", title="y"),
TableColumn(field='color', title='color')]
table = DataTable(source=source, columns=columns, editable=True, height=200)
draw_tool = PointDrawTool(renderers=[renderer], empty_value='black')
p.add_tools(draw_tool)
p.toolbar.active_tap = draw_tool
handle = show(Column(p, table), notebook_handle=True)
Using such method of showing plot does not provide synchronization between Python and JS. To solve this you can use bookeh server, as described here. Usualy you use commnad:
bokeh serve --show myapp.py
Then you can embed this application in your jupyter. For me it was very inconvinient so I started to look for other solutions.
It is possible to run bookeh app from jupyter notebook, you can find example here.
Sample code for your problem would look like this:
from bokeh.plotting import figure, output_notebook, show, Column
from bokeh.models import DataTable, TableColumn, PointDrawTool, ColumnDataSource
output_notebook()
def modify_doc(doc):
p = figure(x_range=(0, 10), y_range=(0, 10), tools=[],
title='Point Draw Tool')
p.background_fill_color = 'lightgrey'
source = ColumnDataSource({
'x': [1, 5, 9], 'y': [1, 5, 9], 'color': ['red', 'green', 'yellow']
})
renderer = p.scatter(x='x', y='y', source=source, color='color', size=10)
columns = [TableColumn(field="x", title="x"),
TableColumn(field="y", title="y"),
TableColumn(field='color', title='color')]
table = DataTable(source=source, columns=columns, editable=True, height=200)
draw_tool = PointDrawTool(renderers=[renderer], empty_value='black')
p.add_tools(draw_tool)
p.toolbar.active_tap = draw_tool
doc.add_root(Column(p, table))
show(modify_doc)
I am new to Prolog and came across this practice excercise. The question asks to define a predicate
zipper([[List1,List2]], Zippered). //this is two lists within one list.
This predicate should interleave elements of List1 with elements of List2.
For example,
zipper([[1,3,5,7], [2,4,6,8]], Zippered) -> Zippered = [1,2,3,4,5,6,7,8].
zipper([[1,3,5], [2,4,6,7,8]], Zippered) -> Zippered = [1,2,3,4,5,6,7,8].
So far I have a solution for two different list:
zipper ([],[],Z).
zipper([X],[],[X]).
zipper([],[Y],[Y]).
zipper([X|List1],[Y|List2],[X,Y|List]) :- zipper(List1,List2,List).
I am not sure how I can translate this solution for one list. Any suggestion on where I can start would be greatly helpful!
Firstly you should change zipper ([],[],Z). to zipper ([],[],[]).. Then to make it work for one list you could do what mat recommended in the comment or you could change it a little. So my version is:
zipper([],[],[]).
zipper([X,[]],X).
zipper([[],Y],Y).
zipper([[X|List1],[Y|List2]],[X,Y|List]) :- zipper([List1,List2],List).
And for your examples:
?- zipper([[1,3,5,7], [2,4,6,8]], Zippered).
Zippered = [1, 2, 3, 4, 5, 6, 7, 8] ;
Zippered = [1, 2, 3, 4, 5, 6, 7, 8] ;
false.
?- zipper([[1,3,5],[2,4,6,7,8]],Zippered).
Zippered = [1, 2, 3, 4, 5, 6, 7, 8] ;
false.
I have a set of datapoints such as (THIS IS AN EXAMPLE)
val=4; (*this value is calculated before in the program, so it not known a priori*)
x={0,1,2,3};
data=Table[0, {val}];
data[[1]] = {1,5,6,8};
data[[2]] = {9,7,1,3};
data[[3]] = {3,4,5,6};
data[[4]] = {2,2,4,6};
Now I can plot each of these data with ListPlot as
ListPlot[Transpose[{x,data[[1]]}]]
and if I want to plot more than one I can do
ListPlot[{Transpose[{x, data[[1]]}], Transpose[{x, data[[2]]}]}]
but how can I plot all of them in one code single line, by considering that val is calculated before in the program?
Is there a way to do something like
For[i = 1, i < val + 1, i++, ListPlot[Transpose[{x,data[i]}]]......]
having a single graph with all x-y curves?
Indeed I would like a static picture of
Manipulate[ListPlot[Transpose[{x, data[[i]]}]], {i, 1, val,1}]
Thanks
Virgilio
You want to "do the same thing" to every element of a list. That should tell you to think of using Map. Your list is named data and each element is your four element sublist. If you look at the help page for Map it shows you need to think up a function that does what you need to do to each individual sublist. You have already understood that you need to use Transpose with x and your sublist so that tells you your function and you are almost there. The result of Map will be a list of all those results. So
In[1]:= x = {0, 1, 2, 3};
data = {{1, 5, 6, 8}, {9, 7, 1, 3}, {3, 4, 5, 6}, {2, 2, 4, 6}};
ListPlot[Map[Transpose[{x, #}] &, data], Joined -> True]
Out[3]= ...FourOverlaidPlotsSnipped...
Go through that a word at a time until you can really understand the thinking that was done to be able to write that. You will use this idea again and again if you keep using Mathematica.
For the example you give the cleanest method is to use DataRange:
data = {{1, 5, 6, 8}, {9, 7, 1, 3}, {3, 4, 5, 6}, {2, 2, 4, 6}};
ListLinePlot[data, DataRange -> {0, 3}]
Please ask your future questions on the dedicated Mathematica StackExchange site: