Matplotlib - get value of autoscale - python-2.7

Is there a possibility to read the scale of the axis which are set by autoscale?
For example:
import matplotlib.pyplot as plt
x=[1,2,3,4,5]
y=[1,2,3,4,5]
plt.plot(x,y)
plt.grid(True)
fig = plt.gcf()
plt.show()
I want to get the scale of the x and the y axis. In this case it would be 0.5.

After the plotting, you can do
# calculate x scale
ax = fig.gca()
foo = ax.get_xmajorticklabels()
bar = [float(foo[i].get_text()) for i in range(len(foo))]
scale_x = bar[1]-bar[0]
# calculate y scale
foo = ax.get_ymajorticklabels()
bar = [float(foo[i].get_text()) for i in range(len(foo))]
scale_y = bar[1]-bar[0]
print scale_x, scale_y
This only works after plt.show() is called (unclear to me, maybe a separate question for SO), but it works. Just append those lines to your script.

Related

Plot a 3D bar histogram with python

I have some x and y data, with which I would like to generate a 3D histogram, with a color gradient (bwr or whatever).
I have written a script which plot the interesting values, in between -2 and 2 for both x and y abscesses:
import numpy as np
import numpy.random
import matplotlib.pyplot as plt
# To generate some test data
x = np.random.randn(500)
y = np.random.randn(500)
XY = np.stack((x,y),axis=-1)
def selection(XY, limitXY=[[-2,+2],[-2,+2]]):
XY_select = []
for elt in XY:
if elt[0] > limitXY[0][0] and elt[0] < limitXY[0][1] and elt[1] > limitXY[1][0] and elt[1] < limitXY[1][1]:
XY_select.append(elt)
return np.array(XY_select)
XY_select = selection(XY, limitXY=[[-2,+2],[-2,+2]])
heatmap, xedges, yedges = np.histogram2d(XY_select[:,0], XY_select[:,1], bins = 7, range = [[-2,2],[-2,2]])
extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]]
plt.figure("Histogram")
#plt.clf()
plt.imshow(heatmap.T, extent=extent, origin='lower')
plt.show()
And give this correct result:
Now, I would like to turn this into a 3D histogram. Unfortunatly I don't success to plot it correctly with bar3d because it takes by default the length of x and y for abscisse.
I am quite sure that there is a very easy way to plot this in 3D with imshow. Like an unknow option...
I finaly succeded in doing it. I am almost sure there is a better way to do it, but at leat it works:
import numpy as np
import numpy.random
import matplotlib.pyplot as plt
# To generate some test data
x = np.random.randn(500)
y = np.random.randn(500)
XY = np.stack((x,y),axis=-1)
def selection(XY, limitXY=[[-2,+2],[-2,+2]]):
XY_select = []
for elt in XY:
if elt[0] > limitXY[0][0] and elt[0] < limitXY[0][1] and elt[1] > limitXY[1][0] and elt[1] < limitXY[1][1]:
XY_select.append(elt)
return np.array(XY_select)
XY_select = selection(XY, limitXY=[[-2,+2],[-2,+2]])
xAmplitudes = np.array(XY_select)[:,0]#your data here
yAmplitudes = np.array(XY_select)[:,1]#your other data here
fig = plt.figure() #create a canvas, tell matplotlib it's 3d
ax = fig.add_subplot(111, projection='3d')
hist, xedges, yedges = np.histogram2d(x, y, bins=(7,7), range = [[-2,+2],[-2,+2]]) # you can change your bins, and the range on which to take data
# hist is a 7X7 matrix, with the populations for each of the subspace parts.
xpos, ypos = np.meshgrid(xedges[:-1]+xedges[1:], yedges[:-1]+yedges[1:]) -(xedges[1]-xedges[0])
xpos = xpos.flatten()*1./2
ypos = ypos.flatten()*1./2
zpos = np.zeros_like (xpos)
dx = xedges [1] - xedges [0]
dy = yedges [1] - yedges [0]
dz = hist.flatten()
cmap = cm.get_cmap('jet') # Get desired colormap - you can change this!
max_height = np.max(dz) # get range of colorbars so we can normalize
min_height = np.min(dz)
# scale each z to [0,1], and get their rgb values
rgba = [cmap((k-min_height)/max_height) for k in dz]
ax.bar3d(xpos, ypos, zpos, dx, dy, dz, color=rgba, zsort='average')
plt.title("X vs. Y Amplitudes for ____ Data")
plt.xlabel("My X data source")
plt.ylabel("My Y data source")
plt.savefig("Your_title_goes_here")
plt.show()
I use this example, but I modified it, because it introduced an offset. The result is this:
You can generate the same result using something as simple as the following:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-2, 2, 7)
y = np.linspace(-2, 2, 7)
xx, yy = np.meshgrid(x, y)
z = xx*0+yy*0+ np.random.random(size=[7,7])
plt.imshow(z, interpolation='nearest', cmap=plt.cm.viridis, extent=[-2,2,2,2])
plt.show()
from mpl_toolkits.mplot3d import Axes3D
ax = Axes3D(plt.figure())
ax.plot_surface(xx, yy, z, cmap=plt.cm.viridis, cstride=1, rstride=1)
plt.show()
The results are given below:

