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

Table adds points to list #2326

Closed
jimmytudeski opened this issue Feb 9, 2018 · 13 comments
Closed

Table adds points to list #2326

jimmytudeski opened this issue Feb 9, 2018 · 13 comments
Labels
type: bug Something isn't correct or isn't working

Comments

@jimmytudeski
Copy link

jimmytudeski commented Feb 9, 2018

Hi,
I played around a bit and now I have some questions.
(Hoping this is the right place for this.) So I want to have a plot where I can place points which then will be used for polynomial interpolations. This is what I have so far:

import numpy as np 
import holoviews as hv
hv.extension('bokeh')

# helper functions
def isininterval(x, interval):
    """True if x is in interval."""
    i1, i2 = interval
    if i1 > i2:
        i1, i2 = i2, i1
    return i1<= x and x <=i2

def fit_points(points, x, degree, label='None'):
    """Returns an interpolating curve"""
    if len(points) >= 2:
        x_points, y_points = zip(*points)
        p = np.poly1d(np.polyfit(x_points, y_points, degree))
        y = p(x)
        return hv.Curve((x, y), label=label)
    else:
        return hv.Curve(([],[]), label=label)
#callback for DynamicMap
def plot(x, y, x_range, y_range, degree, reset):
    global points
    #clear point if reset
    if reset:
        del points[:]
        
    # adds point (x, y) to list points iff (x, y) is in range
    if None not in [x, y] and isininterval(1.01*x, x_range) and isininterval(1.01*y, y_range):
        points.append((x, y))
    
    points_plot = hv.Points(points)
    x_lin = np.linspace(*x_range, num=1000)
    curve1 = fit_points(points, x_lin, 1, 'linear')
    curve2 = fit_points(points, x_lin, degree, 'custom')
    curve3 = fit_points(points, x_lin, len(points)- 1, label='perfect')
    
    return points_plot * curve1 * curve2 * curve3 + hv.Table(points, kdims=['x', 'y']) # Table adds points to points?
%%opts Points Curve [height=300 width= 400] 
%%opts Points  (size=15 color='black')
%%opts Curve (color=Cycle(['blue', 'red', 'green']) alpha=0.8)

tap = hv.streams.SingleTap(transient=True)
reset = hv.streams.PlotReset(transient=True)
xy_range = hv.streams.RangeXY(x_range=(0,1), y_range=(0,1))
points=[]
dmap = hv.DynamicMap(plot, kdims=['degree'],  streams=[tap, xy_range, reset]).redim.range(degree=(1, 20))
dmap 

unbenannt

So now my questions:

  1. By tapping every point gets added twice to the list points. But If I skip " + hv.Table(...) " in
    the return of function "plot" it's like expected. (Every point gets added just once.)

  2. I tried to use the reset button to clear the points list. How can I set reset back to False (in the stream)? transient=True seems not to be correct.

  3. Is there an easy way to prevent placing points when tapping on the legend? (to switch off a curve)

  4. Is there an easy way to drag and drop, remove points?

  5. Sometimes a curve disappears when zooming out.

Sorry for the long list and if this is already answered in the docs. If so, a reference would be enough of course. So feel free to ignore stuff ; ) Improvements are welcome. ; )
Thanks for the great work you're doing.

@philippjfr
Copy link
Member

By tapping every point gets added twice to the list points. But If I skip " + hv.Table(...) " in
the return of function "plot" it's like expected. (Every point gets added just once.)

This definitely appears to be a bug.

I tried to use the reset button to clear the points list. How can I set reset back to False (in the stream)? transient=True seems not to be correct.

Another (quite stupid) bug, Streams have a reset method which is what makes transient work. Adding a reset parameter breaks that, it'll have to be renamed to "resetting".

Is there an easy way to prevent placing points when tapping on the legend? (to switch off a curve)

Not as far as I know.

Is there an easy way to drag and drop, remove points?

The new PointDraw stream will handle a bunch of the stuff you're doing here and simplify your example a lot, since it adds an explicit tool you have to activate it'll also be more convenient for your legend issue above.

Sometimes a curve disappears when zooming out.

I'd like to turn your example into a demo for the docs so I'll rework it a bit and get back to you.

@philippjfr philippjfr added the type: bug Something isn't correct or isn't working label Feb 9, 2018
@jimmytudeski
Copy link
Author

I'd like to turn your example into a demo for the docs so I'll rework it a bit and get back to you.

That would be great. Don't hurry, it's not that important for me.
Thanks again.

@jbednar
Copy link
Member

jbednar commented Feb 9, 2018

Nice example!

Is there an easy way to prevent placing points when tapping on the legend? (to switch off a curve)

Bokeh allows the legend to be placed outside of the plot, which would be another way to address this issue, but I don't know if HoloViews currently supports that.

@philippjfr
Copy link
Member

Bokeh allows the legend to be placed outside of the plot, which would be another way to address this issue, but I don't know if HoloViews currently supports that.

Sure it does, %%opts Overlay [legend_position='left'/'right'/'top'/'bottom']

@jlstevens
Copy link
Contributor

Agreed that this would be a great demo!

@jimmytudeski
Copy link
Author

Hi,
I gave it another try:

%%opts Points (size=10 color='black')
data = hv.OrderedDict({'x':[], 'y':[]})
points = hv.Points(data).redim.soft_range(x=(-3, 3))
points_s = streams.PointDraw(data=points.columns(), source=points)
x_range = streams.RangeX(source=points, x_range =(-3, 3))
reset = streams.PlotReset(transient=True)

