I'm trying to plot some data in polar coordinates, but I don't want the standard ticks, labels, axes, etc. that you get with the Matplotlib polar() function. All I want is the raw plot and nothing else, as I'm handling everything with manually drawn patches and lines.
Here are the options I've considered:
1) Drawing the data with polar(), hiding the superfluous stuff (with ax.axes.get_xaxis().set_visible(False), etc.) and then drawing my own axes (with Line2D, Circle, etc.). The problem is when I call polar() and subsequently add a Circle patch, it's drawn in polar coordinates and ends up looking like an infinity symbol. Also zooming doesn't seem to work with the polar() function.
2) Skip the polar() function and somehow make my own polar plot manually using Line2D. The problem is I don't know how to make Line2D draw in polar coordinates and haven't figured out how to use a transform to do that.
Any idea how I should proceed?
Your option #2 is probably the simplest, given what you want to do. You would thus stay in rectangular coordinates, modify your function from polar to rectangular coordinates, and plot it with plot() (which is easier than using `Line2D').
The transformation of your polar function into a rectangular one can be done with:
def polar_to_rect(theta, r):
return (r*cos(theta), r*sin(theta))
and the plotting can be done with:
def my_polar(theta, r, *args, **kwargs):
"""
theta, r -- NumPy arrays with polar coordinates.
"""
rect_coords = polar_to_rect(theta, r)
pyplot.plot(rect_coords[0], rect_coords[1], *args, **kwargs)
# You can customize the plot with additional arguments, or use `Line2D` on the points in rect_coords.
To remove the ticks and the labels, try using
`matplotlib.pyplot.tick_params(axis='both', which='both', length=0, width=0, labelbottom = False, labeltop = False, labelleft = False, labelright = False)`
From http://matplotlib.sourceforge.net/api/pyplot_api.html#matplotlib.pyplot.polar
Regarding your comment about using the matplotlib transforms...I used the following method to translate a polar plot into a polygon that I could draw on my cartesian/rectangular axes.
import matplotlib.pyplot as plt
polarPlot = plt.subplot(111, polar = True)
# Create some dummy polar plot data
polarData = np.ones((360,2))
polarData[:,0] = np.arange(0, np.pi, np.pi/360) * polarData[:,0]
# Use the polar plot axes transformation into cartesian coordinates
cartesianData = polarPlot.transProjection.transform(polarData)
Related
I am working on a visualization script for a linear algebra class at the university and I am trying to show multiple vectors using the quiver function in python. I am trying to plot vectors coming from a 2x2 matrix in one quiver function, however, now that I am trying to label them I would like to access each vector individually.
import numpy as np
import matplotlib.pyplot as plt
A = np.array([[1,3], [2,2]])
# create figure
fig = plt.figure()
# creates variable containing current figure
ax = fig.gca()
baseArrow = ax.quiver(*origin, A[0,:], A[1,:], color=['r','g']', angles='xy', scale_units='xy', scale=1)
ax.quiverkey(baseArrow,.85,.85,0.8,'i-hat',labelcolor='k',labelpos='S', coordinates = 'figure')
# display grid
plt.grid()
# display figure
plt.show()
This alows me to label the first vector with the respective color (red). Now what I would like to do is label the second vector in green with a different label?
Maybe something like:
ax.quiverkey(baseArrow**[2]**,.85,.85,0.8,'i-hat',labelcolor='k',labelpos='S', coordinates = 'figure')
Is there any way to pull out each vector by itself or would it be better to plot them individually instead of as a vector? I looked at the following question but it doesn't really solve my issue. Matplotlib Quiver plot matching key label color with arrow color
My feeling is that the quiver function is better suited/intended to plot numerous vectors as you would find in a graph depicting magnetic forces, vortices (sic) or gradients (see meshgrid for example). And it's API reflects that, in that it accepts end and start coordinates separately: i.e. you need to split the components of your vectors as you have done above.
May I suggest you look into the plot or arrow functions which will give you greater control over your visualization (e.g. vector-independent labels) and will also provide greater clarity in your code, as you will be able to declare vectors (as np.arrays of course) and use them directly.
Finally note that you can obtain fig and ax in one call: fib, ax = plt.subplots().
Hope this helps!
This question already has answers here:
Is there a way to make a discontinuous axis in Matplotlib?
(7 answers)
Closed 5 years ago.
Best way to describe what I want to achieve is using my own image:
Now I have a lot of dead space in the spectra plot, especially between 5200 and 6300. My question is quite simple, how would I add in a nice little // break that looks something similar to this (image lifted from the net):
I'm using this setup for my plots:
nullfmt = pyplot.NullFormatter()
fig = pyplot.figure(figsize=(16,6))
gridspec_layout1= gridspec.GridSpec(2,1)
gridspec_layout1.update(left=0.05, right=0.97, hspace=0, wspace=0.018)
pyplot_top = fig.add_subplot(gridspec_layout1[0])
pyplot_bottom = fig.add_subplot(gridspec_layout1[1])
pyplot_top.xaxis.set_major_formatter(nullfmt)
I'm quite certain it is achievable with gridpsec but an advanced tutorial cover exactly how this is achieved would be greatly appreciated.
Apologies also if this question has been dealt with previously on stackoverflow but I have looked extensively for the correct procedure for gridSpec but found nothing as yet.
I have managed to go as far as this, pretty much there:
However, my break lines are not as steep as I would like them...how do I change them? (I have made use of the example answer below)
You could adapt the matplotlib example for a break in the x-axis directly:
"""
Broken axis example, where the x-axis will have a portion cut out.
"""
import matplotlib.pylab as plt
import numpy as np
x = np.linspace(0,10,100)
x[75:] = np.linspace(40,42.5,25)
y = np.sin(x)
f,(ax,ax2) = plt.subplots(1,2,sharey=True, facecolor='w')
# plot the same data on both axes
ax.plot(x, y)
ax2.plot(x, y)
ax.set_xlim(0,7.5)
ax2.set_xlim(40,42.5)
# hide the spines between ax and ax2
ax.spines['right'].set_visible(False)
ax2.spines['left'].set_visible(False)
ax.yaxis.tick_left()
ax.tick_params(labelright='off')
ax2.yaxis.tick_right()
# This looks pretty good, and was fairly painless, but you can get that
# cut-out diagonal lines look with just a bit more work. The important
# thing to know here is that in axes coordinates, which are always
# between 0-1, spine endpoints are at these locations (0,0), (0,1),
# (1,0), and (1,1). Thus, we just need to put the diagonals in the
# appropriate corners of each of our axes, and so long as we use the
# right transform and disable clipping.
d = .015 # how big to make the diagonal lines in axes coordinates
# arguments to pass plot, just so we don't keep repeating them
kwargs = dict(transform=ax.transAxes, color='k', clip_on=False)
ax.plot((1-d,1+d), (-d,+d), **kwargs)
ax.plot((1-d,1+d),(1-d,1+d), **kwargs)
kwargs.update(transform=ax2.transAxes) # switch to the bottom axes
ax2.plot((-d,+d), (1-d,1+d), **kwargs)
ax2.plot((-d,+d), (-d,+d), **kwargs)
# What's cool about this is that now if we vary the distance between
# ax and ax2 via f.subplots_adjust(hspace=...) or plt.subplot_tool(),
# the diagonal lines will move accordingly, and stay right at the tips
# of the spines they are 'breaking'
plt.show()
For your purposes, just plot your data twice (once on each axis, ax and ax2 and set your xlims appropriately. The "break lines" should move to match the new break because they are plotted in relative axis coordinates rather than data coordinates.
The break lines are just unclipped plot lines drawn between a pair of points. E.g. ax.plot((1-d,1+d), (-d,+d), **kwargs) plots the break line between point (1-d,-d) and (1+d,+d) on the first axis: this is the bottom righthand one. If you want to change the graident, change these values appropriately. For example, to make this one steeper, try ax.plot((1-d/2,1+d/2), (-d,+d), **kwargs)
The solution provided by xnx is a good start, but there is a remaining issue that the scales of the x-axes are different between the plots. This is not a problem if the range in the left plot and the range in the right plot are the same, but if they are unequal, subplot will still give the two plots equal width, so the x-axis scale will be different between the two plots (as is the case with xnx's example). I made a package, brokenaxes to deal with this.
I am trying to understand this code snippet:
def add_inset(ax, rect, *args, **kwargs):
box = ax.get_position()
inax_position = ax.transAxes.transform(rect[0:2])
infig_position = ax.figure.transFigure.inverted().transform(inax_position)
new_rect = list(infig_position) + [box.width * rect[2], box.height * rect[3]]
return fig.add_axes(new_rect, *args, **kwargs)
This code adds an inset to an existing figure. It looks like this:
The original code is from this notebook file.
I don't understand why two coordinates transformation are needed:
inax_position = ax.transAxes.transform(rect[0:2])
infig_position = ax.figure.transFigure.inverted().transform(inax_position)
Explanation
In the method add_inset(ax, rect), rect is a rectangle in axes coordinates. That makes sense because you often want to specify the location of the inset relavtive to the axes in which it lives.
However in order to later be able to create a new axes, the axes position needs to be known in figure coordinates, which can then be given to fig.add_axes(figurecoordinates).
So what is needed is a coordinate transform from axes coordinates to figure coordinates. This is performed here in a two-step process:
Transform from axes coords to display coords using transAxes.
Transform from display coords to figure coords using the inverse of transFigure.
This two step procedure could be further condensed in a single transform like
mytrans = ax.transAxes + ax.figure.transFigure.inverted()
infig_position = mytrans.transform(rect[0:2])
It may be of interest to read the matplotlib transformation tutorial on how transformations work.
Alternatives
The above might not be the most obvious method to place an inset. Matplotlib provides some tools itself. A convenient method is the mpl_toolkits.axes_grid1.inset_locator. Below are two ways to use its inset_axes method when creating insets in axes coordinates.
import matplotlib.pyplot as plt
import mpl_toolkits.axes_grid1.inset_locator as il
fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(4,4))
ax1.plot([1,2,3],[2.2,2,3])
# set the inset at upper left (loc=2) with width, height=0.5,0.4
axins = il.inset_axes(ax1, "50%", "40%", loc=2, borderpad=1)
axins.scatter([1,2,3],[3,2,3])
# set the inset at 0.2,0.5, with width, height=0.8,0.4
# in parent axes coordinates
axins2 = il.inset_axes(ax2, "100%", "100%", loc=3, borderpad=0,
bbox_to_anchor=(0.2,0.5,0.7,0.4),bbox_transform=ax2.transAxes,)
axins2.scatter([1,2,3],[3,2,3])
plt.show()
I am trying to create a color wheel in Python, preferably using Matplotlib. The following works OK:
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
xval = np.arange(0, 2*pi, 0.01)
yval = np.ones_like(xval)
colormap = plt.get_cmap('hsv')
norm = mpl.colors.Normalize(0.0, 2*np.pi)
ax = plt.subplot(1, 1, 1, polar=True)
ax.scatter(xval, yval, c=xval, s=300, cmap=colormap, norm=norm, linewidths=0)
ax.set_yticks([])
However, this attempt has two serious drawbacks.
First, when saving the resulting figure as a vector (figure_1.svg), the color wheel consists (as expected) of 621 different shapes, corresponding to the different (x,y) values being plotted. Although the result looks like a circle, it isn't really. I would greatly prefer to use an actual circle, defined by a few path points and Bezier curves between them, as in e.g. matplotlib.patches.Circle. This seems to me the 'proper' way of doing it, and the result would look nicer (no banding, better gradient, better anti-aliasing).
Second (relatedly), the final plotted markers (the last few before 2*pi) overlap the first few. It's very hard to see in the pixel rendering, but if you zoom in on the vector-based rendering you can clearly see the last disc overlap the first few.
I tried using different markers (. or |), but none of them go around the second issue.
Bottom line: can I draw a circle in Python/Matplotlib which is defined in the proper vector/Bezier curve way, and which has an edge color defined according to a colormap (or, failing that, an arbitrary color gradient)?
One way I have found is to produce a colormap and then project it onto a polar axis. Here is a working example - it includes a nasty hack, though (clearly commented). I'm sure there's a way to either adjust limits or (harder) write your own Transform to get around it, but I haven't quite managed that yet. I thought the bounds on the call to Normalize would do that, but apparently not.
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import cm
import matplotlib as mpl
fig = plt.figure()
display_axes = fig.add_axes([0.1,0.1,0.8,0.8], projection='polar')
display_axes._direction = 2*np.pi ## This is a nasty hack - using the hidden field to
## multiply the values such that 1 become 2*pi
## this field is supposed to take values 1 or -1 only!!
norm = mpl.colors.Normalize(0.0, 2*np.pi)
# Plot the colorbar onto the polar axis
# note - use orientation horizontal so that the gradient goes around
# the wheel rather than centre out
quant_steps = 2056
cb = mpl.colorbar.ColorbarBase(display_axes, cmap=cm.get_cmap('hsv',quant_steps),
norm=norm,
orientation='horizontal')
# aesthetics - get rid of border and axis labels
cb.outline.set_visible(False)
display_axes.set_axis_off()
plt.show() # Replace with plt.savefig if you want to save a file
This produces
If you want a ring rather than a wheel, use this before plt.show() or plt.savefig
display_axes.set_rlim([-1,1])
This gives
As per #EelkeSpaak in comments - if you save the graphic as an SVG as per the OP, here is a tip for working with the resulting graphic: The little elements of the resulting SVG image are touching and non-overlapping. This leads to faint grey lines in some renderers (Inkscape, Adobe Reader, probably not in print). A simple solution to this is to apply a small (e.g. 120%) scaling to each of the individual gradient elements, using e.g. Inkscape or Illustrator. Note you'll have to apply the transform to each element separately (the mentioned software provides functionality to do this automatically), rather than to the whole drawing, otherwise it has no effect.
I just needed to make a color wheel and decided to update rsnape's solution to be compatible with matplotlib 2.1. Rather than place a colorbar object on an axis, you can instead plot a polar colored mesh on a polar plot.
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import cm
import matplotlib as mpl
# If displaying in a Jupyter notebook:
# %matplotlib inline
# Generate a figure with a polar projection
fg = plt.figure(figsize=(8,8))
ax = fg.add_axes([0.1,0.1,0.8,0.8], projection='polar')
# Define colormap normalization for 0 to 2*pi
norm = mpl.colors.Normalize(0, 2*np.pi)
# Plot a color mesh on the polar plot
# with the color set by the angle
n = 200 #the number of secants for the mesh
t = np.linspace(0,2*np.pi,n) #theta values
r = np.linspace(.6,1,2) #radius values change 0.6 to 0 for full circle
rg, tg = np.meshgrid(r,t) #create a r,theta meshgrid
c = tg #define color values as theta value
im = ax.pcolormesh(t, r, c.T,norm=norm) #plot the colormesh on axis with colormap
ax.set_yticklabels([]) #turn of radial tick labels (yticks)
ax.tick_params(pad=15,labelsize=24) #cosmetic changes to tick labels
ax.spines['polar'].set_visible(False) #turn off the axis spine.
It gives this:
I would like to plot a circle on an auto-scaled pyplot-generated graphic. When I run
ax.get_aspect()
hoping for a value with which I could manipulate the axes of a ellipse, pyplot returns:
auto
which is less than useful. What methods would you suggest for plotting a circle on a pyplot plot with unequal axes?
This question is more than one year old, but I too just had this question. I needed to add circles to a matplotlib plot and I wanted to be able to specify the circle's location in the plot using data coordinates, and I didn't want the circle radius to change with panning/zooming (or worse the circle turning into an ellipse).
The best and most simple solution that I've found is simply plot a curve with a single point and include a circle marker:
ax.plot(center_x,center_y,'bo',fillstyle='none',markersize=5)
which gives a nice, fixed-size blue circle with no fill!
It really does depend what you want it for.
The problem with defining a circle in data coordinates when aspect ratio is auto, is that you will be able to resize the figure (or its window), and the data scales will stretch nicely. Unfortunately, this would also mean that your circle is no longer a circle, but an ellipse.
There are several ways of addressing this. Firstly, and most simply, you could fix your aspect ratio and then put a circle on the plot in data coordinates:
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = plt.axes()
ax.set_aspect(1)
theta = np.linspace(-np.pi, np.pi, 200)
plt.plot(np.sin(theta), np.cos(theta))
plt.show()
With this, you will be able to zoom and pan around as per usual, but the shape will always be a circle.
If you just want to put a circle on a figure, independent of the data coordinates, such that panning and zooming of an axes did not effect the position and zoom on the circle, then you could do something like:
import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = plt.axes()
patch = mpatches.Circle((325, 245), 180, alpha=0.5, transform=None)
fig.artists.append(patch)
plt.show()
This is fairly advanced mpl, but even so, I think it is fairly readable.
HTH,
Building on #user3208430, if you want the circle to always appear at the same place in the axes (regardless of data ranges), you can position it using axes coordinates via transform:
ax.plot(.94, .94, 'ro', fillstyle='full', markersize=5, transform=ax.transAxes)
Where x and y are between [0 and 1]. This example places the marker in the upper right-hand corner of the axes.