Understanding matplotlib verts

我只是一个虾纸丫 提交于 2019-11-27 07:12:55

问题


I'm trying to create custom markers in matplotlib for a scatter plot, where the markers are rectangles with fix height and varying width. The width of each marker is a function of the y-value. I tried it like this using this code as a template and assuming that if verts is given a list of N 2-D tuples it plots rectangles with the width of the corresponing first value and the height of the second (maybe this is already wrong, but then how else do I accomplish that?).

I have a list of x and y values, each containing angles in degrees. Then, I compute the width and height of each marker by

field_size = 2.
symb_vec_x = [(field_size / np.cos(i * np.pi / 180.)) for i in y]
symb_vec_y = [field_size for i in range(len(y))]

and build the verts list and plot everything with

symb_vec = list(zip(symb_vec_x, symb_vec_y))
fig = plt.figure(1, figsize=(14.40, 9.00))
ax = fig.add_subplot(1,1,1)
sc = ax.scatter(ra_i, dec_i, marker='None', verts=symb_vec)

But the resulting plot is empty, no error message however. Can anyone tell me what I did wrong with defining the verts and how to do it right? Thanks!


回答1:


As mentioned 'marker='None' need to be removed then the appropriate way to specify a rectangle with verts is something like

verts = list(zip([-10.,10.,10.,-10],[-5.,-5.,5.,5]))
ax.scatter([0.5,1.0],[1.0,2.0], marker=(verts,0))

The vertices are defined as ([x1,x2,x3,x4],[y1,y2,y3,y4]) so attention must be paid to which get minus signs etc.

This (verts,0) is mentioned in the docs as

For backward compatibility, the form (verts, 0) is also accepted, but it is equivalent to just verts for giving a raw set of vertices that define the shape.

However I find using just verts does not give the correct shape.

To automate the process you need to do something like

v_val=1.0
h_val=2.0
verts = list(zip([-h_val,h_val,h_val,-h_val],[-v_val,-v_val,v_val,v_val]))

Basic example:

import pylab as py
ax = py.subplot(111)
v_val=1.0
h_val=2.0
verts = list(zip([-h_val,h_val,h_val,-h_val],[-v_val,-v_val,v_val,v_val]))
ax.scatter([0.5,1.0],[1.0,2.0], marker=(verts,0))

*

edit

Individual markers

So you need to manually create a vert for each case. This will obviously depend on how you want your rectangles to change point to point. Here is an example

import pylab as py
ax = py.subplot(111)


def verts_function(x,y,r):
    # Define the vertex's multiplying the x value by a ratio
    x = x*r
    y = y
    return [(-x,-y),(x,-y),(x,y),(-x,y)]

n=5
for i in range(1,4):
    ax.scatter(i,i, marker=(verts_function(i,i,0.3),0))
    py.show()

so in my simple case I plot the points i,i and draw rectangles around them. The way the vert markers are specified is non intuitive. In the documentation it's described as follows:

verts: A list of (x, y) pairs used for Path vertices. The center of the marker is located at (0,0) and the size is normalized, such that the created path is encapsulated inside the unit cell.

Hence, the following are equivalent:

vert = [(-300.0, -1000), (300.0, -1000), (300.0, 1000), (-300.0, 1000)]
vert = [(-0.3, -1), (0.3, -1), (0.3, 1), (-0.3, 1)]

e.g they will produce the same marker. As such I have used a ratio, this is where you need to do put in the work. The value of r (the ratio) will change which axis remains constant.

This is all getting very complicated, I'm sure there must be a better way to do this.




回答2:


I got the solution from Ryan of the matplotlib users mailing list. It's quite elegant, so I will share his example here:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
from matplotlib.collections import PatchCollection

n = 100

# Get your xy data points, which are the centers of the rectangles.
xy = np.random.rand(n,2)

# Set a fixed height
height = 0.02
# The variable widths of the rectangles
widths = np.random.rand(n)*0.1

# Get a color map and make some colors
cmap = plt.cm.hsv
colors = np.random.rand(n)*10.
# Make a normalized array of colors
colors_norm = colors/colors.max()
# Here's where you have to make a ScalarMappable with the colormap
mappable = plt.cm.ScalarMappable(cmap=cmap)
# Give it your non-normalized color data
mappable.set_array(colors)

rects = []
for p, w in zip(xy, widths):
    xpos = p[0] - w/2 # The x position will be half the width from the center
    ypos = p[1] - height/2 # same for the y position, but with height
    rect = Rectangle( (xpos, ypos), w, height ) # Create a rectangle
    rects.append(rect) # Add the rectangle patch to our list

# Create a collection from the rectangles
col = PatchCollection(rects)
# set the alpha for all rectangles
col.set_alpha(0.3)
# Set the colors using the colormap
col.set_facecolor( cmap(colors_norm) )
# No lines
col.set_linewidth( 0 )
#col.set_edgecolor( 'none' )

# Make a figure and add the collection to the axis.
fig = plt.figure()
ax = fig.add_subplot(111)
ax.add_collection(col)
# Add your ScalarMappable to a figure colorbar
fig.colorbar(mappable)
plt.show()

Thank you, Ryan, and everyone who contributed their ideas!



来源:https://stackoverflow.com/questions/16189928/understanding-matplotlib-verts

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!