Hover tooltip with stacked Area chart in holoviews - python

I am creating a stacked area chart in holoviews with bokeh backend, similarly to the example here:
http://holoviews.org/reference/elements/matplotlib/Area.html
I would like to have a Hover tooltip but if I add it to the code, the resulting chart shows the hover cross but no data is displayed in the tooltip.
My code:
import holoviews as hv
values = np.random.rand(5, 20)
percentages = (values/values.sum(axis=0)).T*100
overlay = hv.Overlay([hv.Area(percentages[:, i], vdims=[hv.Dimension('value', unit='%')]).opts(tools=["hover"]) for i in range(5)])
stackA = hv.Area.stack(overlay)
I also tried putting the hover option in the hv.Stack step instead:
stackA = hv.Area.stack(overlay).opts(tools=["hover"])
but this does nothing.
I would like the hover tooltip to show the area value below the cursor and potentially other dimensions of my dataset.

This is a known issue: https://github.com/pyviz/holoviews/issues/3187. The same is valid for the Spread element.
The reason is (my guess) that bokeh has no hovertool for Patch, which is the glyph used to render Area and Spread elements: https://stackoverflow.com/a/53384398. So at the moment your best bet is probably trying to implement the vectorized workaround proposed in that stackoverflow answer in holoviews/plotting/bokeh/chart.py.

Related

Altair: fix title position for interactive() chart

I am using Altair to create a mark_point plot with the slightly strange combination (as far as I can tell via searching) of clip=False and .interactive(). This allows me to pan and zoom the axes while the points themselves are allowed to leave the axes bounding box and stay visible.
An unintuitive side effect of this is that the plot title moves its position to get out of the way of the points as they leave the axes - to the point where the title will in fact leave the screen altogether if I pan down far enough. I would like to have the title stay in a fixed position regardless of the contents of a mark moving around outside the axes.
It seems like this should be possible via configure_title but I can't figure it out. The frame keyword controls the reference for the anchor, but there is no option for using screen pixels instead of the data. I checked the Vega documentation for Title and it does not appear like there are any relevant properties that Altair is not controlling. Weirdly, the axis labels don't have this problem: they stay in their place even as the points move over them. I would like the title to behave the same way.
Here is a very simple MRE that creates a plot with this problem:
import altair as alt
import pandas as pd
import numpy as np
df = pd.DataFrame({'x': np.linspace(0, 4, 100)})
df['y'] = np.sin(2 * np.pi * df['x'])
chart = alt.Chart(df, title='TITLE HERE').mark_point(filled=True, size=100, clip=False).encode(
x='x:Q', y='y:Q').interactive()
chart = chart.configure_title(frame='group')
The obvious workaround is to not use a chart title and instead use a mark_text with clip=False and fixed pixel coordinates at the top of the window to manually make my own title, but I would rather not have to jump through that hoop every time I make a plot.

How to add/ modify/ patch altair chart data dynamically?

I would like to add chart(s), or data to existing chart dynamically (via ipywidget.interact) as in the code below (chart + dotchart). I get nearly what I want except whole chart gets re-drawn and this causes flickering.
How do I add/ modify/ patch data dynamically and avoid re-drawing whole chart?
Thanks!
import pandas as pd
import numpy as np
import altair as alt
from ipywidgets import interact
df = pd.DataFrame({"xval": range(100), "yval": np.random.randint(0,100,100)})
chart = alt.Chart(df).mark_point().encode(x="xval", y="yval",)
def update(x, y):
dot = pd.DataFrame(dict(x=[x], y=[y]))
dotchart = alt.Chart(dot).mark_point().encode(x="x", y="y", color=alt.value("red"))
return chart + dotchart
interact(update, x=(0, 100), y=(0, 100))
# x, y widgets that control position of 'red dot'
The only way to patch data into an Altair chart without re-rendering it is in Javascript, using the Vega View API. You can see an example of this here: https://vega.github.io/vega-lite/tutorials/streaming.html.
I don't know of any prior work on calling the Vega view API from Python, but it's possible in principle.
See the related Altair feature request here: https://github.com/altair-viz/altair/issues/426.
you may be able to do so using the top-level chart configuration methods that altair provides. See here: https://altair-viz.github.io/user_guide/configuration.html
This way you can update many many properties of the current chart without regenerating it.

Using Bokeh for a dropdown menu which would create different charts

