interactively move a point in x and y direction in python (matplotlib) - python

I would like to move a point in x and y direction using sliders. The problem is that as soon as the point has been moved in one direction, it returns to the origin (X0 and Y0 are read again). is it possible to remember the previous x or y position or is there an easier way to do this? thanks for the replies (geogebra is not an option just pyhton)
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
from matplotlib.widgets import Slider, Button, RadioButtons
f = lambda y: (1.5*y)
g = lambda x: (1.5*x)
# Start pos
x0 = 0
y0 = 0
# Select length of axes and the space between tick labels
xmin, xmax, ymin, ymax = -5, 5, -5, 5
ticks_frequency = 1
# Plot points
fig, ax = plt.subplots(figsize=(50, 10))
ptplot, = plt.plot(x0, y0, 'ko')
ax_x = plt.axes([0.25, 0.15, 0.65, 0.03])
ax_y = plt.axes([0.25, 0.1, 0.65, 0.03])
xSlider = Slider(ax_x, 'x', -5.0, 5.0, valinit=x0, valstep=0.1)
ySlider = Slider(ax_y, 'y', -5.0, 5.0, valinit=y0, valstep=0.1)
def update_x(x):
y = g(x)
ptplot.set_data(x,y)
xSlider.eventson = False
xSlider.set_val(x)
fig.canvas.draw()
xSlider.eventson = True
print("x1", x)
print("y1", y)
def update_y(y):
x = f(y)
ptplot.set_data(x,y)
ySlider.eventson = False
ySlider.set_val(y)
fig.canvas.draw()
ySlider.eventson = True
print("x2", x)
print("y2", y)
xSlider.on_changed(update_x)
ySlider.on_changed(update_y)
# Set identical scales for both axes
ax.set(xlim=(xmin-1, xmax+1), ylim=(ymin-1, ymax+1), aspect='equal')
# Set bottom and left spines as x and y axes of coordinate system
ax.spines['bottom'].set_position('zero')
ax.spines['left'].set_position('zero')
# Remove top and right spines
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
# Create 'x' and 'y' labels placed at the end of the axes
ax.set_xlabel('x', size=14, labelpad=-24, x=1.03)
ax.set_ylabel('y', size=14, labelpad=-21, y=1.02, rotation=0)
# Create custom major ticks to determine position of tick labels
x_ticks = np.arange(xmin, xmax+1, ticks_frequency)
y_ticks = np.arange(ymin, ymax+1, ticks_frequency)
ax.set_xticks(x_ticks[x_ticks != 0])
ax.set_yticks(y_ticks[y_ticks != 0])
# Create minor ticks placed at each integer to enable drawing of minor grid
# lines: note that this has no effect in this example with ticks_frequency=1
ax.set_xticks(np.arange(xmin, xmax+1), minor=True)
ax.set_yticks(np.arange(ymin, ymax+1), minor=True)
# Draw major and minor grid lines
ax.grid(which='both', color='grey', linewidth=1, linestyle='-', alpha=0.2)
# Draw arrows
arrow_fmt = dict(markersize=4, color='black', clip_on=False)
ax.plot((1), (0), marker='>', transform=ax.get_yaxis_transform(), **arrow_fmt)
ax.plot((0), (1), marker='^', transform=ax.get_xaxis_transform(), **arrow_fmt)
plt.show()

You can get the current position of your point with ptplot.get_data.
So your update functions could look like this:
def update_x(x):
y = g(x)
x0, y0 = ptplot.get_data()
ptplot.set_data(x0 + x, y0 + y)
xSlider.eventson = False
xSlider.set_val(x)
fig.canvas.draw()
xSlider.eventson = True
print("x1", x)
print("y1", y)
def update_y(y):
x = f(y)
x0, y0 = ptplot.get_data()
ptplot.set_data(x0 + x, y0 + y)
ySlider.eventson = False
ySlider.set_val(y)
fig.canvas.draw()
ySlider.eventson = True
print("x2", x)
print("y2", y)
However, although that is what you asked for, I doubt that this is the logic you are after.

Related

How to put the scaling on the ticks in ternary plot instead of x and y axis

