4

I would like to specify my scatter dot size not using points or pixels but rather using plot units. Take for instance the following:

import matplotlib.pyplot as plt
x = [0]
y = [0]
plt.scatter(x,y)
plt.show()

This produces a plot as follows:

enter image description here

What if, say, I wanted my marker to have a radius of exactly 0.002 plot units? I understand the s parameter indicates the area of the marker size in points. Is there a way to directly provide it in plot units? If not what is the best way to convert from plot units to points so I can pass it into the s parameter?

I have also seen some other questions which propose using a PatchCollection and simply plotting a circle with a specified radius. I would prefer not to use this approach. I'd like to avoid extensively rewriting existing code and instead use the s parameter along with plt.scatter.

Edit

I tried using the code from this question which apparently allows you to control the marker size based on plot units. However I couldn't really get it to work for my basic case:

import matplotlib.pyplot as plt
import numpy as np


class scatter():
    def __init__(self,x,y,ax,size=1,**kwargs):
        self.n = len(x)
        self.ax = ax
        self.ax.figure.canvas.draw()
        self.size_data=size
        self.size = size
        self.sc = ax.scatter(x,y,s=self.size,**kwargs)
        self._resize()
        self.cid = ax.figure.canvas.mpl_connect('draw_event', self._resize)

    def _resize(self,event=None):
        ppd=72./self.ax.figure.dpi
        trans = self.ax.transData.transform
        s =  ((trans((1,self.size_data))-trans((0,0)))*ppd)[1]
        if s != self.size:
            self.sc.set_sizes(s**2*np.ones(self.n))
            self.size = s
            self._redraw_later()

    def _redraw_later(self):
        self.timer = self.ax.figure.canvas.new_timer(interval=10)
        self.timer.single_shot = True
        self.timer.add_callback(lambda : self.ax.figure.canvas.draw_idle())
        self.timer.start()


x = [0]
y = [0]
fig = plt.figure()
ax = fig.gca()
size = 3.14*0.002**2
scatter(x, y, ax, size = 0.002, edgecolors = 'black')
ax.set_ylim(-0.004, 0.004)
ax.set_xlim(-0.004, 0.004)
ax.scatter(x,y)
ax.grid()
plt.show()

The above outputs the following figure:

enter image description here

Which does not look like it has a size of 0.002 as I indicated in scatter(x, y, ax, size = 0.002, edgecolors = 'black'). I'm not sure what I'm doing wrong here.

halfer
  • 19,824
  • 17
  • 99
  • 186
user32882
  • 5,094
  • 5
  • 43
  • 82
  • This should help, https://stackoverflow.com/questions/14827650/pyplot-scatter-plot-marker-size – Sahil Aug 18 '19 at 09:19
  • 3
    There's nothing in there about how it is related to actual *plot* units... One of the answers talks about points per inch and dots per inch. But what is an "inch"? Is it a physical inch? is it 1 map unit? I'm most interested in defining my scatter points in map units... – user32882 Aug 18 '19 at 09:26
  • 2
    geopandas will make a non-linear transform, so theoretically your circles will be warped and no longer circles. In your example above, the transform is only being applied in y, and it indeed gives you a circle that is 0.002 tall. But the x-direction is not 0.002 wide because your axes is wider than it is high. Scatter will not make an oval, so you have to choose one direction that will be correct, and one that will be wrong. – Jody Klymak Aug 18 '19 at 16:23
  • 3
    i dont know what youre talking about. The plot I'm making above is not using geopandas. – user32882 Aug 18 '19 at 18:32
  • I was asked to look at this question, but really, @JodyKlymak 's comment above is all correct. The circle is correct in y data units. Of course you could make it such that the x data units are taken instead. Otherwise I'm not sure I understand the desired outcome. – ImportanceOfBeingErnest Aug 18 '19 at 19:46
  • You could set the aspect ratio to equal and then it should work fine in both directions. But again this won’t work so well with geopandas (ahem which you explicitly mention as your end use below) – Jody Klymak Aug 18 '19 at 20:15

0 Answers0