I am trying to create a dropdown interface for my work. My dataset looks like this, it is a random dataset
Now I would like 2 dropdowns say CNN and BBC here. After selecting a channel from dropdown, I would like to select a Topic which would produce a bar chart according to it's value.
I am trying to access just one value initially, but it gives me a blank graph.
from bokeh.plotting import figure
from bokeh.io import output_notebook,show,output_file
p=figure()
import csv
data = [row for row in csv.reader(open('C:/Users/Aishwarya/Documents/books/books_q4/crowd_computing/Bokeh-Python-Visualization-master/interactive/data/data.csv', 'r',encoding="utf8"))]
p.vbar(x=data[1][2], width=0.5, bottom=0,
top=data[1][1], color="firebrick")
#output_notebook()
output_file('1.html')
show(p)
There are probably two issues going on:
The first is that if you are using categorical coordinates on an axis, e.g. "CNN" which it appears you are expecting to use, then you need to etll Bokeh what the categorical range is:
p.figure(x_range=["CNN", ...]) # list all the factors for x_range
If you need to update the axis later you can update the range directly:
p.x_range.factors = [...]
Additionally, as of Bokeh 0.13.0 there is a current open issue that prevents "single" factors from working as coordinates: #6660 Coordinates should accept single categorical values. The upshot is that you will have to put the data in a Bokeh ColumnDataSource explicityl (always an option), or in this case a workaround is also just to pass a single-item list instead:
p.vbar(x=["cnn"], ...)
Here is a complete update of your code, with some fake data put in:
from bokeh.plotting import figure
from bokeh.io import show
p = figure(x_range=["cnn"])
p.vbar(x=["cnn"], width=0.5, bottom=0, top=10, color="firebrick")
show(p)
I would also recommend studying the User's guide section Handling Categorical Data.

Python Bokeh: Plotting same chart multiple times in gridplot

I'm currently trying to get an overview of plots of data of different dates. To get a good feeling of the data I would like to plot relevant plots next to each other. This means I want to use the same plot multiple times in the gridplot command. However what I noticed is that when i use the same chart multiple times it will only show it once in the final .html file. My first attempt at solving this was to use a copy.deepcopy for the charts, but this gave the following error:
RuntimeError: Cannot get a property value 'label' from a LineGlyph instance before HasProps.__init__
My approach has been as follows:
from bokeh.charts import Line, output_file, show, gridplot
import pandas as pd
output_file('test.html')
plots = []
df = pd.DataFrame([[1,2], [3,1], [2,2]])
print(df)
df.columns = ['x', 'y']
for i in range(10):
plots.append(Line(df, x='x', y='y', title='Forecast: ' + str(i),
plot_width=250, plot_height=250))
plot_matrix = []
for i in range(len(plots)-1, 2, -1):
plot_matrix.append([plots[i-3], plots[i-2], plots[i]])
p = gridplot(plot_matrix)
show(p)
The results of which is a an html page with a grid plot with a lot of missing graphs. Each graph is exactly shown once (instead of the 3 times required), which leads me to think that the gridplot does not like me using the same object multiple times. An obvious solve is to simply create every graph 3 times as a different object, which I will do for now, but not only is this inefficient, it also hurts my eyes when looking at my code. I'm hoping somebody has a more elegant solution for my problem.
EDIT: made code runable
This is not possible. Bokeh plots (or Bokeh objects in general) may not be re-used in layouts.

Python graph- change axis marker colours and legend border colours

I'm using Python to plot a couple of graphs and I'm trying to change the formatting and essentially 'brand' the graph. I've managed to change most things using pylab.rcParams[...], but I can't work out how to change the colour of the markers on the axes and the border around the legend. Any help would be much appreciated. The line below is an example of the type of code I've been using to edit other parts. Basically just lines taken from matplotlibrc, but I can't find them to change everything I want.
pylab.rcParams[axes.labelcolor' = '#031F73'
If you just want to use rcParams, the proper parameters are xticks.color and yticks.color. I can't seem to find a key for the legend frame color. You can set that (along with the tick colors) programmatically though.
import pylab
pylab.plot([1,2,3],[4,5,6], label ='test')
lg = pylab.legend()
lg.get_frame().set_edgecolor('blue')
ax = pylab.axes()
for line in ax.yaxis.get_ticklines():
line.set_color('blue')
for line in ax.xaxis.get_ticklines():
line.set_color('blue')
for label in ax.yaxis.get_ticklabels():
label.set_color('blue')
for label in ax.xaxis.get_ticklabels():
label.set_color('blue')
pylab.show()

Categories