I am try to work out with my atomic composition with ternary phase diagram, here is my picture
I wish to put my scale to the ticks on the ternary phase diagram (i.e. those triangular axis) instead of x and y axis. Is there a ways to put the scale on the tick at triangular axis instead of axis x and y? How to remove the x-axis and y-axis while still maintain its labels?
from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.tri as tri
def plot_ticks(start, stop, tick, n):
r = np.linspace(0, 1, n+1)
x = start[0] * (1 - r) + stop[0] * r
x = np.vstack((x, x + tick[0]))
y = start[1] * (1 - r) + stop[1] * r
y = np.vstack((y, y + tick[1]))
plt.plot(x, y, 'k', lw=1)
n = 5
tick_size = 0.1
margin = 0.05
# define corners of triangle
left = np.r_[0, 0]
right = np.r_[1, 0]
top = np.r_[0.5, np.sqrt(3)*0.576]
triangle = np.c_[left, right, top, left]
# define corners of triangle
left = np.r_[0, 0]
right = np.r_[1, 0]
top = np.r_[0.5, np.sqrt(3)*0.576]
triangle = np.c_[left, right, top, left]
# define vectors for ticks
bottom_tick = 0.8264*tick_size * (right - top) / n
right_tick = 0.8264*tick_size * (top - left) / n
left_tick = 0.8264*tick_size * (left - right) / n
# first load some data: format x1,x2,x3,value
test_data = np.array([[4,0,0,2.238],
[0,4,0,2.315],
[0,0,4,2.147],
[3,1,0,2.494],
[2,2,0,2.190],
[2,2,0,2.632],
[3,0,1,2.173],
[2,0,2,2.329],
[1,0,3,2.526],
[0,3,1,2.365],
[0,2,2,2.220],
[0,1,3,2.080],
[2,1,1,2.231],
[1,2,1,2.291],
[1,1,2,2.088]])
#Define twin axis
#ax = plt.gca()
fig, ax = plt.subplots()
plot_ticks(left, right, bottom_tick, n)
plot_ticks(right, top, right_tick, n)
plot_ticks(left, top, left_tick, n)
#ax2 = ax.twinx()
# barycentric coords: (a,b,c)
a=test_data[:,0]
b=test_data[:,1]
c=test_data[:,2]
# values is stored in the last column
v = test_data[:,-1]
# translate the data to cartesian corrds
x = 0.5 * ( 2.*b+c ) / ( a+b+c )
y = 0.576*np.sqrt(3) * c / (a+b+c)
# create a triangulation out of these points
T = tri.Triangulation(x,y)
# plot the contour
plt.tricontourf(x,y,T.triangles,v,cmap='jet')
# create the grid
corners = np.array([[0, 0], [1, 0], [0.5, np.sqrt(3)*0.576]])
triangle = tri.Triangulation(corners[:, 0], corners[:, 1])
# creating the grid
refiner = tri.UniformTriRefiner(triangle)
trimesh = refiner.refine_triangulation(subdiv=4)
#plotting the mesh and caliberate the axis
plt.triplot(trimesh,'k--')
#plt.title('Binding energy peratom of Al-Ti-Ni clusters')
ax.set_xlabel('Al-Ti',fontsize=12,color='black')
ax.set_ylabel('Ti-Ni',fontsize=12,color='black')
ax2 = ax.twinx()
ax2.set_ylabel('Al-Ni',fontsize=12,color='black')
plt.gcf().text(0.07, 0.05, 'Ti', fontsize=12,color='black')
plt.gcf().text(0.93, 0.05, 'Al', fontsize=12,color='black')
plt.gcf().text(0.5, 0.9, 'Ni', fontsize=12,color='black')
#set scale for axis
ax.set_xlim(1, 0)
ax.set_ylim(0, 1)
ax2.set_ylim(1, 0)
cax = plt.axes([0.75, 0.55, 0.055, 0.3])
plt.colorbar(cax=cax,format='%.3f')
plt.savefig("AID.png", dpi=1000)
plt.show()
As was mentioned in the comments you can make your own axis just by adding a text to the ticks you generate. Most of the time you need a little tweaking
to get the offsets right...
def plot_ticks(start, stop, tick, n, offset=(.0, .0)):
r = np.linspace(0, 1, n+1)
x = start[0] * (1 - r) + stop[0] * r
x = np.vstack((x, x + tick[0]))
y = start[1] * (1 - r) + stop[1] * r
y = np.vstack((y, y + tick[1]))
plt.plot(x, y, 'k', lw=1)
# add tick labels
for xx, yy, rr in zip(x[1], y[1], r):
plt.text(xx+offset[0], yy+offset[1], "{:.2}".format(rr))
# Note that the ordering from start to stop is important for the tick labels
plot_ticks(right, left, bottom_tick, n, offset=(0, -0.04))
plot_ticks(left, top, left_tick, n, offset=(-0.06, -0.0))
plot_ticks(top, right, right_tick, n)
In addition I switched the axis off via ax.set_axis_off() and I also deleted the twin axis, as you used these only to display the ticks and labels for the connections. These labels can also easily be placed via fig.text() as you did with the corners:
# Corners
fig.text(0.07, 0.05, 'Ti', fontsize=12, color='black')
fig.text(0.93, 0.05, 'Al', fontsize=12, color='black')
fig.text(0.50, 0.90, 'Ni', fontsize=12, color='black')
# Connections
fig.text(0.47, 0.05, 'Ti-Al', fontsize=12, color='black') # Note: not sure about
fig.text(0.72, 0.50, 'Al-Ni', fontsize=12, color='black') # the nomenclature;
fig.text(0.25, 0.50, 'Ti-Ni', fontsize=12, color='black') # might be switched

