Get Matplotlib axis spine position in relative screen coordinates? - python

Consider this example:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Button
freqs = np.arange(2, 20, 3)
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.2)
t = np.arange(0.0, 1.0, 0.001)
s = np.sin(2*np.pi*freqs[0]*t)
l, = plt.plot(t, s, lw=2)
left, bottom, width, height = 0.2, 0.91, 0.09, 0.05
btn = Button(plt.axes([left, bottom, width, height]), "Hello!")
plt.show()
This produces:
How can I get the actual position of the left spine of the axis in screen coordinates (relative, from 0.0 to 1.0) - so that I could set left to it, and the button would align with the red line indicated on the screenshot?

You could use the InsetPosition helper class provided by mpl_toolkits.axes_grid1:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Button
from mpl_toolkits.axes_grid1.inset_locator import InsetPosition
freqs = np.arange(2, 20, 3)
fig, ax = plt.subplots(figsize=(10,8))
plt.subplots_adjust(bottom=0.2)
t = np.arange(0.0, 1.0, 0.001)
s = np.sin(2*np.pi*freqs[0]*t)
l, = ax.plot(t, s, lw=2)
left, bottom, width, height = 0, 1.05, 0.09, 0.05
button_ax = plt.axes([0, 0, 1, 1])
ip = InsetPosition(ax, [left, bottom, width, height])
button_ax.set_axes_locator(ip)
btn = Button(button_ax, "Hello!")
plt.show()
Sample output:
As a side effect, the button size will change when resizing the window. Whether this is a wanted or unwanted effect is up to your judgment.