Cut a bounding box using numpy meshgrid python

I want to create a bounding box out of the following dimensions using meshgrid but just not able to get the right box.
My parent dimensions are x = 0 to 19541 and y = 0 to 14394. Out of that, I want to cut a box from x' = 4692 to 12720 and y' = 4273 to 10117.
However, I am not getting the right bounds. Could someone please help me here?
from matplotlib.path import Path
xmin, xmax = 4692, 12720
ymin, ymax = 4273, 10117
sar_ver = [(4692, 10117), (12720, 10117), (12658, 4274), (4769, 4273), (4692, 10117)]
x, y = np.meshgrid(np.arange(xmin, xmax + 1), np.arange(ymin, ymax + 1))
shx = x
x, y = x.flatten(), y.flatten()
points = np.vstack((x, y)).T
path = Path(sar_ver)
grid = path.contains_points(points)
grid.shape = shx.shape # 5845 X 8029
print grid
UPDATE: This is what I tried and I am close to what I want but not exactly. I want to change the original origin from 0 to the image's surrounding box as shown in expected output.
The updated code that I am using is this
from matplotlib.path import Path
nx, ny = 16886, 10079
sar_ver = [(16886, 1085), (15139, 2122), (14475, 5226), (8419, 5601), (14046, 6876), (14147, 10079), (16816, 3748), (16886, 1085)]
x, y = np.meshgrid(np.arange(nx), np.arange(ny))
x, y = x.flatten(), y.flatten()
points = np.vstack((x,y)).T
path = Path(sar_ver)
grid = path.contains_points(points)
grid.shape = (10079, 16886)
grid = np.multiply(grid,255)
int_grid = grid.astype(np.uint8)
grid_img = Image.fromarray(int_grid)
grid_img.save('grid_image.png') # ACTUAL OUTPUT IMAGE WITH ORIGIN NOT SHIFTED
Input geom:
Expected output is this: Doesn't matter if the image is rotated the other way round but will be a cherry on top if its aligned correctly.
However I am getting right now this so my ACTUAL OUTPUT from the updated code posted is this:
So I want to shift the origin around the box.
BOUNDING BOX PROBLEM DETAILS AFTER GETTING THE MASK: This code comes after the line posted in the second update grid_img.save('grid_image.png') # ACTUAL OUTPUT IMAGE WITH ORIGIN NOT SHIFTED
Here im is the matrix of the actual image. What should be the x-y min, max of im to have the same shape as mask and multiply both of them to get pixel values and the rest cancelled out with 0s.
img_x = 19541 # 0 - 19541
img_y = 14394 # 0 - 14394
im = np.fromfile(binary_file_path, dtype='>f4')
im = np.reshape(im.astype(np.float32), (img_x, img_y))
im = im[:10079, :16886]
bb_list = np.multiply(grid, im)
# slice and dice
slice_rows = np.any(bb_list, axis=1)
slice_cols = np.any(bb_list, axis=0)
ymin, ymax = np.where(slice_rows)[0][[0, -1]]
xmin, xmax = np.where(slice_cols)[0][[0, -1]]
answer = bb_list[ymin:ymax + 1, xmin:xmax + 1]
# convert to unit8
int_ans = answer.astype(np.uint8)
fin_img = Image.fromarray(int_ans)
fin_img.save('test_this.jpeg')
My GOAL is to cut out a polygon of a given geom out of a given image. So I am taking the mask out of that polygon and then using that mask to cut the same out of the original image. So multiplying mask's 1's and 0's with the pixel values in the image to just get 1*pixel values.
I tried the following to cut out the actual image to have the same dimensions so that I can multiply np.multiply(im, mask) but it didn't work as image's shape is not cut into same shape as mask's. I tried your min and max below but didn't work!
im = im[xmin:xmax, ymin:ymax]
ipdb> im.shape
(5975, 8994)
ipdb> mask.shape
(8994, 8467)
Clearly I cannot multiple mask and im now.
I think you got it almost right in the first attempt, in the second one you're building a meshgrid for the full image while you just want the shape mask, don't you?
import numpy as np
import matplotlib as mpl
from matplotlib.path import Path
from matplotlib import patches
import matplotlib.pyplot as plt
from PIL import Image
sar_ver = [(16886, 1085), (15139, 2122), (14475, 5226), (8419, 5601),
(14046, 6876), (14147, 10079), (16816, 3748), (16886, 1085)]
path = Path(sar_ver)
xmin, ymin, xmax, ymax = np.asarray(path.get_extents(), dtype=int).ravel()
x, y = np.mgrid[xmin:xmax, ymin:ymax]
points = np.transpose((x.ravel(), y.ravel()))
mask = path.contains_points(points)
mask = mask.reshape(x.shape).T
img = Image.fromarray((mask * 255).astype(np.uint8))
img.save('mask.png')
# plot shape and mask for debug purposes
fig = plt.figure(figsize=(8,4))
gs = mpl.gridspec.GridSpec(1,2)
gs.update(wspace=0.2, hspace= 0.01)
ax = plt.subplot(gs[0])
patch = patches.PathPatch(path, facecolor='orange', lw=2)
ax.add_patch(patch)
ax.set_xlim(xmin, xmax)
ax.set_ylim(ymin, ymax)
ax = plt.subplot(gs[1])
ax.imshow(mask, origin='lower')
plt.savefig("shapes.png", bbox_inches="tight", pad_inches=0)
It produces the mask:
And also plots both the mask and the path for debugging purposes:
The different orientation comes from the different origin position in matplotlib plots and images, but it should be trivial enough to change it the way you want.
EDIT after latest question edits
Here's an updated script that takes an image, generates a mask for your path and cuts it out. I'm using a dummy image and scaling down shapes a bit so they're easier to work with.
import numpy as np
import matplotlib as mpl
from matplotlib.path import Path
from matplotlib import patches
import matplotlib.pyplot as plt
import skimage.transform
import skimage.data
from PIL import Image
sar_ver = np.asarray([(16886, 1085), (15139, 2122), (14475, 5226), (8419, 5601),
(14046, 6876), (14147, 10079), (16816, 3748), (16886, 1085)])
# reshape into smaller path for faster debugging
sar_ver = sar_ver // 20
# create dummy image
img = skimage.data.chelsea()
img = skimage.transform.rescale(img, 2)
# matplotlib path
path = Path(sar_ver)
xmin, ymin, xmax, ymax = np.asarray(path.get_extents(), dtype=int).ravel()
# create a mesh grid of the shape of the final mask
x, y = np.mgrid[:img.shape[1], :img.shape[0]]
# mesh grid to points
points = np.vstack((x.ravel(), y.ravel())).T
# mask for the point included in the path
mask = path.contains_points(points)
mask = mask.reshape(x.shape).T
# plots
fig = plt.figure(figsize=(8,6))
gs = mpl.gridspec.GridSpec(2,2)
gs.update(wspace=0.2, hspace= 0.2)
# image + patch
ax = plt.subplot(gs[0])
ax.imshow(img)
patch = patches.PathPatch(path, facecolor="None", edgecolor="cyan", lw=3)
ax.add_patch(patch)
# mask
ax = plt.subplot(gs[1])
ax.imshow(mask)
# filter image with mask
ax = plt.subplot(gs[2])
ax.imshow(img * mask[..., np.newaxis])
# remove mask from image
ax = plt.subplot(gs[3])
ax.imshow(img * ~mask[..., np.newaxis])
# plt.show()
plt.savefig("shapes.png", bbox_inches="tight", pad_inches=0)
I tried the open cv2 library and it appears to be faster than meshgrid or mgrid on large images. Posting opencv2 solution:
import numpy as np
import cv2
import matplotlib.pyplot as plt
from matplotlib.path import Path
sar_ver = np.array([[[1688, 108], [1513, 212], [1447, 522], [841, 560], [1404, 687], [1414, 1007], [1681, 374], [1688, 108]]] , 'int32')
print sar_ver.shape
mask=np.zeros((1439, 1954))
cv2.fillPoly(mask, sar_ver, 255)
sar_ver = np.asarray([(1688, 108), (1513, 212), (1447, 522), (841, 560), (1404, 687), (1414, 1007), (1681, 374), (1688, 108)])
path = Path(sar_ver)
xmin, ymin, xmax, ymax = np.asarray(path.get_extents(), dtype=int).ravel()
plt.imshow(mask[ymin:ymax+1, xmin:xmax+1])
plt.show()
Also, posting mgrid solution helped by Filippo above and on online chat:
import cv2
from matplotlib.path import Path
from PIL import Image
import numpy as np
sar_ver = np.asarray([(1518, 2024), (2018, 2024), (1518, 2524), (1518, 2024)])
imag = cv2.imread('test_image.jpg')
img = cv2.cvtColor(imag, cv2.COLOR_BGR2GRAY)
h, w = img.shape
path = Path(sar_ver)
xmin, ymin, xmax, ymax = np.asarray(path.get_extents(), dtype=int).ravel()
# create a mesh grid of the shape of the final mask
x, y = np.mgrid[:w, :h]
# mesh grid to points
points = np.vstack((x.ravel(), y.ravel())).T
# mask for the point included in the path
mask = path.contains_points(points)
mask = mask.reshape(x.shape).T
im = np.array(img)
bb = np.multiply(im, mask)[ymin:ymax+1, xmin:xmax+1]
# saving image or we can do plt.show
int_ans = bb.astype(np.uint8)
fin = Image.fromarray(int_ans)
fin.save('crop_test.png')