How to update one plot as mouse moving over another?

I wanted to plot data in supplemental plot, which correspond to current X-value of mouse hover in main plot.
I coded
import math
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(dpi=100, figsize=(5, 5))
x = np.arange(0, 6, 0.1)
plt.plot(x, np.sin(x), 'r')
fig2, ax2 = plt.subplots(dpi=100, figsize=(5, 5))
def plot_ray(angle, y):
circle = plt.Circle((0, 0), 1, color='b', fill=False)
length = y / math.sin(angle)
line = plt.Line2D([0, length * math.cos(angle)], [0, length * math.sin(angle)])
ax2.clear()
ax2.set_xlim(-2, 2)
ax2.set_ylim(-2, 2)
ax2.add_artist(circle)
ax2.add_artist(line)
def mouse_move(event):
x = event.xdata
y = event.ydata
if x is not None and y is not None:
angle = x
plot_ray(angle, y)
cid = fig.canvas.mpl_connect('motion_notify_event', mouse_move)
plt.show(block=True)
Unfortunately, ax2 behaves unpredicatble. It is either not updated while I hovering mouse, until I click fig2 window. Or it doesn't update, until I set or release breakpoint in pycharm.
How to code correct behaviour?
You forgot to refresh the second figure after changing it. Add fig2.canvas.draw_idle() at the end.
def mouse_move(event):
x = event.xdata
y = event.ydata
if x is not None and y is not None:
angle = x
plot_ray(angle, y)
fig2.canvas.draw_idle()
Note that this would now create new circle and artist on every mouse_move event, which is rather inefficient. You would rather want to create those artists once and only update their properties.
The following runs much more smoothly.
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(dpi=100, figsize=(5, 5))
x = np.arange(0, 6, 0.1)
plt.plot(x, np.sin(x), 'r')
fig2, ax2 = plt.subplots(dpi=100, figsize=(5, 5))
circle = plt.Circle((0, 0), 1, color='b', fill=False)
ax2.add_artist(circle)
line, = ax2.plot([],[])
ax2.set_xlim(-2, 2)
ax2.set_ylim(-2, 2)
def plot_ray(angle, y):
length = y / np.sin(angle)
line.set_data([0, length * np.cos(angle)], [0, length * np.sin(angle)])
def mouse_move(event):
x = event.xdata
y = event.ydata
if x is not None and y is not None:
angle = x
plot_ray(angle, y)
fig2.canvas.draw_idle()
cid = fig.canvas.mpl_connect('motion_notify_event', mouse_move)
plt.show(block=True)

How to put circles on top of a polygon?

