Contourplot discussion

Continued from: https://github.com/bokeh/bokeh/issues/8360

The initial proof of concept is here:

What should the API look like? Should it look like figure.image, and take an image, a x-coordinate, a y-coordinate (for the anchor), a x-width, and y-height?

Or should it look closer to the matplotlib API: An optional X matrix/vector, and Y matrix/vector, and then the image.

The advantage of the Matplotlib API is that the X and Y points do not need to be constantly spaced… though skimage contours will be on an evenly spaced X and Y dimension, so it adds some difficulty in mapping the contours from the skimage space back to the X and Y space of the input.

For thresholds, do we want it to default to 10 or so? or require an int (for evenly spaced contours), or an iterable (for a given set of contours).

Also: what does the API for the matplotlib contour generation look like? I might be able to wrap the skimage contour generation into something to match the matplotlib API, so when/if we replace the skimage dependency with the matplotlib library, it is just a drop-in replacement.

@spott Thanks for updating the notebook, it does run through now. It looks like possibly the contour result is transposed x/y from the original image?

We don’t need to worry about matching the MPL API, at least not for the sake of future implemenation changes. Even if we want to try and use the older C or new C++ contour libraries to do the contouring in the future, the public API it not really relevant. It’s the internal usage that would need to be “close to” MPL but I think Bokeh is different enough internally that there is no chance that things being a drop-in replacement regardless. So, we should concentrate on making APIs that fit in to Bokeh cleanly.

As for the inputs, if we can handle non-uniform grids, that’s definitely a plus. Perhaps we can support multiple contour creation functions in bokeh.contour, e.g.:

def contour_data_from_uniform_grid(...)

def contour_data_from_mesh(...)

These low-level functions would encapsulate the cases nicely and afford different capabilities for different users who want more control. Then. the Figure.contour API wan figure out later. options are

  • pick one of the low level functions to wrap as the default
  • try to have an API that could delegate to the right low-level function based on what arguments it gets

It looks like possibly the contour result is transposed x/y from the original image?

It is transposed from the Figure.image result, though I’m not entirely sure why. Ultimately, we can just transpose the image and work on the transposed one, but I’m not sure why that is the case (might be something to do with the way skimage generates the contours).

I’m also noticing some weirdness with some of the contour values now, I’ll have to play around to make sure it is correct.

As for the inputs, if we can handle non-uniform grids, that’s definitely a plus.

Ok. I’ll have to think about how best to do this… there are two immediate ideas that come to mind:

  • interpolate from the non-uniform mesh onto a constant spacing grid, then generate contours.
  • find a function that moves from a uniform grid of unit spacing to the non-uniform grid spacing, so interpolate the grid. This should be doable as two separate f(x)-> x’ and f(y) -> y’.

I’m not sure how to manage this without interpolation… does bokeh have an interpolation routine in the codebase somewhere, or should I write one up?

does bokeh have an interpolation routine in the codebase somewhere, or should I write one up?

There’s not one in Bokeh. With a few exceptions (e.g hexbin) there is not much “real work” on the Python side, so there is not a need for functions like that typically. I would say it’s worth seeing if there are other existing tools for interpolation that we could leverage. As long as we are taking a runtime-optional dependency on skimage we can take other runtime-optional dependencies at the same time.

Alternatively we can also just punt on arbitrary mesh support right now, and just implement contour_uniform_grid for the time being. It would be a nice-to-have but honestly I expect most users will have regular grids in NumPy arrays (an assumption on my part).

FYI The old C code that MPL uses (which actually pre-dates MPL by many years… it dates back to at least the Yorick and GIST days) can be found here. I mention is mostly only because it opens with a thorough discussion in the header comments about various corner cases and difficulties that a contour algorithm needs to handle.

I would say it’s worth seeing if there are other existing tools for interpolation that we could leverage. As long as we are taking a runtime-optional dependency on skimage we can take other runtime-optional dependencies at the same time.

I know that there is an interpolation routine in scipy, but that is another large dependency to add. I can look around for a smaller one.

I could probably whip up an interpolation routine (a simple cubic that falls back on a linear would probably be fine) if we need it. But I’ll put that off till later.

Alternatively we can also just punt on arbitrary mesh support right now, and just implement contour_uniform_grid for the time being. It would be a nice-to-have but honestly I expect most users will have regular grids in NumPy arrays (an assumption on my part).

For the moment, I’ll probably do this… especially considering log axes can be done on the figure level… and that is going to be one of the more common use-cases of non-uniform grids.

FYI The old C code that MPL[…]

Thanks! I’ll have to take a look.

Just so I make my own priorities evident: I definitely prioritize any solution that adds the smallest amount of new code to Bokeh. Definitley want to minimize our testing and maintenance surface as much as possible. From this perspective, I am perfectly happy to have scipy as a runtime-optional dependency that is required only for contouring. (For that matter, I am a little surprised skimage does not bring in scipy on its own)

skimage does not bring in scipy on its own

It does, I just hadn’t thought of that.