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

Support Pixel Ratio #1411

Merged
merged 17 commits into from
Sep 20, 2024
Merged

Support Pixel Ratio #1411

merged 17 commits into from
Sep 20, 2024

Conversation

philipc2
Copy link
Contributor

@philipc2 philipc2 commented Sep 10, 2024

Closes #626

Overview

  • Adds support for setting the pixel_ratio parameter

Example Plots

image

@philipc2 philipc2 changed the title DRAFT: Support Pixel Ratio Support Pixel Ratio Sep 10, 2024
@philipc2 philipc2 marked this pull request as ready for review September 10, 2024 20:55
@philipc2
Copy link
Contributor Author

@ahuang11

Thanks for pointing me in the right direction with #1368

This should be ready for review.

@ahuang11
Copy link
Collaborator

ahuang11 commented Sep 10, 2024

Thanks! Took a quick skim; could you also add a line to Customization.ipynb to document it's available?

@philipc2
Copy link
Contributor Author

Thanks! Took a quick skim; could you also add a line to Customization.ipynb to document it's available?

Sure! Will run the pre-commit too

@ahuang11
Copy link
Collaborator

I think op_options is missing a comma. Otherwise I think it looks good and I can take a deeper look/merge tomorrow!

Copy link
Collaborator

@ahuang11 ahuang11 left a comment

Choose a reason for hiding this comment

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

I applied a minor fix to Customization.ipynb; I believe it's ready!

@ahuang11
Copy link
Collaborator

Actually, the test seems to be failing:

    def test_pixel_ratio(self, da2):
        plot = da2.isel(other=0).hvplot(rasterize=True, pixel_ratio=4.0)
        opts = Store.lookup_options('bokeh', plot, 'plot')
>       assert opts.kwargs['pixel_ratio'] == 4.0
E       KeyError: 'pixel_ratio'

@ahuang11 ahuang11 requested a review from maximlt September 11, 2024 09:17
" Specifies the smallest allowed sampling interval along the x/y axis."
" Specifies the smallest allowed sampling interval along the x/y axis.\n",
" pixel_ratio (default=None):\n",
" Pixel ratio applied to the height and width. Useful for higher\n",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Pointed out by @maximlt, we should also mention this is applicable for datashader operations here and in the docstring.

Bonus if you also mention it for x_sampling/y_sampling!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done! Let me know what you think of the addition.

@philipc2
Copy link
Contributor Author

Actually, the test seems to be failing:

    def test_pixel_ratio(self, da2):
        plot = da2.isel(other=0).hvplot(rasterize=True, pixel_ratio=4.0)
        opts = Store.lookup_options('bokeh', plot, 'plot')
>       assert opts.kwargs['pixel_ratio'] == 4.0
E       KeyError: 'pixel_ratio'

Do you have any suggestions on what I could do to fix this and/or how to best test this?

@philipc2 philipc2 requested a review from ahuang11 September 11, 2024 12:46
@ahuang11
Copy link
Collaborator

ahuang11 commented Sep 11, 2024

Thanks for updating customization! Can you also add to docstring?