def fit(data, degree, x_range):
    if data == None or len(data['x']) < 2:
        return hv.Curve(([], []))
    
    else:
        x, y = data['x'], data['y']
        p = np.poly1d(np.polyfit(x, y, degree))
        t = np.linspace(*x_range, 300)
        return hv.Curve((t, p(t)))
    
        
def plot(data, deg, x_range, resetting):
    polys = {'linear': 1, 'quadratic':2, 'cubic':3, 'custom':deg}
    return hv.NdOverlay({name: fit(data, degree, x_range) for name, degree in polys.items()})
    
def plot_table(data):
    return hv.Table(data, kdims=['x', 'y'])
    
dmap = hv.DynamicMap(plot,sort=True, kdims='deg', streams=[points_s, x_range, reset]).redim.range(deg=(1, 20))
((points * dmap ).options(width=700, height=500, legend_position='top') + hv.DynamicMap(plot_table, streams=[points_s])).cols(1)

bildschirmfoto zu 2018-05-17 11-03-43

But I still have some questions:

  1. Why is streams.PointDraw({'x':[], 'y':[]}).data None and not something with "empty lists"?
  2. How can I use the reset button?
  3. I tried to use shared_datasource for the table, but that did not work with the slider. Is that possible?

Some other things I noticed when playing around with the point_draw notebook point_draw notebook :

  1. In JupyterLab it seems that point_stream.data does not get more data after placing points. (In the normal notebook, it works though.)
  2. Just running:
%%opts Points (color='color' size=10)
#[tools=['hover'] ] 
data = hv.OrderedDict({'x':[1, 2, 4], 'y':[1, 2 , 4], 'color':['blue', 'red', 'green']})
points = hv.Points(data, vdims='color')
points

gives:
ERROR:bokeh.core.validation.check:E-1001 (BAD_COLUMN_NAME): Glyph refers to nonexistent column name: color [renderer: GlyphRenderer(id='9524cbe8-b102-42eb-9b21-fa3b40daf986', ...)]

which goes away if you include the [tools=['hover'] plot option.
Thanks for your work.
Cheers

@jlstevens
Copy link
Contributor

... which goes away if you include the [tools=['hover'] plot option.

Now that is odd!

@philippjfr
Copy link
Member

Why is streams.PointDraw({'x':[], 'y':[]}).data None and not something with "empty lists"?

hv.streams.PointDraw(data={'x':[], 'y':[]}).data works as expected. It looks like the first argument defines the empty_value, which is not what you want to set.

How can I use the reset button?

You already seem to have figured out that you have to use the PlotReset stream, but it seems like that is currently broken due to a bug, to fix it you could declare this:

from holoviews.plotting.bokeh.callbacks import ResetCallback

class FixedResetCallback(ResetCallback):
    def _process_msg(self, msg):
        return {'resetting': True}

hv.streams.Stream._callbacks['bokeh'][hv.streams.PlotReset] = FixedResetCallback

But we will have a fix out soon.

In JupyterLab it seems that point_stream.data does not get more data after placing points. (In the normal notebook, it works though.)

That sounds like a bug that we'll have to investigate.

Just running: %%opts Points (color='color' size=10)

HoloViews does not currently support using the color style argument to a column/dimension name (although that is planned to be supported in #2152). So the fact that this works at all is an accident, which occurs because 1) bokeh does allow referencing columns by name and 2) enabling the hover tool will ensure the 'color' column is sent along with everything else.

@philippjfr
Copy link
Member

In JupyterLab it seems that point_stream.data does not get more data after placing points. (In the normal notebook, it works though.)

I've just double checked this and cannot reproduce, works fine for me both in classic notebook and jupyterlab.

@jimmytudeski
Copy link
Author

jimmytudeski commented May 18, 2018

I've just double checked this and cannot reproduce, works fine for me both in classic notebook and jupyterlab.

%%opts Points (color='color' size=10) [tools=['hover'] width=400 height=400] 
%%opts Layout [shared_datasource=True] Table (editable=True)
data = hv.OrderedDict({'x': [0, 0.5, 1], 'y': [0, 0.5, 0], 'color': ['red', 'green', 'blue']})
points = hv.Points(data, vdims=['color']).redim.range(x=(-.1, 1.1), y=(-.1, 1.1))
point_stream = streams.PointDraw(data=points.columns(), source=points, empty_value='black')
points# + hv.Table(data, ['x', 'y'], 'color')

I looked at this again. When I'm removing the table, it actually works for me. This is funny ;)
bildschirmfoto zu 2018-05-18 17-33-18

@Gordon90s
Copy link

Gordon90s commented Jul 2, 2018

While I am not able to make your example work, it looks like it is very cool! I'm still new to HoloViews, so not sure what is going wrong (I've simply copy pasted the above code in various constellations).

Is it still planned that this example makes it into the gallery soon?

@jlstevens
Copy link
Contributor

@philippjfr It would be worth noting the bugs identified in this issue and determining which ones have been resolved and filing new issues for the ones that still need to be addressed.

@philippjfr
Copy link
Member

As far as I can tell the issues here have been addressed, if any haven't been addressed they should be discussed in a dedicated issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug Something isn't correct or isn't working
Projects
None yet
Development

No branches or pull requests

5 participants