first of all congratulations on the new features which you brought to the python version of plotly! I really love all the widget interactivity and callbacks. This opens a new universe! Your effort has been really great. Maybe you want to further advertise this on the github readme? Because at first sight, I did not read the change log because I did not expect such profound changes! Afterwards, I stumbled upon the release Medium post and, boy, this was a great day
Playing around with the callbacks, I was wondering on how to create a zoomable histogram.
Something similar to https://uwdata.github.io/falcon/flights/
Here you can zoom into the histogram with the mouse wheel and the histogram will recalculate the bins.
So, the bin size should stay the same but we want to subscribe on a change of the xaxis of the figure layout.
If the user zoom, then the bin width should be reduced to still show the same number of bins. Also, the y-axis should be adjusted
What is the best way to implement this for the Jupyter lab/notebook platform?
Should the on_change callback create a new trace and hide the old one?
The user should still be able to reset the zoom with the double click.
Thanks a lot for the kind words! This is a pretty neat example, and yes it is possible to do now with the FigureWidget.
The general idea is to display the data using a plotly histogram trace type. This trace has built-in logic for bin selection based on the number of samples and the spread. Then, listen for changes in the xaxis range and update the data passed to the histogram to include only the points that are present within the current view. This will cause the trace to recompute binning and set the appropriate y-axis range. To get double-click to restore the original view, make sure you explicitly specify the xaxis range of the initial trace.
Hereโs an example
# imports
import plotly.plotly as py
import plotly.graph_objs as go
import numpy as np
# Generate dataset
x = np.random.randn(5000)
# Create figure and get reference to histogram trace
fig = go.FigureWidget(
[go.Histogram(x=x)],
go.Layout(
title='Dynamic Histogram',
xaxis={'range': [-4, 4],
'title': 'x'},
yaxis={'title': 'count'},
bargap=0.05))
hist = fig.data[0]
# Install xaxis zoom callback
def handle_zoom(xaxis, xrange):
filtered_x = x[np.logical_and(xrange[0] <= x, x <= xrange[1])]
hist.x = filtered_x
fig.layout.xaxis.on_change(handle_zoom, 'range')
# Display FigureWidget
fig
thank you so much for your answer.
It works great.
However, I noticed one issue with the go.Histogram.
And this is, that the nbinsx cannot be changed arbitrarily?
For example when I set
fig.data[0].nbinsx = 19
Then the figure will only update for some specific values. Currently it changes similar to those breakpoints: 2, 4, 5, 10, 20, 50, 75
Is there a way to prevent this arbitrary binning?
Or is the only solution to calculate the histogram yourself with pandas and then just let plotly show the result as a bar chart?
Currently, I use the histogram with ipywidgets which should allow the user to set the binning. And, there I also have this problem. However, the problem also exists if I set the value manually as described aboveโฆ
Yeah, the nbinsx property only sets the max number of bins it doesnโt force the exact number. You can be more precise by using the xbins.size and xbins.start properties. Hereโs an updated example that will always show exactly 9 bins (You could also set num_bins in an ipywidget callback to change it interactively)
# imports
import plotly.plotly as py
import plotly.graph_objs as go
import numpy as np
# Generate dataset
x = np.random.randn(5000)
# Create figure and get reference to histogram trace
fig = go.FigureWidget(
[go.Histogram(x=x)],
go.Layout(
title='Dynamic Histogram',
xaxis={'range': [-4, 4],
'title': 'x'},
yaxis={'title': 'count'},
bargap=0.05))
hist = fig.data[0]
num_bins = 9
# Install xaxis zoom callback
def handle_zoom(xaxis, xrange):
filtered_x = x[np.logical_and(xrange[0] <= x, x <= xrange[1])]
with fig.batch_update():
hist.x = filtered_x
hist.xbins.start = xrange[0]
hist.xbins.size = (xrange[1] - xrange[0])/9
fig.layout.xaxis.on_change(handle_zoom, 'range')
# Display FigureWidget
fig
Hi Jon!
I was wondering if it is possible to save a graph like this from jupyter notebook to the cloud. I notice that every time I try to do so I lose the rebinning feature that I like so much.
Thanks!
Erica
Unfortunately this isnโt possible, because the rebinning relies on executing Python code which isnโt available in the chart studio context. If you need to host this kind of functionality outside of the jupyter notebook, it would be possible to implement it using Dash (See https://dash.plot.ly/interactive-graphing for info on figure callbacks in Dash).
Thanks for your effort first. My question is how can i use Dynamic Hitogram in my Dash App. Actually i tried a lot to write a callback but fail to write. Can you please help me out.