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

Two-dimensional scatter plot #317

Merged
merged 44 commits into from
Apr 9, 2024
Merged

Two-dimensional scatter plot #317

merged 44 commits into from
Apr 9, 2024

Conversation

nvaytet
Copy link
Member

@nvaytet nvaytet commented Mar 4, 2024

This PR introduces a new 2d scatter plot.
Fixes #264

Example:

import plopp as pp

a = pp.data.scatter()
pp.scatter(a, cbar=True)

See docs notebook for more examples.

In addition, there has been a refactor of the interface to how we update figures:
Old:

fig.update(data_a, key='a')
fig.update(data_b, key='b')

New:

fig.update(a=data_a, b=data_b)

It made sense to do this change here to unify the different views (made a common GraphicalView base class).

Also fixes #315
Also fixes #323

@jokasimr jokasimr self-requested a review April 8, 2024 09:19
'hist': hist,
}
for array in (values, mask):
array['y'] = np.concatenate([array['y'][0:1], array['y']])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What changed here? Before we didn't add a point to the values, but now we do.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think what changed is that before we were using scipp.concat for y values, and numpy for the mask values, but now we switch to numpy earlier (because I had to resort to using np.asarray because in some cases some lists of arrays were supplied)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aha yeah I see what you mean

HBox(
[
self.left_bar,
VBox([self._view.canvas.to_widget()]),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand what this does, why is the widget wrapped in a VBox?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was an annoying thing where the buttons would end up all the way to the right, and for some reason, wrapping it in a VBox helps.
However, I now see that's it's still a little broken (see #323).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think i fixed it.

@@ -227,9 +227,10 @@ def _set_normalizer_limits(self):
self.normalizer.vmin = self.vmin
self.normalizer.vmax = self.vmax

def update(self, key: str, data: sc.DataArray):
def update(self, _=None, **kwargs):
Copy link
Contributor

@jokasimr jokasimr Apr 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(minor) The argument name confused me here, I always assume _ is unused, so I was surprised when it was used.
Maybe we should rename it to values?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I basically wanted to avoid a name clash from happening. Like when you update a dict in python.
You can do

d.update(a=1, b=3, values=5)
d.update({'a': 1, 'b': 3, 'values': 5})

If my function signature is

def update(self, values=None, **kwargs):

then the first case will fail because it is expecting values to be a dict, not just a single value.

I don't actually know how python does it, because you can also use _ as a key:

d = {'a': 1, 'b': 3}
d.update(_=55)
print(d)
d.update({'a': 33, 'c': 6})
print(d)
{'a': 1, 'b': 3, '_': 55}
{'a': 33, 'b': 3, '_': 55, 'c': 6}

Maybe I should look at how they implemented it...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I think a better way of implementing this is

def update(self, *args, **kwargs):
    new = kwargs
    if args:
        if len(args) > 1:
            raise TypeError('Update expected at most 1 arg.  Got {}.'.format(len(args)))
        else:
            new.update(args[0])
    ...

Copy link
Contributor

@jokasimr jokasimr Apr 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that makes sense. We can avoid clashes and make the name more clear by using something like: _dict_arg. That's unlikely to cause a clash and it's also a bit more clear. But I'll leave it to you to decide.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even with *args there is no clash, you can still do view.update(args=1, a=2) and the keys will be args and a.



class GraphicalView(View):
""" """
Copy link
Contributor

@jokasimr jokasimr Apr 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this needs a short docstring explaining what makes it different from View.

The id of the node that sent the new data.
"""
if new_values.ndim != 1:
raise ValueError("LineView can only be used to plot 1-D data.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I missed something but it seems we loose this error message here. Is there another check elsewhere using the new _ndim attribute?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, at the start of the update function in the GraphicalView ;-)

Copy link
Contributor

@jokasimr jokasimr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Tested the scatter plot and played around with it a bit and it seems to work well. The changes to the update method and the to the structure seem to make a lot of sense.

The superplot was not interactive for me, do I need another package to make it work?

@nvaytet
Copy link
Member Author

nvaytet commented Apr 8, 2024

The superplot was not interactive for me, do I need another package to make it work?

Did you use the notebook from the docs? Did you have %matplotlib widget enabled?

@jokasimr
Copy link
Contributor

jokasimr commented Apr 8, 2024

The superplot was not interactive for me, do I need another package to make it work?

Did you use the notebook from the docs? Did you have %matplotlib widget enabled?

Yes I did. But when running it the first time it told me to install ipympl, so I had to do that manually before running, then it ran but was not interactive. Maybe that's related to the issue.

@nvaytet
Copy link
Member Author

nvaytet commented Apr 8, 2024

I think you need to skip the weird cell with

from ipywidgets import HBox

b = p.children[0].children[1].children[0]
sl = p.children[1].controls['y']['slider']
b.click()
sl.value = 20
b.click()
sl.value = 30
f = p.children[0].children[0]
f.children = [
    f.top_bar,
    HBox([f.left_bar, f.canvas.to_image(), f.right_bar]),
    f.bottom_bar,
]

which is hidden in the docs.

@jokasimr
Copy link
Contributor

jokasimr commented Apr 8, 2024

I think you need to skip the weird cell with

Yes that did the trick. How do you make a cell hidden in the docs?

@nvaytet
Copy link
Member Author

nvaytet commented Apr 8, 2024

Yes that did the trick. How do you make a cell hidden in the docs?

https://nbsphinx.readthedocs.io/en/0.9.3/hidden-cells.html

@nvaytet nvaytet merged commit ea5c760 into main Apr 9, 2024
3 checks passed
@nvaytet nvaytet deleted the scatter2d branch April 9, 2024 07:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants