How to make Altair plots responsive - python

Can one make Altair plots fit the screen size, rather than have a pixel-defined width and height? I've read things about autosize "fit", but I am unsure about where to specify these.

While it is true that the vega-lite determines the size of the chart itself, it is possible to treat the chart (rendered as canvas) like an image.
I used an example from w3css which applied to my vega-light charts scales the charts accordingly. Basically it is then scaled proportionally according to the surrounding container.
Example HTML generated by vega-light:
<div id="visInteractiveYear" class="vega-embed">
<canvas width="1366" height="960" class="marks" style="width: 976px; height: 686px;"></canvas>
</div>
Here the CSS snippet, that basically overwrite width/height (style) of the canvas:
canvas.marks {
max-width: 100%!important;
height: auto!important;
}
Here a test without any scaling and its original size:
Here a test with scaling to fit within the surrounding box.
If you are using interactive charts, the scaling might be a problem. I guess that the click positions are not correctly translated (since canvas is scaled, but the logic of vega does not know about it), thus strange behaviour is the result. In my case, the selection has always an offset to the mouse cursor.
Also, the user has to zoom in to be able to read the chart, since it is proportionally scaled. Probably in most cases not the ideal user-friendly way to go.
Maybe a better option is to switch to another vega-light spec, which renders the chart for the specific display size, or to switch to a different representation (e.g., a standard list), which can then easily be read on smaller displays (adaptive design).
For complex charts, opening the chart in another browser tab, might be also be a good solution. The user knows about the new tab, the mobile browser has to show only one chart/image, so there is not much clutter to worry about. Thus, it is easy to navigate/scroll in the new tab, since it only contains the chart and maybe a back/close tab button.

There is no way to do this. The dimensions of Altair/Vega-Lite charts are pre-determined by the chart specification and data, and cannot be made to scale with the size of the browser window.

If you are using Altair 4.0 or greater you can use properties() and container:
import altair as alt
from vega_datasets import data
source = data.cars()
plot = alt.Chart(source).mark_circle(size=60).encode(
x='Horsepower',
y='Miles_per_Gallon',
color='Origin',
tooltip=['Name', 'Origin', 'Horsepower', 'Miles_per_Gallon']
).properties(
width='container',
height='container'
)
This will make it responsive, note that this is the size of each plot if you have multiple it doesn't work as you would expect but instead each plot will have the size of the parent container.

Related

How to add Label/text in Bokeh that can be auto rescaled?

I learnt how to add text by using Label in Bokeh in this question.
However, I found that the text doesn't rescale as I zoom in and out.
The ideal behavior is something like Patches, which becomes larger as you zoom in.
How can I configure for this feature?
Related Questions
Selectively show text in Bokeh plot based on zoom level
As of Bokeh 2.3 scalable text is still an open issue:
https://github.com/bokeh/bokeh/issues/9407
There are some potential partial workarounds discussed there, but nothing that concretely works all the time. Depending on your use case, you could potentially use CustomJS callback on the plot ranges to update the text size that you care about in some way.

Display hovertool for the uppermost glyph in bokeh?

I have a graph with several hundreds of glyphs on it, generated in bokeh. All the glyphs have a hovertool associated with them that shows a popup window with an image in it when moused over.
Some of the glyphs have 4-5 glyphs layered underneath them, and when someone mouses over them, all the hovertools are displayed.
Is it possible to only display the hovertool for the uppermost glyph?
As of Bokeh 1.3.4 there is no way to control the number of tooltips displayed without creating some sort of a custom extension. There is an open issue #9087 Provide max_tooltips property on HoverTool that is targeted for one of the next few releases.
But note: that work will just take a tooltip out of multiple to show. The draw order for a single glyph is not well-defined, it can change due to: spatial indexing, selections, "Level of Detail" mode being active, and other things. I don't know that there will ever be a reliable notion of "total z-order" such that e.g the "top" circle could be the one picked (and always be the same index). A more sophisticated selection manager could sort or condition on other CDS column values, etc. though, so you could potentially explicitly provide the order you intend.

Add Label Annotations on Axes

I currently annotate my charts with the last value of each series by adding a Label and supplying my the name of corresponding range it's plotted on:
Label(
...
x=data.index.max(),
y=data.loc[data.index.max(), 'my_col'],
y_range_name='my_range'
...
)
Which gives me:
How do I move the labels so they are positioned on their respective axis?
Example:
Please note that my labels' y-positioning is off, so I need some help with that aspect too. I've tried tweaking the y_offset but this has not yielded any consistently good results.
My data are always numerical time series.
As of Bokeh 1.2 there is no built-in annotation or glyph that will display outside the central plot area. There is an open issue on GitHub that this is similar to that you can follow or comment on. For the time being, something like this would require making a custom extension

How does this Bokeh Column Data Source Work?