I use matplotlib to generate an image in the following way:
fig = plt.figure()
ax = fig.add_subplot(111)
ax.fill(border[0],border[1], color='g', linewidth=1, fill=True, alpha = 0.5)
patches = []
for x1,y1,r in zip(x, y, radii):
circle = Circle((x1,y1), r)
patches.append(circle)
p = PatchCollection(patches, cmap='cool', alpha=1.0)
p.set_array(c)
ax.add_collection(p)
plt.colorbar(p)
plt.savefig(fig_name)
What I want to have is a polygon (given by its border) and colored circles on the top of this polygon. However, I get the polygon on the top of the circles.
This is strange because I plot the polygon first and then I add circles to the plot.
Does anybody know why it happens and how this problem can be resolved?
ADDED
As requested, here is fully working example:
import pandas
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.collections import PatchCollection
from matplotlib.patches import Circle, Polygon
import numpy as np
def plot_xyc(df, x_col, y_col, c_col, radius, fig_name, title, zrange):
resolution = 50
x = df[x_col]
y = df[y_col]
c = df[c_col]
x0 = (max(x) + min(x))/2.0
y0 = (max(y) + min(y))/2.0
dx = (max(x) - min(x))
dy = (max(y) - min(y))
delta = max(dx, dy)
radii = [delta*radius for i in range(len(x))]
fig = plt.figure()
plt.title(title)
ax = fig.add_subplot(111)
border = ([-3, 3, 3, -3], [-3, -3, 3, 3])
ax.fill(border[0],border[1], color='g', linewidth=1, fill=True, alpha = 1.0)
patches = []
for x1,y1,r in zip(x, y, radii):
circle = Circle((x1,y1), r)
patches.append(circle)
patches.append(Circle((-100,-100), r))
patches.append(Circle((-100,-100), r))
p = PatchCollection(patches, cmap='cool', alpha=1.0)
p.set_array(c)
max_ind = max(c.index)
c.set_value(max_ind + 1, min(zrange))
c.set_value(max_ind + 2, max(zrange))
plt.xlim([x0 - delta/2.0 - 0.05*delta, x0 + delta/2.0 + 0.05*delta])
plt.ylim([y0 - delta/2.0 - 0.05*delta, y0 + delta/2.0 + 0.05*delta])
ax.add_collection(p)
plt.colorbar(p)
plt.savefig(fig_name)
if __name__ == '__main__':
df = pandas.DataFrame({'x':[1,2,3,4], 'y':[4,3,2,1], 'z':[1,1,2,2]})
plot_xyc(df, 'x', 'y', 'z', 0.1, 'test2.png', 'My Titlle', (0.0, 3.0))
You're looking for zorder.
In matplotlib, all additional arguments are just passed up the class heirarchy. zorder is a kwarg of the Artist class, so you just need to make sure that at some point it gets zorder.
You can do it two ways in your example;
either add it in here:
ax.fill(border[0],border[1], color='g', linewidth=1, fill=True, alpha = 1.0, zorder=1)
or here:
p = PatchCollection(patches, cmap='cool', alpha=1.0, zorder=2)
or if you want, both. Objects with a higher zorder sit on top of those with lower values.

Matplotlib - 2 problems. Common colorbar / labels not showing up

I finally forced the 3 plots I want into one plot with 3 subplots...now I need to add a common colorbar, preferably horizontally oriented. Also, now that I have them as subplots, I have lost the labels that were there in a previous iteration.
It seems that the examples suggest I add an axes, but I don't quite get what the numbers in the arguments are.
def plot_that_2(x_vals, y_vals, z_1_vals, z_2_vals, z_3_vals, figname, units, efficiency_or_not):
global letter_pic_width
plt.close() #I moved this up from the end of the file because it solved my QTagg problem
UI = [uniformity_calc(z_1_vals), uniformity_calc(z_2_vals), uniformity_calc(z_3_vals)]
ranges = [ str(int(np.max(z_1_vals) - np.min(z_1_vals))), str(int(np.max(z_2_vals) - np.min(z_2_vals))), str(int(np.max(z_3_vals) - np.min(z_3_vals)))]
z_vals = [z_1_vals, z_2_vals, z_3_vals]
fig = plt.figure(figsize = (letter_pic_width, letter_pic_width/3 ))
ax0 = fig.add_subplot(1,3,1, aspect = 1)
ax1 = fig.add_subplot(1,3,2, aspect = 1)
ax2 = fig.add_subplot(1,3,3, aspect = 1)
axenames = [ax0, ax1, ax2]
for z_val, unif, rangenum, ax in zip(z_vals, UI, ranges, axenames):
ax.scatter(x_vals, y_vals, c = z_val, s = 100, cmap = 'rainbow')
if efficiency_or_not:
ax.vmin = 0
ax.vmax = 1
ax.xlabel = 'Uniformity: ' + unif
else:
ax.xlabel = 'Uniformity: ' + unif + ' ' + rangenum + ' ppm'
plt.savefig('./'+ figname + '.jpg', dpi = 100)
To set the xlabel, use ax.set_xlabel('Uniformity: ' + unif) See more information here in the documentation for axes.
The example you linked to uses the add_axes method of a figure as an alternative to add_subplot. The documentation for figures explains what the numbers in add_axes are: "Add an axes at position rect [left, bottom, width, height] where all quantities are in fractions of figure width and height."
rect = l,b,w,h
fig.add_axes(rect)
To answer your question about the colorbar axis, the numbers represent
[bottom_left_x_coord, bottom_left_y_coord, width, height]
An appropriate colorbar might be
# x y w h
[0.2, 0.1, 0.6, 0.05]
Here's your code, somewhat reworked which adds a colorbar:
import numpy as np
import matplotlib.pyplot as plt
WIDTH = 9
def uniformity_calc(x):
return x.mean()
def plotter(x, y, zs, name, units, efficiency=True):
fig, axarr = plt.subplots(1, 3, figsize=(WIDTH, WIDTH/3),
subplot_kw={'aspect':1})
fig.suptitle(name)
UI = map(uniformity_calc, zs)
ranges = map(lambda x: int(np.max(x)-np.min(x)), zs)
for ax, z, unif, rangenum in zip(axarr, zs, UI, ranges):
scat = ax.scatter(x, y, c=z, s=100, cmap='rainbow')
label = 'Uniformity: %i'%unif
if not efficiency:
label += ' %i ppm'%rangenum
ax.set_xlabel(label)
# Colorbar [left, bottom, width, height
cax = fig.add_axes([0.2, 0.1, 0.6, 0.05])
cbar = fig.colorbar(scat, cax, orientation='horizontal')
cbar.set_label('This is a colorbar')
plt.show()
def main():
x, y = np.meshgrid(np.arange(10), np.arange(10))
zs = [np.random.rand(*y.shape) for _ in range(3)]
plotter(x.flatten(), y.flatten(), zs, 'name', None)
if __name__ == "__main__":
main()

