Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify the interactive_xy_plot interface, and make it work with specified y-scale #1

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

meson800
Copy link

The old interface is to have the user create a plot function and a callback function. In the callback function, the plot is created without x/y ranges, but is initialized in interactive_xy_plot to use DataRange1d's. Then, the user is responsible for the first call to the callback, which fills the data source.

This fails under certain circumstances when the y range is specified in addition to the x-range (the docstring was also inaccurate regarding use of the y-axis). I was plotting (see Piazza code) a vector field using "segment", which exceeded the given y-range. Because the y-range is overridden to be flexible with respect the data, you get infinite re-scaling when the plot is generated, without any user input.

bokeh_plotting

This change is tested against that test case, and is partially tested with HW1 solutions and some of the other test cases. If just the x-axis is specified in the bokeh.plotting.figure constructor, the y-axis is still setup to be auto-ranged by the data. I didn't rebuild the Python module, just copy and pasted the code into Jupyter and renamed the interactive_xy_plot to something local to test, so that would also be good to check.

The new interface, using an example from the lecture notes, would be just directly specifying the ranges in the bokeh.plotting.figure constructor.

I also don't have a fantastic grasp on Bokeh yet, so take this PR with a grain of salt.

slider_params = (biocircuits.AttributeContainer(title='k', start=0.1, 
                                                end=10, value=1, step=0.05),
                 biocircuits.AttributeContainer(title='n', start=1, 
                                                end=10, value=1, step=0.1))

extra_args = (400,)


def hill_callback(source, x_range, y_range, sliders, toggles, n_points):
    # Set up x values, keeping minimum at zero
    x = np.linspace(0, x_range.end, n_points)
    
    # Pull out slider values
    k = sliders[0].value
    n = sliders[1].value
    
    # Compute Hill function
    source.data['x'] = x
    source.data['y'] = (x/k)**n / (1 + (x/k)**n)


def hill_plot(callback, sliders, toggles, extra_args):
    # Set up plot
    p = bokeh.plotting.figure(plot_width=500,
                              plot_height=300,
                              x_axis_label='x',
                              y_axis_label='f(x;k,n)',
                    # -- CHANGE, just specify axis limits here
                              x_range=bokeh.models.Range1d(0,10))

    # Set up empty data source
    source = bokeh.models.ColumnDataSource()

    # Populate glyphs
    p.line('x', 'y', source=source, line_width=2)

    # ---CHANGE-> NO LONGER NEED THIS CALL
    #callback(source, bokeh.models.Range1d(0, 10), None, 
    #        sliders, toggles, *extra_args)

    return p, source

hill_app = biocircuits.interactive_xy_plot(hill_plot, hill_callback, 
                                           slider_params, (), extra_args)
bokeh.io.show(hill_app, notebook_url=notebook_url)

…or the first callback call to the _plot_app function
@justinbois
Copy link
Owner

I fixed the doc string issue yesterday, but haven't pushed (internet out all day yesterday).

I intentionally left call to the callback in the base plot function because I was thinking of use cases where you might want to call the plotting function in other places in the notebook to generate a one-off plot without sliders. Another use case that I use in other applications is to build the plot using some Python functions, and then having a pure JavaScript callback. Under the hood, this has already appeared in one of the notebooks in class. In other words, you might not want to populate the glyphs of the base plot using the callback function (though in most cases, this would be the best practice), and I wanted to leave the flexibility to do that.

@meson800 , how strong is your opinion that we should do the the way you suggest (which I considered, but ultimately chose otherwise, but I'm definitely willing to listen to other opinions)?

The infinite rescaling is a problem I'm aware of and can be dealt with by setting, e.g., p.x_range.range_padding = 0.

Coincidentally, I'm not a big fan of quiver plots, preferring stream plots instead. I will be adding a stream plot function to this package, but not until it appears in full form in one of the instructional notebooks for the course, as is our M.O. in the spirit of teaching students how to do analyses themselves (something you've clearly excelled at, @meson800 !)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants