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

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()

Related

framing a pie chart in matplotlib

I am desperately trying to add a "dark" border around this pie chart. I have tried the solutions described in plenty of questions here, but none turned out to add anything. You can find part of the attempts in the code:
import matplotlib.pyplot as plt
from cycler import cycler
plt.rc("axes", prop_cycle=cycler("color", ["darkgray", "gray", "lightgray"])
)
plt.rcParams["axes.edgecolor"] = "0.15"
plt.rcParams["axes.linewidth"] = 1.25
labels = ["lab1", "lab2"]
sizes = [2000, 3000]
def make_autopct(values):
def my_autopct(pct):
total = sum(values)
val = int(round(pct*total/100.0))
s = '{p:.2f}%({v:d}%)'.format(p=pct,v=val)
s = f"${val}_{{\\ {pct:.2f}\%}}$"
return s
return my_autopct
fig, ax = plt.subplots(figsize=(10, 3))
ax.pie(sizes, explode=(0,0.02), labels=labels, autopct=make_autopct(sizes))
ax.set_title("title")
ax.patch.set_edgecolor('black')
ax.patch.set_linewidth('1')
plt.savefig("title.png")
If I've understood your question right possible solution is the following:
# pip install matplotlib
import matplotlib.pyplot as plt
import numpy as np
# set chart style
plt.style.use('_mpl-gallery-nogrid')
# set data
x = [5, 2, 3, 4]
# set colors of segments
colors = plt.get_cmap('GnBu')(np.linspace(0.2, 0.7, len(x)))
# plot
fig, ax = plt.subplots()
ax.pie(x, colors=colors, radius=2,
wedgeprops={"linewidth": 2, "edgecolor": "black", 'antialiased': True}, # << HERE
frame=False, startangle=0, autopct='%.1f%%', pctdistance=0.6)
plt.show()
Below, three possibilities:
add a frame around pie patch:
ax.pie(sizes,
explode=(0,0.02),
labels=labels,
autopct=make_autopct(sizes),
frame=True)
add a border using axes coordinates (0, 0) to (1, 1) with fig.add_artist which draw on the fig object:
rect = pt.Rectangle((-0.1, -0.1), 1.2, 1.2,
fill=False, color="blue", lw=3, zorder=-1
transform=ax.transAxes)
fig.add_artist(rect)
add a border using fig coordinates (0, 0) to (1, 1) with fig.add_artist which draw on the fig object:
rect = pt.Rectangle((0.05, 0.05), .9, .9,
fill=False, ec="red", lw=1, zorder=-1,
transform=fig.transFigure)
fig.add_artist(rect)
Result:
Edit This matplotlib's transformations page explains the different coordinate systems

Get Matplotlib axis spine position in relative screen coordinates?

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.

Modifying saved plot with matplotlib

I am having a problem right now. I have run an extremely heavy simulation and, thus, generated a plot with matplotlib containing the results and saved it (as .jpg). However, there are some elemnts of the plot I would like to change, such as labels size and one vertical line. Is there a straighforward way to do this using matplotlib? I know I could have stored the data and now just replot changing the parameters (and, actually, I have done this), but I was wondering whether there is an easier way. Maybe something like:
fig, ax = plt.figure(path_to_figure)
ax.set_ylabel("Y_label")
...
You can refer to below example, which gives you more idea on how you can do this while plotting everything.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
%matplotlib inline
plt.rc('text', usetex=True)
def f(t):
return t ** 2
t1 = np.arange(0.0, 2.0, 0.1)
noise = np.random.randn(len(t1)) * 0.04
# x coordinates for the lines
xcoords = [0.1, 0.3, 0.5]
# colors for the lines
colors = ['r','k','b']
fig = plt.figure(figsize=(4, 3), dpi=200)
ax = fig.add_subplot(1, 1, 1)
plt.scatter(t1, f(t1 + noise), color = 'hotpink', label='Values obtained by experiment', edgecolors='k')
plt.plot(t1, f(t1), ls='solid', label='Theoretical expectation', color='b')
plt.title(r'This is latex title example $\mathbf{E = m \times c^2}$', fontsize='small')
for xc,c in zip(xcoords,colors):
plt.axvline(x=xc, label='line at x = {}'.format(xc), c=c)
plt.grid()
plt.legend(loc=0)
If you want to make all the fonts bold, you can also use below code to make everything bold:
font = {'weight' : 'bold',
'size' : 14 }
plt.rc('font', **font)
def f(t):
return t ** 2
t1 = np.arange(0.0, 2.0, 0.1)
noise = np.random.randn(len(t1)) * 0.04
# x coordinates for the lines
xcoords = [0.1, 0.3, 0.5]
# colors for the lines
colors = ['r','k','b']
fig = plt.figure(figsize=(4, 3), dpi=200)
ax = fig.add_subplot(1, 1, 1)
plt.scatter(t1, f(t1 + noise), color = 'hotpink', label='Values obtained by experiment', edgecolors='k')
plt.plot(t1, f(t1), ls='solid', label='Theoretical expectation', color='b')
plt.title(r'This is latex title example $\mathbf{E = m \times c^2}$', fontsize='small')
plt.xlabel("This is X-label.", fontsize=12)
plt.ylabel("This is Y-label.", fontsize=16)
for xc,c in zip(xcoords,colors):
plt.axvline(x=xc, label='line at x = {}'.format(xc), c=c)
plt.grid()
plt.legend(loc=(1.15,0.2))

update bar chart using a slider in matplotlib

I try to make a simple bar chart, where I can monitor a chemical reaction ( A -> B) using a slider for reaction steps.
So far, the following code yields a bar chart for A with a slider for reactionsteps. The print function prints the expected values for A after certain reaction steps. However, the plot won't be updated. I tried plt.draw(), plt.show() and fig.canvas.draw() but none of them worked.
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.widgets import Slider, Button
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.25)
fig.canvas.set_window_title('Einfluss der Geschwindigkeitskonstanten')
a0 = 1
b0 = 0
plt.axis([0, 5, 0, 2])
plt.xticks([1, 4], ['A', 'B'])
plt.bar(1, a0, color = 'red')
#slider:
axcolor = 'lightblue'
axrs = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor=axcolor)
srs = Slider(axrs, 'RS', 0, 20, valinit=0)
def slider_val_fkt(t):
ya = []
t = srs.val
ya = [np.exp(-0.6 * t)]
print(ya)
plt.bar(1, ya, color = 'red')
#plt.draw()
#plt.show()
fig.canvas.draw()
srs.on_changed(slider_val_fkt)
plt.show()
The new bar is drawn inside the slider axes instead of the original axes:
To overcome this you should work on the axes objects instead of using pyplot. However, since you anyways want to update the bar instead of drawing a new one, it is sufficient here to work with the bars themselves and update them using set_height.
bars = plt.bar(1, a0, color = 'red')
axrs = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor='lightblue')
srs = Slider(axrs, 'RS', 0, 20, valinit=0)
def slider_val_fkt(t):
t = srs.val
ya = [np.exp(-0.6 * t)]
bars[0].set_height(ya[0])
fig.canvas.draw_idle()
srs.on_changed(slider_val_fkt)

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