plotly.py icon indicating copy to clipboard operation
plotly.py copied to clipboard

Make marker size absolute in scatterplot

Open Ben-Epstein opened this issue 4 years ago • 25 comments

It seems that when creating a scatterplot, the marker sizes are always in pixels. The result is that when zooming into the plot, the points remain small. Is there a way to have them dynamically resize? Or set the size to be absolute instead of pixel?

A code snippet to showcase the point

import pandas as pd
import numpy as np
import plotly.express as px


some_data = pd.DataFrame({"id":list(range(50_000)), "x":np.random.normal(size=50_000), "y":np.random.normal(size=50_000)})
some_data["c"] = pd.Categorical(np.random.randint(low=0,high=2,size=50_000))


fig = px.scatter(some_data, x="x", y="y", color="c")

fig.update_traces(marker=dict(size=3, opacity=0.5),
                  selector=dict(mode='markers'))

fig.update_layout({
    'plot_bgcolor': 'rgba(0, 0, 0, 0)',
    'paper_bgcolor': 'rgba(0, 0, 0, 0)',
})

fig.update_yaxes(matches=None, showticklabels=False, visible=False)
fig.update_xaxes(matches=None, showticklabels=False, visible=False)


config = dict({'scrollZoom': True})

fig.show(config=config)

Zoomed out, this is a nice graph that shows the distribution clearly, but when zooming in, the points are so small it's nearly impossible to interact with any individual one.

https://user-images.githubusercontent.com/22605641/147709359-34eb5036-d96e-4feb-a32f-f9076a7f3b4d.mov

Is there a way for those points to have absolute sizes instead of fixed ones? Such that zooming in would cause them to increase (as would be intuitive naturally)?

Thanks!

Ben-Epstein avatar Dec 29 '21 23:12 Ben-Epstein

+1 would love to see this as well. Would love a way to set their absolute size without having to dig too deep into plotly internals or plotly.js callbacks.

nikita-galileo avatar Dec 29 '21 23:12 nikita-galileo

+1 I looked into this for some time a little while back with no success :(. It would be amazing to finally find a solution to this!

jonathangomesselman avatar Dec 30 '21 15:12 jonathangomesselman

+2 tried to forge a hand-made solution for this but failed

luggie avatar Mar 20 '22 17:03 luggie

+1, also looking for a solution

PaulRivaud avatar Mar 28 '22 12:03 PaulRivaud

+1 d3 does this out of the box.

Alpha-Rome0 avatar Apr 08 '22 07:04 Alpha-Rome0

  • 1 we also didn't found a solution yet

JeyDi avatar Jun 27 '22 10:06 JeyDi

kiiiiind of a 'solution' would be to draw shapes instead of scatter points for each scatter point with add_shape() here, zooming includes an increase of objects size when coming closer. However there are drawbacks because shapes are not meant to be plot objects but rather drawings/annotations. Therefore things like hover info need to be implemented with invisible scatter points etc. Feels very dodgy but for my purposes it does the job.

luggie avatar Jun 27 '22 10:06 luggie

+1 this would be very nice to have

Paximilianus avatar Nov 04 '22 09:11 Paximilianus

I currently need this feature for work :)

benocd avatar Nov 14 '22 23:11 benocd

+1 Drawing spheres do provide a solution. However, it can be too heavy when I have to draw over tens of thousands of points in the 3D plot. Waiting for an upgrade.

WhoTHU avatar Feb 27 '23 05:02 WhoTHU

+1 This would be a big help in a dataset I'm working with where the size-generating data spans orders of magnitude and where there are dense regions of smaller-size markers.

AdamColligan avatar Apr 08 '23 19:04 AdamColligan

This would be very useful!

tsitsimis avatar Jun 12 '23 20:06 tsitsimis

Came here to see a solution. Looks like they've added documentation based on @luggie's solution https://plotly.com/python/shapes/#circles-positioned-relative-to-the-axis-data

