How to invert axes of a function with matplotlib - python

I have the following function, what I am looking for is to invert the x and y axes.
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
major_ticks = np.arange(-2, 10+2, 1)
minor_ticks = np.arange(-2, 10+2, 0.25)
ax.set_xticks(major_ticks)
ax.set_xticks(minor_ticks, minor=True)
ax.set_yticks(major_ticks)
ax.set_yticks(minor_ticks, minor=True)
ax.grid(which='both')
ax.grid(which='minor', alpha=0.2)
ax.grid(which='major', alpha=0.5)
plt.title('Image')
plt.xlabel('Height (m)')
plt.ylabel('Length (m)')
plt.axis('equal')
function = np.array([0, 0.52, 0.92, 0.8])
plt.plot(function)
The following graph illustrates how it should look, best regards.

The plot command lets you specify both x and y but when you call it with a single vector, it assumes the first vector is a list of ints. So to invert the plot, create the default x and then swap x and y.
plt.plot(function, np.arange(4), '.-') # function is now treated as x, and I've created the default x and am using it for y.
Also, I renamed the axes and removed most of the formatting so this looked right, but the only interesting change is the plot command.

Related

Center specified tick labels for matplotlib's pcolomesh at the boxes

I do not understand, how to properly plot my heatmap (pcolormesh) with matplotlib. I want the tick's labels be centered below/beside the corresponding boxes - and only my given data, not some artificially extended ranges.
In the docs I found an example, which works slightly modified to floats just fine for me.
Z = []
for i in range(1, 7):
Z.append([j*i for j in range(1, 11)])
Z = np.asarray(Z)
x = np.linspace(0.1, 1.0, num=10)
y = np.linspace(0.1, 0.6, num=6)
fig, ax = plt.subplots()
ax.pcolormesh(x, y, Z, vmin=np.min(Z), edgecolors='w', linewidths=0.5, vmax=np.max(Z), shading='auto')
plt.show()
The result prints the ticks centered at the boxes, which is exactly what I want.
But as soon as I use my own data it ends up with some weird behaviour
data = pd.DataFrame(index=[0, 0.25, 0.5], data={0: [31.40455938, 101.43291831, 101.67128077], 0.25: [31.40455938, 89.81448724, 99.65066293], 0.5: [31.40455938, 57.01406046, 101.47536496]})
x = data.columns.astype(np.float64).to_numpy()
y = data.index.astype(np.float64).to_numpy()
z = data.to_numpy()
cmap = LinearSegmentedColormap.from_list('G2R', ["green", "red"])
fig, ax = plt.subplots()
ax.pcolormesh(x, y, z, shading='auto', cmap=cmap, edgecolors='w', linewidths=0.5, vmin=0, vmax=100) # shading='gouraud'
ax.set_title('not what i want')
ax.set_xlabel('X')
ax.set_ylabel('Y')
plt.show()
How do I get my heatmap to simply plot the given floats as centered tick labels without those unwanted floats? I literally want to specify my tick labels (floats or strings) to be shown centered to the boxes. I would assume there must be a way, to specify a list or array as tick labels. How do I accomplish that?
After plotting the pcolormesh you can set x and y tick with matplotlib.axes.Axes.set_xticks and matplotlib.axes.Axes.set_yticks respectively:
ax.pcolormesh(x, y, z, shading='auto', cmap=cmap, edgecolors='w', linewidths=0.5, vmin=0, vmax=100) # shading='gouraud'
ax.set_xticks(data.columns)
ax.set_yticks(data.index)

How to fix double layered text on python graph