I am trying to get a better understanding about the column data source in Bokeh (for Python). I found this code, but I can't seem to find the documentation that explains some things I am looking for, For instance:
Where is the callback from the lasso_select tool? I want to see where the expected functionality is described.
How is the functionality of the lasso_select described in code? (What if I want to change it?)
What is happening to the column data source so that the circles outside the lasso-select region change appearance? (I want to know how I can use the column data source for more complex visualization than is shown by this demo. So I'd like to know what dictionary field is being manipulated, and how is it being manipulated. For example, is there a hidden "color" field or something like that, which isn't explicit in this code?)
What code causes the figure to be redrawn when a lasso_select action is made?
I have many more questions related to this and the CDSView, but I'll stop here for now.
from bokeh.io import output_file, show
from bokeh.layouts import gridplot
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure
output_file("brushing.html")
x = list(range(-20, 21))
y0 = [abs(xx) for xx in x]
y1 = [xx**2 for xx in x]
# create a column data source for the plots to share
source = ColumnDataSource(data=dict(x=x, y0=y0, y1=y1))
TOOLS = "box_select,lasso_select,help"
# create a new plot and add a renderer
left = figure(tools=TOOLS, plot_width=300, plot_height=300, title=None)
left.circle('x', 'y0', source=source)
# create another new plot and add a renderer
right = figure(tools=TOOLS, plot_width=300, plot_height=300, title=None)
right.circle('x', 'y1', source=source)
p = gridplot([[left, right]])
show(p)
This is related to my previous question, where the only answer was very narrow in explaining for that specific question. However, I am really interested in what's going on under the hood to give the results that are seen. It would help my understanding a lot more if I could know some of those details.
1) There is no callback. The tool is responsible for defining a selection geometry, and and then hit-testing baed on that geometry. The hit test results are store in a selection property of the data source. Glyph renderers draw glyphs based on the selection property of their data source. If two glyph renderers (even on different plots) share the same data source, they will both draw the same set of selected/nonselected as a result.
2) If you mean the appearance of the normal vs selected vs non-selected objects, how to configure that is described in the docs here:
https://docs.bokeh.org/en/latest/docs/user_guide/styling.html#selected-and-unselected-glyphs
There are also a few properties on the LassoTool object itself, that control, e.g. whether a selection should be made on every mousemove, or only on mouseup, and what the selection overlay looks like. All of these are recorded in the ReferenceGuide. If you are asking how to change the implementation, as with msot everything in Bokeh, the real work is not done in Python, it is done in the JavaScript library BokehJS. The implementation of the LassoTool is here:
https://github.com/bokeh/bokeh/blob/master/bokehjs/src/lib/models/tools/gestures/lasso_select_tool.ts
If you want something fundamentally different you would need to implement your own custom model, including its JavaScript component. There is an entire User's Guide section about building custom extensions:
https://docs.bokeh.org/en/latest/docs/user_guide/extensions.html
3) The Plot is configured with various Renderers, one of which can be a GlyphRenderer. The GlyphRenderer itself does not draw anything, but it configures various sub-glyphs that are used to draw in specific situations:
glyph draws "normal" versions of glyphs (i.e. when there is no selection on the data source)
selected_glyph draws "selected" versions of glyphs (i.e. the ones inside a lasso or box tool when a selection is active)
nonselected_glyph draws the "non-selected" versions of glyphs (i.e. the ones outside a lasso or box tool when a selection is active) By default the non selection glyph is just a copy of the "normal" glyph with the alpha value set very low.
hover_glyph draws the "hovered" versions of glyphs (i.e. when a hover tool has inspected them)
You configure the appearance in the different situation by configuring properties on the glyphs that are used in each situation. There are sensible defaults for them, but they can be updated as described in the first link of 2)
4) BokehJS has an internal signal/slots event system that is used (among other things) to request canvas redraws whenever various properties change.

Select text in Bokeh plot

I would like to be able to search for specific words in my Bokeh plot. Say that I have a very simple plot:
import numpy as np
from bokeh.plotting import figure, show, output_file
x = np.linspace(0, 4*np.pi, 100)
y = np.sin(x)
TOOLS = "pan,wheel_zoom,box_zoom,reset,save,box_select"
p1 = figure(title="Some sample title", tools=TOOLS)
p1.circle(x,y, legend="sin(x)")
output_file("legend.html", title="legend.py example")
show(p1)
Which results in
I would like to be able to search the text in my browser using [ctrl+f] or [cmd+f]. Is there any way to do that? I would like to be able to search for the title and/or for labels, so in this case, example queries would be one of {sample, title,1,0.5}. Of course this example is hypothetical, but I think it's enough to illustrate the question.
Is there any way to use browser search functionality inside a Bokeh plot?
There is no way to do this in Boken currently, as it renders to an HTML5 canvas object, so the browser just sees the final result of the rendering. If you're willing to use Bokeh's sister library HoloViews, it however has a both Bokeh and SVG backend. When rendered through that SVG backend, your browser will then have access to all the text elements.
To help evaluate plotting libraries to see if they're suitable for your purpose, what you're looking for is basically a SVG backend. Usually it's easy to find a list of supported backends in the documentation of each library.
Also note that "having all individual plot elements accessible to the browser" and "plotting a lot of data points" are conflicting goals. The HTML5 canvas backend works well for plotting lots of data (even more so with datashader) partly because it only exposes the final plot image to the browser. If you want to expose the details of your plot to the browser (e.g via the SVG backend), you should expect to see a performance hit at some point if your plots get bigger (more data) or otherwise more complex, compared to the HTML5 canvas backend.
There is no way to do this. Bokeh plots are not textual DOM elements, everything is rendered on an HTML raster canvas, which the browser only sees as an rectangular area of RGBA pixels.

Categories