stefansjs avatar Oct 16 '23 23:10 stefansjs

Came here to see a solution. Looks like they've added documentation based on @luggie's solution https://plotly.com/python/shapes/#circles-positioned-relative-to-the-axis-data

Useful when there is little data.

duchengyao avatar Oct 19 '23 10:10 duchengyao

This would be also useful in order to be able to become independent of rescaling the figure dpi after plotting

dboeckenhoff avatar Nov 06 '23 10:11 dboeckenhoff

+1 I have been searching for a solution to this problem and am yet to find one. This should be standard behaviour.

umarbutler avatar Dec 11 '23 06:12 umarbutler

I could imagine 2 different APIs for this.

  1. To add sizex and sizey to marker. In this case the a circle marker become an ellipse when scaled in on direction.
  2. To add a constant sizemode. In this case a circle marker remains circle when scaled in one direction as it is adjusted in other direction to maintain the area.

archmoj avatar Dec 14 '23 14:12 archmoj

For those of you interested in this feature: the key next step is to design the API, and for that we need to know a bit more about the use cases you have in mind. The crux of the issue is that zooming in and out does not generally preserve aspect ratio, so if we're trying to set the marker size in reference to the axis scaling rather than pixels, the question is which axis? I guess the options could be:

  • Give two sizes, one for x and one for y. This means in general as you zoom the markers will change aspect ratio, to become ellipses, rectangles, etc, but if the marker size really is supposed to mean something related to each axis scale, that might be the right solution. (note: this solution doesn't scale to 3D, because the markers maintain their orientation as the scene is rotated).
  • Size the markers based on one of the axis scales, but maintain aspect ratio regardless of the scale of the other axis.
  • Size the markers based on the data-scale area they cover, meaning when converted to linear size, markers grow with the geometric mean of the x and y (and z, this flavor would scale to 3D) axis scales. (@archmoj I guess this is what you meant with your variant 2? I like it!) This feels to me like it may be the best if the goal is "make the markers bigger when you've zoomed in so there are only a few of them." I worry though that it'll be hard to use, as you'll need to figure out a reasonable scale on both axes and multiply these together, giving a number that might not be very meaningful to you, so you'd likely end up just trying a few things until something looks good.
  • I wonder if there's a calculation we could do as an "auto" input to the above variant - something like the std deviation along each axis of all the traces with markers, divided by the total number of markers?

And for all variants, I bet it'll also be important to constrain the max and min resulting sizes. For array-sized markers we already have a marker.sizemin attribute, we should be able to reuse that here, and perhaps add marker.sizemax as well.

alexcjohnson avatar Dec 17 '23 16:12 alexcjohnson

I can't imagine any use case where somebody wants an absolute scale, but not absolute in both axes.

What I would use is a fixed radius, which means, r = xscale = yscale. That means it would be an ellipse when zoomed in.

What's the situation when you might want a fixed scale in one axis but not the other?

That said, if you want an ellipse API and a circle API, just make two different shapes?

stefansjs avatar Dec 18 '23 00:12 stefansjs

I would also like to have this feature. If we have same units on each axis like a coordinates in meter for each axis and radius or diameter in meter for each marker we could associate this data to sizing each marker. As proposed by @archmoj I think it would be a good approach for circle data, the markers will appears as circle if axis x and y are orthonormal, as an ellipsis if not.

alferan avatar Jan 05 '24 14:01 alferan

kiiiiind of a 'solution' would be to draw shapes instead of scatter points for each scatter point with add_shape() here, zooming includes an increase of objects size when coming closer. However there are drawbacks because shapes are not meant to be plot objects but rather drawings/annotations. Therefore things like hover info need to be implemented with invisible scatter points etc. Feels very dodgy but for my purposes it does the job.

Unfortunately, this only really works for small datasets. I managed to plot >100k datapoints as circles using this stack overflow answer but there are so many shapes in the figure that it is essentially impossible to render.

umarbutler avatar Feb 24 '24 10:02 umarbutler