I played around with the spline properties and I think I found what you need. If you print(ax.spines['left']get_spine_transform()) you get
BlendedGenericTransform(
BboxTransformTo(
TransformedBbox(
Bbox(x0=0.125, y0=0.20000000000000007, x1=0.9, y1=0.88),
BboxTransformTo(
TransformedBbox(
Bbox(x0=0.0, y0=0.0, x1=6.4, y1=4.8),
Affine2D().scale(100.0))))),
CompositeGenericTransform(
TransformWrapper(
BlendedAffine2D(
IdentityTransform(),
IdentityTransform())),
CompositeGenericTransform(
BboxTransformFrom(
TransformedBbox(
Bbox(x0=-0.04995, y0=-1.1, x1=1.04895, y1=1.1),
TransformWrapper(
BlendedAffine2D(
IdentityTransform(),
IdentityTransform())))),
BboxTransformTo(
TransformedBbox(
Bbox(x0=0.125, y0=0.20000000000000007, x1=0.9, y1=0.88),
BboxTransformTo(
TransformedBbox(
Bbox(x0=0.0, y0=0.0, x1=6.4, y1=4.8),
Affine2D().scale(100.0))))))))
It looks like 0.125 is the value you are looking for. I am not sure how the trnaform structure works with matplotlib,so I don't know how to actually get that value contians in
BlendedGenericTransform(
BboxTransformTo(
TransformedBbox(
Bbox(
So this answer isn't quite complete, but hopefully a helpful starting point.

Related

How to change background color of inset figure

I'm trying to create an inset figure that has a different projection from the parent. The only issue I have at this point is the inset figures's tick labels are not legible because they are black and blend in with the plot behind it. I could change the color of the ticks and labels to white, but that does not help when the data in ax0 yields lighter colors. Here is the MWE:
import calipsoFunctions as cf
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import numpy as np
import pylab as pl
from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter
from mpl_toolkits.axes_grid1.inset_locator import inset_axes, mark_inset, InsetPosition
x, y = np.arange(100), np.arange(200)
X, Y = np.meshgrid(x, y)
C = np.random.randint(0, 100, (200, 100))
fig = pl.figure(figsize=(6.5, 5.25))
gs0 = pl.GridSpec(3, 1)
gs0.update(left=0.08, right=0.925,
top=0.95, bottom=0.33,
hspace=0.10, wspace=0.0)
gs1 = pl.GridSpec(1, 2)
gs1.update(left=0.08, right=0.925,
top=0.225, bottom=0.05,
hspace=0.0, wspace=0.025)
# create primary axes
ax0 = pl.subplot(gs0[0])
ax1 = pl.subplot(gs0[1])
ax0.pcolormesh(X, Y, C, vmin=0, vmax=75)
ax1.pcolormesh(X, Y, C, vmin=0, vmax=75)
# add map plot (inset axis)
loc_box = [0.8, 0.55, 0.20, 0.45]
ax0_inset = fig.add_axes(loc_box,
projection=ccrs.PlateCarree(),
aspect="auto",
facecolor="w",
frameon=True)
lat_array = np.arange(-20, 20)
lon_array = np.arange(-10, 10, 0.5)
ax0_inset.plot(lat_array, lon_array, "k-", lw=1)
ip = InsetPosition(ax0, loc_box)
ax0_inset.set_axes_locator(ip)
ax0_inset.coastlines(resolution="10m", linewidth=0.25, color="k")
ax0_inset.add_feature(cfeature.LAND)
llat, ulat = lat_array.min(), lat_array.max()
llon, ulon = lon_array.min(), lon_array.max()
llat = np.round(llat / 10) * 10
ulat = np.round(ulat / 10) * 10
llon = np.round(llon / 5) * 5
ulon = np.round(ulon / 5) * 5
ax0_inset.set_yticks(np.arange(llat, ulat, 20), minor=False)
ax0_inset.set_yticks(np.arange(llat, ulat, 10), minor=True)
ax0_inset.set_yticklabels(np.arange(llat, ulat, 20),
fontsize=8)
ax0_inset.yaxis.set_major_formatter(LatitudeFormatter())
ax0_inset.set_xticks(np.arange(llon, ulon, 5), minor=False)
ax0_inset.set_xticks(np.arange(llon, ulon, 1), minor=True)
ax0_inset.set_xticklabels(np.arange(llon, ulon, 5),
fontsize=8,
rotation=45)
ax0_inset.xaxis.set_major_formatter(LongitudeFormatter())
ax0_inset.grid()
ax0_inset.tick_params(which="both",
axis="both",
direction="in",
labelsize=8)
fig.show()
Is there a way to change the background color of ax0_inset so that these tick labels are legible? I tried changing the face_color to "w", but that did not work. Ideally, I want the same behavior as ax0.figure.set_facecolor("w"), but for the ax0_inset axis. Is this doable?
Following #Mr. T's comment suggestion, a work-around solution could be:
# insert transparent (or opaque) rectangle around inset_axes plot
# to make axes labels more visible
# make buffer variable to control amount of buffer around inset_axes
buffer = 0.1 # fractional axes coordinates
# use ax inset tuple coords in loc_box to add rectangle patch
# [left, bottom, width, height] (fractional axes coordinates)
fig.add_patch(plt.Rectangle((
loc_box[0]-buffer, loc_box[1]-buffer),
loc_box[2]+buffer, loc_box[3]+buffer,
linestyle="-", edgecolor="k", facecolor="w",
linewidth=1, alpha=0.75, zorder=5,
transform=ax0.transAxes))

Put a slider right under a subplot in matplotlib

I'm trying to put a slider right under the x-axis of a subplot in matplotlib, so that both start and end at the same value. Is there an easy way to do that, meaning that I don't have to find the right coordinates and put them myself when I create the plt.axe containing the slider?
You could use ax.get_position() to get x0, y0, width and height of the axis and use this to define the positions for the axes of the slider.
I adapted the matplotlib example to show a use case:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.3)
t = np.arange(0.0, 1.0, 0.001)
a0 = 5
f0 = 3
delta_f = 5.0
s = a0 * np.sin(2 * np.pi * f0 * t)
l, = plt.plot(t, s, lw=2)
ax.margins(x=0)
axcolor = 'lightgoldenrodyellow'
def xaligned_axes(ax, y_distance, width, **kwargs):
return plt.axes([ax.get_position().x0,
ax.get_position().y0-y_distance,
ax.get_position().width, width],
**kwargs)
axfreq = xaligned_axes(ax=ax, y_distance=0.1, width=0.03, facecolor=axcolor)
axamp = xaligned_axes(ax=ax, y_distance=0.15, width=0.03, facecolor=axcolor)
sfreq = Slider(axfreq, 'Freq', 0.1, 30.0, valinit=f0, valstep=delta_f)
samp = Slider(axamp, 'Amp', 0.1, 10.0, valinit=a0)
def update(val):
amp = samp.val
freq = sfreq.val
l.set_ydata(amp*np.sin(2*np.pi*freq*t))
fig.canvas.draw_idle()
sfreq.on_changed(update)
samp.on_changed(update)
But as you have to use plt.subplots_adjust(bottom=0.3) to have enough space below the plot and you need to define the width and the distance to the axis in y direction I guess you do not win that much.

Polar histogram - no errors but makes no sense

I don't get any erros but the histogram doesn't make sense. Bars aren't centered at all. Here it is:
]1
Some time ago I was doing something similar but the histogram was almost corect. It has gotten worse since. Here it is:
]2
I've tried playing around with ''width'',''bottom'' andd ''align'' but that didn't solve anything.
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
Theta = np.linspace(0.0, 2.0*np.pi, 12)
Dim = [2.31330, 2.32173, 2.32841, 2.32068, 2.31452, 2.30792, 2.31313, 2.31847, 2.31910, 2.31516, 2.30617, 2.30408]
width = (2*np.pi) / 13
ax1 = plt.subplot(111, polar=True)
bars = ax1.bar(Theta, Dim, color='b', width=width, bottom = 0.0, edgecolor = 'k', align = 'edge')
ax1.set_ylim(2.31000, 2.33000)
ax1.set_yticks(np.linspace(2.31000, 2.33000, 5))
ax1.set_rlabel_position(180)
ax1.set_theta_zero_location("N")
ax1.set_theta_direction(-1)
plt.show()
Basically I just need the bars to meet at 0.0 and look like bars, not this. Whatever this is.
The graph does not start at 0, but the bars will; so they are cut off. You may let the bars start at the middle of the graph by setting the bar's bottom argument.
import matplotlib.pyplot as plt
import numpy as np
Theta = np.linspace(0.0, 2.0*np.pi, 12)
Dim = np.array([2.31330, 2.32173, 2.32841, 2.32068, 2.31452, 2.30792, 2.31313,
2.31847, 2.31910, 2.31516, 2.30617, 2.30408])
width = (2*np.pi) / 13
origin = 2.31
ax1 = plt.subplot(111, polar=True)
bars = ax1.bar(Theta, Dim-origin, color='b', width=width, bottom = origin,
edgecolor = 'k', align = 'edge')
ax1.set_ylim(origin, 2.33000)
ax1.set_yticks(np.linspace(2.31000, 2.33000, 5))
ax1.set_rlabel_position(180)
ax1.set_theta_zero_location("N")
ax1.set_theta_direction(-1)
plt.show()

How to create a double headed arrow in matplotlib (with text) [duplicate]

I would like to make a simple arrow and a two head arrow. I used the following to make a simple arrow, but I doubt this is the easiest method :
import matplotlib.pyplot as plt
arr_width = .009 # I don't know what unit it is here.
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax1.plot(range(10))
ax1.arrow(1, 1, 0, .5, width = arr_width, head_width = 3 * arr_width,
head_length = 9 * arr_width)
plt.show()
I can't find how to make two head arrows with this method.
You can create a double-headed arrow using the annotate method with blank text annotation and setting the arrowprops dict to include arrowstyle='<->' as shown below:
import matplotlib.pyplot as plt
plt.annotate(s='', xy=(1,1), xytext=(0,0), arrowprops=dict(arrowstyle='<->'))
plt.show()
You can create double-headed arrows by plotting two plt.arrow which are overlapping. The code below helps to do that.
import matplotlib.pyplot as plt
plt.figure(figsize=(12,6))
# red arrow
plt.arrow(0.15, 0.5, 0.75, 0, head_width=0.05, head_length=0.03, linewidth=4, color='r', length_includes_head=True)
# green arrow
plt.arrow(0.85, 0.5, -0.70, 0, head_width=0.05, head_length=0.03, linewidth=4, color='g', length_includes_head=True)
plt.show()
And the result is like this:
You can see that the red arrow is being plotted firstly, and then the green one. When you supply the correct coordinates, it looks like a double-headed.
You can use matplotlib.patches.FancyArrowPatch to draw a two-headed arrow. This class allows to specify arrowstyle:
import matplotlib.patches as patches
p1 = patches.FancyArrowPatch((0, 0), (1, 1), arrowstyle='<->', mutation_scale=20)
p2 = patches.FancyArrowPatch((1, 0), (0, 1), arrowstyle='<|-|>', mutation_scale=20)
This produces the following arrows:
You can draw two one-headed arrows along the same line but in opposite directions.
import matplotlib.pyplot as plt
# Arrows
plt.arrow(0.3, 0.1, 0.4, 0.7, color='red', head_length = 0.07, head_width = 0.025, length_includes_head = True)
plt.arrow(0.7, 0.8, -0.4, -0.7, color='red', head_length = 0.07, head_width = 0.025, length_includes_head = True)
plt.show()

Arrows in Polar Matplotlib Plot

I am trying to plot the phasors of the voltage across the resistor, capacitor, and inductor in an series R-L-C circuit. I have done all of the calculations and I can get a decent plot with just the normal ax.plot(theta,r,....).
I would like to make the phasor vectors look like arrows. I have been trying to use ax.arrow(0,0,theta,magnitude) but it looks like a line still. The gist to the code that I have written is here : GIST
My image that I create is
I tried to follow the example that I found on this list because it is very similar to what I want to accomplish, it produces the following image:
When I run their code on my computer I get
I am on Xubuntu 14.04 and running matplotlib 1.3.1. I do see that the example I am using was using matplotlib 0.99 in 2009.
Any help would be much appreciated.
Arrow sizes were too big, this:
import matplotlib
import numpy as np
import matplotlib.pyplot as plt
print "matplotlib.__version__ = ", matplotlib.__version__
print "matplotlib.get_backend() = ", matplotlib.get_backend()
# radar green, solid grid lines
plt.rc('grid', color='#316931', linewidth=1, linestyle='-')
plt.rc('xtick', labelsize=15)
plt.rc('ytick', labelsize=15)
# force square figure and square axes looks better for polar, IMO
width, height = matplotlib.rcParams['figure.figsize']
size = min(width, height)
# make a square figure
fig = plt.figure(figsize=(size, size))
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], polar=True, axisbg='#d5de9c')
r = np.arange(0, 3.0, 0.01)
theta = 2*np.pi*r
ax.plot(theta, r, color='#ee8d18', lw=3)
ax.set_rmax(2.0)
plt.grid(True)
ax.set_title("And there was much rejoicing!", fontsize=20)
#This is the line I added:
arr1 = plt.arrow(0, 0.5, 0, 1, alpha = 0.5, width = 0.015,
edgecolor = 'black', facecolor = 'green', lw = 2, zorder = 5)
# arrow at 45 degree
arr2 = plt.arrow(45/180.*np.pi, 0.5, 0, 1, alpha = 0.5, width = 0.015,
edgecolor = 'black', facecolor = 'green', lw = 2, zorder = 5)
plt.show()
Produces:
Better? :)

Categories