My python graph has odd double layered text and as a beginner I don't know how to clean this up. How would this be done?
Thanks
import matplotlib.pyplot as plt
import numpy as np
fig1 = plt.figure(1)
plt.xticks(np.arange(0, 701, 100))
plt.yticks(np.arange(0.0, 3.7, 0.5))
frame1=fig1.add_axes((0.1,0.3,.8,.6))
m, b = np.polyfit(x, y, 1)
plt.plot(x, m*x + b, '-', color='grey', alpha=0.5)
plt.plot(x,y,'.',color='navy',markersize=6)
plt.errorbar(x,y,xerr=0,yerr=yerr,linestyle="None",color='navy')
plt.ylabel('$Natural\ Log\ of\ Rate$',fontsize=17)
plt.grid(False)
frame2=fig1.add_axes((.1,.1,.8,.2))
s = m*x+b #(np.sqrt(4*np.pi*8.85E-12)/2.23E-8)*x
difference = y-s
plt.plot(x, difference, 'ro')
frame2.set_ylabel('$Residual$',fontsize=17)
plt.xlabel('$Time$ $(s)$',fontsize=17)
plt.savefig('mygraph')
plt.show()
You may want to let matplotlib automatically position the subplots using fig.add_subplot(211), where 211 means that on a grid of 2 rows and 1 column, the first (top) subplots shall be used.
Afterwards it's convenient to use the API methods to plot to an axes, ax.plot() instead of plt.plot(), and also setting all other labels and ticks using the API methods. This makes it easier to determine which elements belong to which subplot.
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(0, 700, 100)
y = np.arange(0.0, 3.5, 0.5)
fig1 = plt.figure(1)
ax1=fig1.add_subplot(211)
m, b = np.polyfit(x, y, 1)
ax1.set_xticks(np.arange(0, 701, 100))
ax1.set_yticks(np.arange(0.0, 3.7, 0.5))
ax1.plot(x, m*x + b, '-', color='grey', alpha=0.5)
ax1.plot(x,y,'.',color='navy',markersize=6)
ax1.set_ylabel('$Natural\ Log\ of\ Rate$',fontsize=17)
plt.grid(False)
ax2=fig1.add_subplot(212)
s = m*x+b #(np.sqrt(4*np.pi*8.85E-12)/2.23E-8)*x
difference = y-s
ax2.plot(x, difference, 'ro')
ax2.set_ylabel('$Residual$',fontsize=17)
ax2.set_xlabel('$Time$ $(s)$',fontsize=17)
plt.show()

Adding y=x to a matplotlib scatter plot if I haven't kept track of all the data points that went in

Here's some code that does scatter plot of a number of different series using matplotlib and then adds the line y=x:
import numpy as np, matplotlib.pyplot as plt, matplotlib.cm as cm, pylab
nseries = 10
colors = cm.rainbow(np.linspace(0, 1, nseries))
all_x = []
all_y = []
for i in range(nseries):
x = np.random.random(12)+i/10.0
y = np.random.random(12)+i/5.0
plt.scatter(x, y, color=colors[i])
all_x.extend(x)
all_y.extend(y)
# Could I somehow do the next part (add identity_line) if I haven't been keeping track of all the x and y values I've seen?
identity_line = np.linspace(max(min(all_x), min(all_y)),
min(max(all_x), max(all_y)))
plt.plot(identity_line, identity_line, color="black", linestyle="dashed", linewidth=3.0)
plt.show()
In order to achieve this I've had to keep track of all the x and y values that went into the scatter plot so that I know where identity_line should start and end. Is there a way I can get y=x to show up even if I don't have a list of all the points that I plotted? I would think that something in matplotlib can give me a list of all the points after the fact, but I haven't been able to figure out how to get that list.
You don't need to know anything about your data per se. You can get away with what your matplotlib Axes object will tell you about the data.
See below:
import numpy as np
import matplotlib.pyplot as plt
# random data
N = 37
x = np.random.normal(loc=3.5, scale=1.25, size=N)
y = np.random.normal(loc=3.4, scale=1.5, size=N)
c = x**2 + y**2
# now sort it just to make it look like it's related
x.sort()
y.sort()
fig, ax = plt.subplots()
ax.scatter(x, y, s=25, c=c, cmap=plt.cm.coolwarm, zorder=10)
Here's the good part:
lims = [
np.min([ax.get_xlim(), ax.get_ylim()]), # min of both axes
np.max([ax.get_xlim(), ax.get_ylim()]), # max of both axes
]
# now plot both limits against eachother
ax.plot(lims, lims, 'k-', alpha=0.75, zorder=0)
ax.set_aspect('equal')
ax.set_xlim(lims)
ax.set_ylim(lims)
fig.savefig('/Users/paul/Desktop/so.png', dpi=300)
Et voilĂ 
In one line:
ax.plot([0,1],[0,1], transform=ax.transAxes)
No need to modify the xlim or ylim.
Starting with matplotlib 3.3 this has been made very simple with the axline method which only needs a point and a slope. To plot x=y:
ax.axline((0, 0), slope=1)
You don't need to look at your data to use this because the point you specify (i.e. here (0,0)) doesn't actually need to be in your data or plotting range.
If you set scalex and scaley to False, it saves a bit of bookkeeping. This is what I have been using lately to overlay y=x:
xpoints = ypoints = plt.xlim()
plt.plot(xpoints, ypoints, linestyle='--', color='k', lw=3, scalex=False, scaley=False)
or if you've got an axis:
xpoints = ypoints = ax.get_xlim()
ax.plot(xpoints, ypoints, linestyle='--', color='k', lw=3, scalex=False, scaley=False)
Of course, this won't give you a square aspect ratio. If you care about that, go with Paul H's solution.

