I am using xwheel_zoom (WheelZoomTool) for a Bokeh chart with datetime axis.
p = figure(x_axis_type="datetime", tools='xwheel_zoom, crosshair, undo, reset')
I provide pandas TimeStamp as the x value of this chart. For example:pd.Timestamp.now(tz='utc'). The x axis range of this chart is for the last 24 hours.
start = pd.Timestamp.now(tz='utc') - pd.Timedelta(hours=24)
Using xwheel_zoom, I could zoom in to see my chart better for a given time (e.g. last hour).
Is there any way in Bokeh I could achieve this zoom functionality by coding or connecting to the xwheel_zoom and controling it with a Bokeh widget (e.g. Bokeh dropdown)?
My objective is to have a button to click on and let it show me the zoomed in x_axis for the last hour, or show the chart between a datetime period I define. Idealy, I do not want to re-define/re-draw the chart again and just want to control the xwheel_zoom functionality.
You don't need any tools to do that. Just change the desired range in a callback. Something like:
b = Button()
def update():
p.x_range.update(start=0, end=1)
b.on_click(update)
The example will work only if used with bokeh serve. If you're not using that, you can rewrite the code to work with CustomJS and js_on_click.
Related
I'm hoping someone can point me in the right direction. The python datavis landscape has now become huge and there are so many options that I'm a bit lost on what the best way to achieve this is.
I have an xarray dataset (but it could easily be a pandas dataframe or a list of numpy arrays).
I have 3 columns, A, B, and C. They contain 40 data points.
I want to plot a scatter plot of A vs B + scale*C where scale is determined from an interactive slider.
The more advanced version of this would have a dropdown where you can select a different set of 3 columns but I'll worry about that bit later.
The caveat on all of this is that I'd like it to be online and interactive for others to use.
There seem to be so many options:
Jupyter (I don't use notebooks so I'm not that familiar with them but
with mybinder I assume this is easy to do)
Plotly
Bokeh Server
pyviz.org (this is the really interesting one but again, there'd seem
to be so many options on how to accomplish this)
Any thoughts or advice would be much appreciated.
There are indeed many options and i'm not sure what is best but i use bokeh a lot and am happy about it. The example below can get you started. To launch this open a cmd in the directory where you save the script and run "bokeh serve script.py --show --allow-websocket-origin=*".
from bokeh.plotting import figure
from bokeh.io import curdoc
from bokeh.models.widgets import Slider
from bokeh.models import Row,ColumnDataSource
#create the starting data
x=[0,1,2,3,4,5,6,7,8]
y_noise=[1,2,2.5,3,3.5,6,5,7,8]
slope=1 #set the starting value of the slope
intercept=0 #set the line to go through 0, you can change this later
y= [slope*i + intercept for i in x]#create the y data via a list comprehension
# create a plot
fig=figure() #create a figure
source=ColumnDataSource(dict(x=x, y=y)) #the data destined for the figure
fig.circle(x,y_noise)#add some datapoints to the plot
fig.line('x','y',source=source,color='red')#add a line to the figure
#create a slider and update the graph source data when it changes
def updateSlope(attrname, old, new):
print(str(new)+" is the new slider value")
y = [float(new)*i + intercept for i in x]
source.data = dict(x=x, y=y)
slider = Slider(title="slope", value=slope, start=0.0, end=2.0,step=0.1)
slider.on_change('value', updateSlope)
layout=Row(fig,slider)#put figure and slider next to eachother
curdoc().add_root(layout)#serve it via "bokeh serve slider.py --show --allow-websocket-origin=*"
The allow-websocket-origin=* is to allow other users to reach out to the server and see the graph. The http would be http://yourPCservername:5006/ (5006 is the default bokeh port). If you don't want to serve from your PC you can subscribe to a cloud service like Heroku: example.
I'm working on creating a graph that represents activity over time, modelled after a chart in Sleep As Android. It's similar to a heatmap, but does not use color variation. Each column is a date, and the y axis is the full duration of the day. Whatever intervals of time where an activity occurs are blocked off with a bar. Here's what I have so far:
So far I have only been able to accomplish this by manually plotting rectangles on the figure. I loop through a list of events with dates, start times, and end times, plotting them like so:
ax.add_patch(patches.Rectangle((31*event_month + event_day, end_time), # (x,y)
0.75, # width
duration)) # height
This date handling is clearly wrong - it's just for the purpose of demonstration.
Normally when creating a histogram, I can plot using date objects directly with something like this:
fig.autofmt_xdate()
ax.fmt_xdata = matplotlib.dates.DateFormatter('%Y-%m-%d')
I'd like to be able to somehow just use the date objects directly when plotting. Is there a way to accomplish this in pyplot, or do I need to do something like converting the x axis to use POSIX time and just calculating where I put date labels?
I am experimenting with time plots such as this example from bokeh. Is it possible to create minor ticks for the x-axis? I tried all the different options inside p.xaxis.minor but none seemed useful.
Here's the code from example:
import pandas as pd
from bokeh.plotting import figure, output_file, show
AAPL = pd.read_csv(
"http://ichart.yahoo.com/table.csv?s=AAPL&a=0&b=1&c=2000&d=0&e=1&f=2010",
parse_dates=['Date']
)
output_file("datetime.html")
# create a new plot with a datetime axis type
p = figure(width=800, height=250, x_axis_type="datetime")
p.line(AAPL['Date'], AAPL['Close'], color='navy', alpha=0.5)
show(p)
And the image:
From what it seems it's automatically turned off, and the x-axis updates properly as you zoom in, but it would be great to include minor ticks for visualization purpose.
As of Bokeh 0.10, the BokehJS DatetimeTicker sets num_minor_ticks to zero on the internal tickers that it uses:
https://github.com/bokeh/bokeh/blob/master/bokehjs/src/coffee/ticking/datetime_ticker.coffee#L23
There is currently no simple way exposed to override this (You could write some JavaScript to reach in directly, but that would be a pain). I am not sure if this state of affairs is due to some inherent problem with minor ticks and datatime ranges, or if this was merely an oversight. Please make a new issue on the Bokeh GH issue tracker so it can be investigated:
https://github.com/bokeh/bokeh/issues
I know that I can do logarithmic scales with bokeh using the plotting API:
p = figure(
tools="pan,box_zoom,reset,previewsave",
y_axis_type="log", y_range=[0.001, 10**22], title="log axis example",
x_axis_label='sections', y_axis_label='particles'
)
However, I can't figure out how to get this to apply to high level charts such as Bokeh.charts.Bar. In general I'm having a lot of trouble grokking what to relationship is between a Chart and a figure. Can anyone point me to some documentation on this or explain how to modify things which are only exposed through figure and have them affect my Chart.
I am specifically going to update the documentation describing the different Bokeh APIs this week, but for now, the three Bokeh APIs in increasing order of "level":
models interface: lowest level API, base serialization layer, must put everything together everything manually
glyphs interface (bokeh.plotting): mid-level API, easily create plots/figures centered around visual glyphs with attributes tied to data
charts interface (bokeh.charts): high level API for canned/schematic statistical charts, e.g. "BoxPlot" and "Histogram".
There is no particular relation between figure and the various chart functions, except that they both produces subclasses of Plot as output.
I am not sure it is currently possible to add a log axis to the Bar plot in "charts" interface (that would be a reasonable feature to add). However it would be simple to make a boxplot "by hand" using the middle "glyphs" interface using rect or quad glyphs. Here is a quick example:
from bokeh.plotting import figure, output_file, show
output_file("bars.html")
p = figure(title="log bar example", y_axis_type="log")
p.quad(
bottom=0, top=[10**5, 10**8, 10**3],
left=[0, 2, 4], right=[1,3,5]
)
show(p)
I have a candlestick chart that is dinamically created for different lengths and stocks. The chart is created first (the creation of the chart is contained in a function "createChart") and not shown until the moment the user presses on the button "Show Chart", that will hence call the instruction .show() and display the plot previously created. When the user clicks on the button he gets the following result:
However, what I would like to get is a chart that is already zoomed-in, let's say, on the last 5% of data. So what I would like to get is when the user presses on the button "Show Chart" the plot (that has been already fully created into the "createChart" function) should zoom on the last couple of months, so Nov 2012 - Dec 2012, but so allowing the user to scroll back/forward:
Hence my question is: to make the chart more user-friendly and zoom-in directly on the last observations (that in finance are the most relevant), but still giving the user the possibility of slide the chart and going back or forward as he wishes, how could I customize the .show() method to get this result?
I propose you to use the navigation toolbar tools; here is an example:
from pylab import *
x=[1,2,2,3,5]
y=[2,3,4,5,6]
fig=figure() # create and store a figure
tb=fig.canvas.toolbar # get the toolbar of the figure
ax=fig.add_subplot(1,1,1) # add axes to the figure
ax.plot(x,y) # plot
tb.push_current() # save the current zoom in the view stack
ax.set_xlim([1,3]) # change xlims
ax.set_ylim([2,5]) # change ylims
tb.push_current() # save the new position in the view stack
show() # show the figure
What about xlim((from, to)) and ylim((from, to))?
It limits only the view, not what data is actually plotted. You might have to pay attention to the case where you have a whole lot of data, then the plot() or show() command takes ages to load.