AttributeError: draw_artist can only be used after an initial draw which caches the render

My requirement is to plot the data in polar graph. However I need to keep polar graph in particular angle to looks like "V" shape and data need to plotted in between the particular angle.
In python I don't find a solution to keep the polar graph in particular angle, Example : Graph should be display in between -60 to 60 degree radius. To achieve that I have looked into couple of existing examples and creating required polar graph with FloatingSubplot functions. However I am hitting the issue , when we try to use along with function animation function with blit=True. Error message is displayed is "AttributeError: draw_artist can only be used after an initial draw which caches the render"
Here is my code.
#
import matplotlib
matplotlib.use('Qt4Agg')
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import style
import matplotlib.animation as animation
import mpl_toolkits.axisartist.floating_axes as floating_axes
from matplotlib.transforms import Affine2D
from matplotlib.projections import PolarAxes
from mpl_toolkits.axisartist import angle_helper
from mpl_toolkits.axisartist.grid_finder import MaxNLocator, DictFormatter
from mpl_toolkits.axisartist.floating_axes import GridHelperCurveLinear, FloatingSubplot
plt.close('all')
fig = plt.figure('Practice', dpi=100) # To set the fig title as pratice
ax1 = fig.add_subplot(2, 2, 1) # subplot for 1st plot
plt.ion()
ax1.grid(True)
def fractional_polar_axes(f, thlim=(0, 120), rlim=(0, 20), step=(30, 0.25),
thlabel='theta', rlabel='r', ticklabels=True, theta_offset=0, rlabels=None):
'''Return polar axes that adhere to desired theta (in deg) and r limits. steps for theta
and r are really just hints for the locators.'''
th0, th1 = thlim # deg
r0, r1 = rlim
thstep, rstep = step
tr_rotate = Affine2D().translate(theta_offset, 0)
# scale degrees to radians:
tr_scale = Affine2D().scale(np.pi / 180., 1.)
# pa = axes(polar="true") # Create a polar axis
pa = PolarAxes
tr = tr_rotate + tr_scale + pa.PolarTransform()
theta_grid_locator = angle_helper.LocatorDMS((th1 - th0) // thstep)
r_grid_locator = MaxNLocator((r1 - r0) // rstep)
theta_tick_formatter = angle_helper.FormatterDMS()
if rlabels:
rlabels = DictFormatter(rlabels)
grid_helper = GridHelperCurveLinear(tr,
extremes=(th0, th1, r0, r1),
grid_locator1=theta_grid_locator,
grid_locator2=r_grid_locator,
tick_formatter1=theta_tick_formatter,
tick_formatter2=rlabels)
a = FloatingSubplot(f, 222, grid_helper=grid_helper)
# a = Subplot(f,753, grid_helper=grid_helper)
# f.add_subplot(7,5,(3,34))
f.add_subplot(a)
# adjust x axis (theta):
print(a)
a.axis["bottom"].set_visible(False)
a.axis["top"].set_axis_direction("bottom") # tick direction
a.axis["top"].toggle(ticklabels=ticklabels, label=bool(thlabel))
a.axis["top"].major_ticklabels.set_axis_direction("top")
a.axis["top"].label.set_axis_direction("top")
a.axis["top"].major_ticklabels.set_pad(10)
# adjust y axis (r):
a.axis["left"].set_axis_direction("bottom") # tick direction
a.axis["right"].set_axis_direction("top") # tick direction
a.axis["left"].toggle(ticklabels=True, label=bool(rlabel))
# add labels:
a.axis["top"].label.set_text(thlabel)
a.axis["left"].label.set_text(rlabel)
# create a parasite axes whose transData is theta, r:
auxa = a.get_aux_axes(tr)
print(auxa)
# make aux_ax to have a clip path as in a?:
auxa.patch = a.patch
# this has a side effect that the patch is drawn twice, and possibly over some other
# artists. So, we decrease the zorder a bit to prevent this:
a.patch.zorder = -2
# add sector lines for both dimensions:
thticks = grid_helper.grid_info['lon_info'][0]
rticks = grid_helper.grid_info['lat_info'][0]
print(grid_helper.grid_info['lat_info'])
for th in thticks[1:-1]: # all but the first and last
auxa.plot([th, th], [r0, r1], ':', c='grey', zorder=-1, lw=0.5)
for ri, r in enumerate(rticks):
# plot first r line as axes border in solid black only if it isn't at r=0
if ri == 0 and r != 0:
ls, lw, color = 'solid', 1, 'black'
else:
ls, lw, color = 'dashed', 0.5, 'grey'
# From http://stackoverflow.com/a/19828753/2020363
auxa.add_artist(plt.Circle([0, 0], radius=r, ls=ls, lw=lw, color=color, fill=False,
transform=auxa.transData._b, zorder=-1))
return auxa
def animate(i):
global loopcount, th, r
th = th+.1
r = r+.1
datapoints.set_offsets(np.vstack((th,r)).T)
#print("in animate")
return datapoints,
if __name__ == '__main__':
r_locs = [0,5,10, 15, 20]
r_labels = ['0', '5', '10', '15', '20']
r_ticks = {loc: label for loc, label in zip(r_locs, r_labels)}
a1 = fractional_polar_axes(fig, thlim=(-60, 60), step=(20, 5),
theta_offset=90, rlabels=r_ticks)
th= 20
r=10
a1.scatter(th,r , c = 'r', alpha = 0.5, linewidths = '.2', s = 20) # plotting the line at thetha 20 and radius 10
datapoints = a1.scatter([], [], c='b', alpha = 0.5, linewidths = '.2', s = 20) # creating scatter line with given instruction,
ani = animation.FuncAnimation(fig, animate, frames=30, interval=20, blit=True)
plt.show(block=True)
#
"""
Above code is working perfectly fine with blit=False and also same solution working fine with line and scatter plotting in normal graph.
Please someone help me to resolve the issue.
"""

How to animate and update the title,xlabel,ylabel?

I am new to Matplotlib. Based on my code in following, I wanted to update the data,title,xlabel,ylabel at same time. However, the title and labels did not been updated, but data did.Someone can give me a solution? That will help me a lot.Thank you.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
def updata(frame_number):
current_index = frame_number % 3
a = [[1,2,3],[4,5,6],[7,8,9]]
idata['position'][:,0] = np.asarray(a[current_index])
idata['position'][:,1] = np.asarray(a[current_index])
scat.set_offsets(idata['position'])
ax.set_xlabel('The Intensity of Image1')
ax.set_ylabel('The Intensity of Image2')
ax.set_title("For Dataset %d" % current_index)
fig = plt.figure(figsize=(5,5))
ax = fig.add_axes([0,0,1,1])
idata = np.zeros(3,dtype=[('position',float,2)])
ax.set_title(label='lets begin',fontdict = {'fontsize':12},loc='center')
scat = ax.scatter(idata['position'][:,0],idata['position'][:,1],s=10,alpha=0.3,edgecolors='none')
animation = FuncAnimation(fig,updata,interval=2000)
plt.show()
Running the code, I see an empty window. The reason is that the axes span the complete figure (fig.add_axes([0,0,1,1])). In order to see the title and labels, you would need to make the axes smaller than the figure, e.g. by
ax = fig.add_subplot(111)
Also, the scale of the axes is not defined, so the animation will happen outside the axes limits. You can use ax.set_xlim and ax.set_ylim to prevent that.
Here is a complete running code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
def updata(frame_number):
current_index = frame_number % 3
a = [[1,2,3],[4,5,6],[7,8,9]]
idata['position'][:,0] = np.asarray(a[current_index])
idata['position'][:,1] = np.asarray(a[current_index])
scat.set_offsets(idata['position'])
ax.set_xlabel('The Intensity of Image1')
ax.set_ylabel('The Intensity of Image2')
ax.set_title("For Dataset %d" % current_index)
fig = plt.figure(figsize=(5,5))
ax = fig.add_subplot(111)
idata = np.zeros(3,dtype=[('position',float,2)])
ax.set_title(label='lets begin',fontdict = {'fontsize':12},loc='center')
scat = ax.scatter(idata['position'][:,0],idata['position'][:,1],
s=25,alpha=0.9,edgecolors='none')
ax.set_xlim(0,10)
ax.set_ylim(0,10)
animation = FuncAnimation(fig,updata,frames=50,interval=600)
plt.show()

Matplotlib: Displaying and closing a plot using a loop

Using Matplotlib and a for loop, is it possible to display a plot for a given period of time and then have it close when the for loop is done?
I have tried the following, but the plot simply remains open and the loop doesn't end:
import matplotlib.pyplot as plt
import psychopy
x = [34.00,108.00,64.00,99.00,99.00,51.00]
y = [5.00,17.00,11.00,8.00,14.00,5.00]
scatter(x, y, color = "black")
clock = core.Clock()
while clock.getTime() < 10.0:
plt.show()
plt.close()
Thanks
You can use interactive mode plt.ion() in combination with plt.pause().
E.g. to show your window for 5 seconds:
import matplotlib.pyplot as plt
x = [34.00,108.00,64.00,99.00,99.00,51.00]
y = [5.00,17.00,11.00,8.00,14.00,5.00]
plt.scatter(x, y, color = "black")
plt.ion()
plt.draw()
plt.pause(5)