Position colorbar inside figure

I have a simple scatter plot where each point has a color given by a value between 0 and 1 set to a chosen colormap. Here's a MWE of my code:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.gridspec as gridspec
x = np.random.randn(60)
y = np.random.randn(60)
z = [np.random.random() for _ in range(60)]
fig = plt.figure()
gs = gridspec.GridSpec(1, 2)
ax0 = plt.subplot(gs[0, 0])
plt.scatter(x, y, s=20)
ax1 = plt.subplot(gs[0, 1])
cm = plt.cm.get_cmap('RdYlBu_r')
plt.scatter(x, y, s=20 ,c=z, cmap=cm)
cbaxes = fig.add_axes([0.6, 0.12, 0.1, 0.02])
plt.colorbar(cax=cbaxes, ticks=[0.,1], orientation='horizontal')
fig.tight_layout()
plt.show()
which looks like this:
The problem here is that I want the small horizontal colorbar position to the lower left of the plot but using the cax argument not only feels a bit hacky, it apparently conflicts with tight_layout which results in the warning:
/usr/local/lib/python2.7/dist-packages/matplotlib/figure.py:1533: UserWarning: This figure includes Axes that are not compatible with tight_layout, so its results might be incorrect.
warnings.warn("This figure includes Axes that are not "
Isn't there a better way to position the colorbar, ie without getting a nasty warning thrown at you whenever you run the code?
Edit
I wanted the colorbar to show only the max and min values, ie: 0 and 1 and Joe helped me do that by adding vmin=0, vmax=1 to scatter like so:
plt.scatter(x, y, s=20, vmin=0, vmax=1)
so I'm removing this part of the question.
One may use a mpl_toolkits.axes_grid1.inset_locator.inset_axes to place an axes inside another axes. This axes can be used to host the colorbar. Its position is relative the the parent axes, similar to how legends are placed, using a loc argument (e.g. loc=3 means lower left). Its width and height can be specified in absolute numbers (inches) or relative to the parent axes (percentage).
cbaxes = inset_axes(ax1, width="30%", height="3%", loc=3)
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.gridspec as gridspec
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
x = np.random.randn(60)
y = np.random.randn(60)
z = [np.random.random() for _ in range(60)]
fig = plt.figure()
gs = gridspec.GridSpec(1, 2)
ax0 = plt.subplot(gs[0, 0])
plt.scatter(x, y, s=20)
ax1 = plt.subplot(gs[0, 1])
cm = plt.cm.get_cmap('RdYlBu_r')
plt.scatter(x, y, s=20 ,c=z, cmap=cm)
fig.tight_layout()
cbaxes = inset_axes(ax1, width="30%", height="3%", loc=3)
plt.colorbar(cax=cbaxes, ticks=[0.,1], orientation='horizontal')
plt.show()
Note that in order to suppress the warning, one might simply call tight_layout prior to adding the inset axes.

twinx() and axhline

When using axhline right after twinx(), the horizontal line drawn still follows the coordinates of the first y-axis.
Any tip on how to make it scale to the second y-axis ?
You could call the axhline method from the Axes objects, as in the example below, or set te current Axes with sca.
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0, 10, 0.1)
y1 = np.sin(x)
y2 = 2.0 * np.cos(x)
fig = plt.figure()
ax1 = plt.subplot(111)
ax2 = ax1.twinx()
ax1.axhline( 0.5, 0.1, 0.5, color='r', lw=3)
ax2.axhline(-0.5, 0.5, 0.9, color='b', lw=3)
ax1.plot(x, y1, 'r', lw=2)
ax2.plot(x, y2, 'b', lw=2)
plt.show()
In case you don't have access to the return value of twinx() (for example when it was called for you by Pandas) you can access the left and right axes using the Axes object's left_ax and right_ax attributes.
Only one of these will be present, as each links to the other axis.
If you have a handle to the left axis, its right_ax attribute will point to the linked right axis.
If you have a handle to the right axis, its left_ax attribute will point to the linked left axis.
For example:
df = pandas.DataFrame({'d1': numpy.random.rand(10),
'd2': numpy.random.rand(10) * 10})
ax = df.plot(secondary_y=['d2']) # returns the left axis
ax.axhline(y=0.5, alpha=0.5) # draw a line from it
ax.right_ax.axhline(y=10, color="red", alpha=0.5) # draw a line from the right axis

Categories