How to make four-way logarithmic plot in Matplotlib?

Four-way logarithmic plot is a very often used graph for vibration control and earthquake protection. I am quite interesting in how this plot can be plotted in Matplotlib instead of adding axes in Inkscape. A sample of Four-way logarithmic plot is here.
A quick and dirty Python code can generate main part of the figure, but I cannot add the two axes onto the figure. http://matplotlib.org/examples/axes_grid/demo_curvelinear_grid.html provides an example of adding axes, but I fails to make it working. Anyone has similar experience on adding axes to Matplotlib figure?
from pylab import *
from mpl_toolkits.axisartist.grid_helper_curvelinear import GridHelperCurveLinear
from mpl_toolkits.axisartist import Subplot
beta=logspace(-1,1,500)
Rd={}
for zeta in [0.01,0.1,0.2,0.7,1]:
Rd[zeta]=beta/sqrt((1-beta*beta)**2+(2*beta*zeta)**2)
loglog(beta,Rd[zeta])
ylim([0.1,10])
xlim([0.1,10])
grid('on',which='minor')
Update: Thank you all! I use Inkscape to modify the figure above. I think the result is just fine. However, I am still looking for methods to draw this figure in Matplotlib.
Here is a partial solution. I am still working on how to do all of this in a natural loglog() plot rather than scaling the data. (To complete this example you would have to define custom tick-lables so that they display 10**x rather than x.)
%matplotlib inline # I am doing this in an IPython notebook.
from matplotlib import pyplot as plt
import numpy as np
from numpy import log10
# Generate the data
beta = np.logspace(-1, 1, 500)[:, None]
zeta = np.array([0.01,0.1,0.2,0.7,1])[None, :]
Rd = beta/np.sqrt((1 - beta*beta)**2 + (2*beta*zeta)**2)
def draw(beta=beta, Rd=Rd):
plt.plot(log10(beta), log10(Rd))
plt.ylim([log10(0.1), log10(10)])
plt.xlim([log10(0.1), log10(10)])
plt.grid('on',which='minor')
ax = plt.gca()
ax.set_aspect(1)
from mpl_toolkits.axisartist import GridHelperCurveLinear
from matplotlib.transforms import Affine2D
from mpl_toolkits.axisartist import SubplotHost
from mpl_toolkits.axisartist import Subplot
#tr = Affine2D().rotate(-np.pi/2)
#inv_tr = Affine2D().rotate(np.pi/2)
class Transform(object):
"""Provides transforms to go to and from rotated grid.
Parameters
----------
ilim : (xmin, xmax, ymin, ymax)
The limits of the displayed axes (in physical units)
olim : (xmin, xmax, ymin, ymax)
The limits of the rotated axes (in physical units)
"""
def __init__(self, ilim, olim):
# Convert each to a 3x3 matrix and compute the transform
# [x1, y1, 1] = A*[x0, y0, 1]
x0, x1, y0, y1 = np.log10(ilim)
I = np.array([[x0, x0, x1],
[y0, y1, y1],
[ 1, 1, 1]])
x0, x1, y0, y1 = np.log10(olim)
x_mid = (x0 + x1)/2
y_mid = (y0 + y1)/2
O = np.array([[ x0, x_mid, x1],
[y_mid, y1, y_mid],
[ 1, 1, 1]])
self.A = np.dot(O, np.linalg.inv(I))
self.Ainv = np.linalg.inv(self.A)
def tr(self, x, y):
"""From "curved" (rotated) coords to rectlinear coords"""
x, y = map(np.asarray, (x, y))
return np.dot(self.A, np.asarray([x, y, 1]))[:2]
def inv_tr(self, x, y):
"""From rectlinear coords to "curved" (rotated) coords"""
x, y = map(np.asarray, (x, y))
return np.dot(self.Ainv, np.asarray([x, y, 1]))[:2]
ilim = (0.1, 10)
olim = (0.01, 100)
tr = Transform(ilim + ilim, olim + olim)
grid_helper = GridHelperCurveLinear((tr.tr, tr.inv_tr))
fig = plt.gcf()
ax0 = Subplot(fig, 1, 1, 1)
ax1 = Subplot(fig, 1, 1, 1, grid_helper=grid_helper, frameon=False)
ax1.set_xlim(*np.log10(olim))
ax1.set_ylim(*np.log10(olim))
ax1.axis["left"] = ax1.new_floating_axis(0, 0.)
ax1.axis["bottom"] = ax1.new_floating_axis(1, 0.0)
fig.add_subplot(ax0)
fig.add_subplot(ax1)
ax0.grid('on', which='both')
ax1.grid('on', which='both')
plt.plot(log10(beta), log10(Rd))
plt.ylim(np.log10(ilim))
plt.xlim(np.log10(ilim))
This seems to be a bit tricker than it should. There are ways to center the spines (axis lines), and ways to rotate them, but those do not work together. Adding a normal axis on a line (a la mpl demos) results in a curved axis (because it is logarithmic). Here is a [poor] example of how to draw -- as in, like you would with Inkscape something to look like an additional pair of axis spines with the example data.
import matplotlib.pyplot as plt
import numpy as np
#data
b = np.logspace(-1, 1, 500)
Rd = {}
for zeta in [0.01, 0.1, 0.2, 0.7, 1]:
Rd[zeta] = b / np.sqrt((1 - b * b) ** 2 + (2 * b * zeta) ** 2)
#plot
fig = plt.figure()
ax1 = fig.add_subplot(111)
for z in Rd:
ax1.loglog(b, Rd[z])
ax1.set_xlim([0.1, 10])
ax1.set_ylim([0.1, 10])
ax1.set_aspect(1.)
#draw lines to look like diagonal spines (axes)
xmin, xmax = ax1.get_xlim() # xlim == ylim
a = np.log10(xmin)
b = np.log10(xmax)
span = b - a
period_points = 3 # number of points/ticks per decade
npts = (span * period_points) + 1 # +1 for even powers of 10
x1 = np.logspace(a, b, num=npts)
x2 = np.logspace(b, a, num=npts)
ax1.plot(x1, x1, color='k', marker='x', ms='9')
ax1.plot(x1, x2, color='k', marker='x', ms='9')
#NOTE: v1.2.1 lacks 'TICKUP' and similar - these may be
# a better choice in v1.3x and beyond
ax1.text(0.97, 0.9,
"axis label: A",
size='large',
horizontalalignment='right',
verticalalignment='top',
rotation=45,
transform=ax1.transAxes,
#bbox={'facecolor': 'white', 'alpha': 0.5, 'pad': 10},
)
ax1.text(0.03, 0.9,
"axis label: B",
size='large',
horizontalalignment='left',
verticalalignment='top',
rotation=-45,
transform=ax1.transAxes,
#bbox={'facecolor': 'white', 'alpha': 0.5, 'pad': 10},
)
plt.savefig("example.pdf")

Categories