For the failing test, can you see how datashading is asserted? (If there's a test for that)?

@philipc2
Copy link
Contributor Author

Thanks for updating customization! Can you also add to docstring?

For the failing test, can you see how datashading is asserted? (If there's a test for that)?

Maybe @maximlt can provide some input, I'm not sure the best way to move forward with the test.

@ahuang11
Copy link
Collaborator

Let me investigate a bit too.

@ahuang11
Copy link
Collaborator

ahuang11 commented Sep 11, 2024

Okay I found it

import xarray as xr
import hvplot.xarray
import xarray as xr
import holoviews as hv

ds = xr.open_dataset("sst.oisst.mon.ltm.1991-2020.nc")["sst"].isel(time=0)
p = ds.hvplot.image(rasterize=True, pixel_ratio=0.1)
assert p.callback.inputs[0].callback.operation.p["pixel_ratio"] == 0.1

For future reference, I looked up how other datashading tests were done
image

Maybe we should migrate the test over from testoptions.py to testoperations.py too.

@philipc2
Copy link
Contributor Author

Okay I found it

import xarray as xr
import hvplot.xarray
import xarray as xr
import holoviews as hv

ds = xr.open_dataset("sst.oisst.mon.ltm.1991-2020.nc")["sst"].isel(time=0)
p = ds.hvplot.image(rasterize=True, pixel_ratio=0.1)
assert p.callback.inputs[0].callback.operation.p["pixel_ratio"] == 0.1

For future reference, I looked up how other datashading tests were done image

Maybe we should migrate the test over from testoptions.py to testoperations.py too.

Done! Moved them to testoperations.py and updated the tests.

Copy link

codecov bot commented Sep 11, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 88.91%. Comparing base (efeda78) to head (fec2098).
Report is 3 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1411      +/-   ##
==========================================
+ Coverage   88.73%   88.91%   +0.17%     
==========================================
  Files          51       51              
  Lines        7592     7651      +59     
==========================================
+ Hits         6737     6803      +66     
+ Misses        855      848       -7     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Member

@maximlt maximlt left a comment

Choose a reason for hiding this comment

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

Useful for higher resolution screens where the PlotSize stream reports 'nominal'
dimensions in pixels that do not match the physical pixels. For
instance, setting pixel_ratio=2 can give better results on Retina
displays.

My understanding is that currently Panel/HoloViews automatically pulls the right ratio based on the user display, so if that's the case this comment is misleading as e.g. users on Retina display will be led to set pixel_ratio=2 while this is already done internally.

Pasting a comment made by @philippjfr elsewhere:

That said I wonder if the pixel_ratio here should be relative to the display pixel ratio or simply override the existing pixel ratio as is done in the PR.

@philipc2
Copy link
Contributor Author

Useful for higher resolution screens where the PlotSize stream reports 'nominal'
dimensions in pixels that do not match the physical pixels. For
instance, setting pixel_ratio=2 can give better results on Retina
displays.

My understanding is that currently Panel/HoloViews automatically pulls the right ratio based on the user display, so if that's the case this comment is misleading as e.g. users on Retina display will be led to set pixel_ratio=2 while this is already done internally.

Pasting a comment made by @philippjfr elsewhere:

That said I wonder if the pixel_ratio here should be relative to the display pixel ratio or simply override the existing pixel ratio as is done in the PR.

That may be the case, and I'm happy to rephrase it (took this word-for-word from the HoloViews source code)

@maximlt maximlt added this to the 0.11.0 milestone Sep 13, 2024
@maximlt
Copy link
Member

maximlt commented Sep 13, 2024

@ahuang11 I was reviewing this PR again and read through the original issue (failed to do that previously). I'd like to make sure we're going in the right direction as exposing pixel_ratio is not exactly what was suggested in the issue previously by @jbednar and discussed with @philippjfr and others. Well, one good reason for that is that pixel_ratio was only added in HoloViews after that discussion happened holoviz/holoviews#5288 🙃

Jim suggested 3 options, Philipp being happy with options 1 and 3.

1] hvPlot calls could accept an argument rasterize_args, a dictionary of key:value pairs that would be supplied as-is when hvPlot calls rasterize, after its own arguments. Pros: Provides the full power of hv.operation.datashader.rasterize(), including all the various arguments shown above, within the hvPlot syntax. Cons: Full usage requires looking up the HoloViews documentation for rasterize, at which point maybe people should just be using that operation directly.

3] hvPlot calls could accept an argument rasterize_factor that is multiplicatively applied to the plot's native height and width to get the rasterization resolution. Pros: One new argument rather than two. Makes it simple to express "I want a lower resolution than usual" without fiddling with specific widths and heights. Cons: Only addresses this one use case.

I like option 1 too and somewhat disagree with the cons cited by Jim, it seems to be hvPlot could document the main keys that can be set via this dict argument. Pretty sure there are ways to type this dictionary so that users would get help from their IDE to populate it (well not yet in Jupyter).

Option 3 is what I think is implemented with pixel_ratio? Except that in the issue there was a short discussion on finding a better name for rasterize_factor and I doubt pixel_ratio achieves that?

I would also like us to address this question before merging:

That said I wonder if the pixel_ratio here should be relative to the display pixel ratio or simply override the existing pixel ratio as is done in the PR.

@philipc2
Copy link
Contributor Author

@maximlt

I can create a separate issue on this topic if you'd like,

As for the design, I'm open to any considerations.

@ahuang11
Copy link
Collaborator

Depends how useful pixel_ratio is; if it's really useful I vote for 3; if not 1.

Option 1 is similar to colorbar_opts; I shy away from having to call colorbar_opts because I never remember the proper kwarg values.

there are ways to type this dictionary so that users would get help from their IDE to populate it

Sounds like a dataclass / pydantic model / parameterized

@philipc2
Copy link
Contributor Author

Depends how useful pixel_ratio is; if it's really useful I vote for 3; if not 1.

Option 1 is similar to colorbar_opts; I shy away from having to call colorbar_opts because I never remember the proper kwarg values.

there are ways to type this dictionary so that users would get help from their IDE to populate it

Sounds like a dataclass / pydantic model / parameterized

I've found pixel ratio to be extremely useful when paired with dynamic=False for interactive plots.

Zoomed (Default Pixel Ratio)

image

Zoomed (pixel_ratio=8.0)

image

It's generally pretty intuitive for generating higher-resolution raster plots, while maintain the same plot size.

image

@ahuang11
Copy link
Collaborator

ahuang11 commented Sep 16, 2024

I'm in favor of having pixel_ratio exposed directly.

@maximlt
Copy link
Member

maximlt commented Sep 20, 2024

@philipc2 I took the liberty to push a commit with some changes to the docstring and re-ordering.


In the end, I decided that adding pixel_ratio wasn't making hvPlot's API so much worse:

  • Keeping the same name as used in HoloViews has some value
  • hvPlot's API is a bit of a mess anyway. For instance, you can pass max_px to configure dynspread, it's not in the signature or documented anywhere but is caught internally.

But in general @ahuang11 I think we need to be careful when adding new parameters to hvPlot, because of the flat namespace this adds a cost to all users of hvPlot.

@maximlt maximlt merged commit 266565b into holoviz:main Sep 20, 2024
9 checks passed
@maximlt
Copy link
Member

maximlt commented Sep 20, 2024

Thanks @philipc2 !

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.

Control resolution of rasterized plots
3 participants