Save 3D plot in the correct position in python - numpy

I am trying to export my surface plot into a .png file. For some reason, the saving plot does not correspond to the 3D orientation of the plot showed in spyder. Here is my code:
import csv
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.ticker import LinearLocator
import matplotlib as mpl
import numpy as np
with open(r'path', 'r') as f:
voltpertime = list(csv.reader(f, delimiter=","))
voltpertime = np.array(voltpertime[0:], dtype=np.float)
Z= np.flipud(voltpertime)
fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
# Make data.
X = np.arange(1, 36, 1)
Y = np.arange(-4, 8, 0.1)
X, Y = np.meshgrid(X, Y)
# Plot the surface.
norm = mpl.colors.Normalize(vmin=-0.5, vmax=7)
surf = ax.plot_surface(X, Y, Z, cmap=cm.jet, linewidth=1, antialiased=False, norm=norm)
# Customize the z axis.
ax.set_zlim(-3, 7)
ax.zaxis.set_major_locator(LinearLocator(4))
ax.zaxis.set_major_formatter('{x:.02f}')
plt.colorbar(surf, shrink=0.5, aspect=5, label='current (nA)', pad = 0.1)
plt.yticks((-4, -2, 0, 2, 4, 6, 8), ("8", "6", "4", "2", "0", "-2", "-4"))
# rotate the axes and update
for angle in range(160, 360):
ax.view_init(35, angle)
plt.draw()
plt.pause(.001)
fig.savefig(r'path',
transparent = True, bbox_inches= 'tight', dpi=600, edgecolor= None)
plt.show()
Here is the plot in spyder:
and here is the plot when I save it:
I want to export the plot exactly how it appears in spyder.
Any idea?
Thanks

Related

adjust the location of color bar in subplots containing color and line plots

I am new to python programming. I was trying to make two subplots using matplotlib containing a line plot (panel-a) and 2-D color plot using imshow() (panel-b). I want the colorbar to be shown on the right side with same size as the color plot and it should not be within the subplot box limit.
`
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import datetime as dt
from mpl_toolkits.axes_grid1 import make_axes_locatable
# Panel (a)
x1 = np.linspace(2, -2, 5)
y1 = np.linspace(-2, 2, 5)
# Panel (b)
N = 10
arr = np.random.random((N, N))
x_lims = list(map(dt.datetime.fromtimestamp, [982376726, 982377321]))
x_lims = mdates.date2num(x_lims)
y_lims = [0, 40]
fig, ax = plt.subplots(2, 1, figsize=(14, 10))
ax[0].plot(x1, y1)
ax[0].set_ylim(-2, 2)
ax[0].set_xlim(2, -2)
ax[0].set_xticks([2, 1, 0, -1, -2])
ax[0].set_yticks([-2, -1, 0, 1, 2])
im = ax[1].imshow(arr, extent=[x_lims[0], x_lims[1], y_lims[0],
y_lims[1]],
aspect='auto')
divider = make_axes_locatable(ax[1])
cax = divider.append_axes("right", size="5%", pad=0.05)
plt.colorbar(im, cax=cax, label="diff. en. flux")
ax[1].xaxis_date()
date_format = mdates.DateFormatter('%H:%M:%S')
ax[1].xaxis.set_major_formatter(date_format)

Matplotlib Interpolate empty pixels

I have a file 'mydata.tmp' which contains 3 colums like this:
3.81107 0.624698 0.000331622
3.86505 0.624698 0.000131237
3.91903 0.624698 5.15136e-05
3.97301 0.624698 1.93627e-05
1.32802 0.874721 1.59245
1.382 0.874721 1.542
1.43598 0.874721 1.572
1.48996 0.874721 4.27933
etc.
Then I want to make a heatmap color plot where the first two columns are coordinates, and the third column are the values of that coordinates.
Also, I would like to set the third column in log scale.
I have done this
import pandas as pd
import matplotlib.pyplot as plt
import scipy.interpolate
import numpy as np
import matplotlib.colors as colors
# import data
df = pd.read_csv('mydata.tmp', delim_whitespace=True,
comment='#',header=None,
names=['1','2','3'])
x = df['1']
y = df['2']
z = df['3']
spacing = 500
xi, yi = np.linspace(x.min(), x.max(), spacing), np.linspace(y.min(),
y.max(), spacing)
XI, YI = np.meshgrid(xi, yi)
rbf = scipy.interpolate.Rbf(x, y, z, function='linear')
ZI = rbf(XI, YI)
fig, ax = plt.subplots()
sc = ax.imshow(ZI, vmin=z.min(), vmax=z.max(), origin='lower',
extent=[x.min(), x.max(), y.min(),
y.max()], cmap="GnBu", norm=colors.LogNorm(vmin=ZI.min(),
vmax=ZI.max()))
fig.colorbar(sc, ax=ax, fraction=0.05, pad=0.01)
plt.show()
And I get this Image
which has all these empty pixels.
I am looking for something like this instead (I have done this other picture with GNUplot):
How can I do it?
You could use cmap.set_bad to define a color for the NaN values:
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import griddata
import matplotlib.colors as colors
from matplotlib import cm
import copy
# Some data
x = np.array([0, 1, 3, 0, 2, 4])
y = np.array([0, 0, 0, 1, 1, 1])
z = np.array([2, 2, 3, 2, 3, 4])
# Interpolation on a grid:
nrb_points = 101
xi = np.linspace(-.5, 4.5, nrb_points)
yi = np.linspace(-.5, 1.5, nrb_points)
XI, YI = np.meshgrid(xi, yi)
xy = np.vstack((x, y)).T
XY = (XI.ravel(), YI.ravel())
ZI = griddata(points, z, XY,
method='linear',
fill_value=np.nan) # Value used [for] points
# outside of the convex hull
# of the input points.
ZI = ZI.reshape(XI.shape)
# Color map:
cmap = copy.copy(cm.jet)
cmap.set_bad('grey', 1.)
# Graph:
plt.pcolormesh(xi, yi, ZI,
#norm=colors.LogNorm(),
cmap=cmap);
plt.colorbar(label='z');
plt.plot(x, y, 'ko');
plt.xlabel('x'); plt.ylabel('y');
the result is:
I would also use griddata instead of RBF method for the interpolation. Then, point outside the input data area (i.e. the convex hull) can be set to NaN.

Python Subplot 3d Surface and Heat Map

I plan to create a figure in matplotlib, with a 3D surface on the left and its corresponding contour map on the right.
I used subplots but it only show the contour map (with blank space for the surface), and a separate figure for the surface.
Is it possible to create these plots in one figure side-by side?
EDIT: The code is as follows:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
import numpy as np
x = np.arange(-5, 5, 0.25)
y = np.arange(-5, 5, 0.25)
x, y = np.meshgrid(x, y)
r = np.sqrt(x**2 + y**2)
z = np.sin(r)
fig, (surf, cmap) = plt.subplots(1, 2)
fig = plt.figure()
surf = fig.gca(projection='3d')
surf.plot_surface(x,y,z)
cmap.contourf(x,y,z,25)
plt.show()
I guess it's hard to use plt.subplots() in order to create a grid of plots with different projections.
So the most straight forward solution is to create each subplot individually with plt.subplot.
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
import numpy as np
x = np.arange(-5, 5, 0.25)
y = np.arange(-5, 5, 0.25)
x, y = np.meshgrid(x, y)
r = np.sqrt(x**2 + y**2)
z = np.sin(r)
ax = plt.subplot(121, projection='3d')
ax.plot_surface(x,y,z)
ax2 = plt.subplot(122)
ax2.contourf(x,y,z,25)
plt.show()
Of course one may also use the gridspec capabilities for more sophisticated grid structures.

Matplotlib: Don't show errorbars in legend

I'm plotting a series of data points with x and y error but do NOT want the errorbars to be included in the legend (only the marker). Is there a way to do so?
Example:
import matplotlib.pyplot as plt
import numpy as np
subs=['one','two','three']
x=[1,2,3]
y=[1,2,3]
yerr=[2,3,1]
xerr=[0.5,1,1]
fig,(ax1)=plt.subplots(1,1)
for i in np.arange(len(x)):
ax1.errorbar(x[i],y[i],yerr=yerr[i],xerr=xerr[i],label=subs[i],ecolor='black',marker='o',ls='')
ax1.legend(loc='upper left', numpoints=1)
fig.savefig('test.pdf', bbox_inches=0)
You can modify the legend handler. See the legend guide of matplotlib.
Adapting your example, this could read:
import matplotlib.pyplot as plt
import numpy as np
subs=['one','two','three']
x=[1,2,3]
y=[1,2,3]
yerr=[2,3,1]
xerr=[0.5,1,1]
fig,(ax1)=plt.subplots(1,1)
for i in np.arange(len(x)):
ax1.errorbar(x[i],y[i],yerr=yerr[i],xerr=xerr[i],label=subs[i],ecolor='black',marker='o',ls='')
# get handles
handles, labels = ax1.get_legend_handles_labels()
# remove the errorbars
handles = [h[0] for h in handles]
# use them in the legend
ax1.legend(handles, labels, loc='upper left',numpoints=1)
plt.show()
This produces
Here is an ugly patch:
pp = []
colors = ['r', 'b', 'g']
for i, (y, yerr) in enumerate(zip(ys, yerrs)):
p = plt.plot(x, y, '-', color='%s' % colors[i])
pp.append(p[0])
plt.errorbar(x, y, yerr, color='%s' % colors[i])
plt.legend(pp, labels, numpoints=1)
Here is a figure for example:
The accepted solution works in simple cases but not in general. In particular, it did not work in my own more complex situation.
I found a more robust solution, which tests for ErrorbarContainer, which did work for me. It was proposed by Stuart W D Grieve and I copy it here for completeness
import matplotlib.pyplot as plt
from matplotlib import container
label = ['one', 'two', 'three']
color = ['red', 'blue', 'green']
x = [1, 2, 3]
y = [1, 2, 3]
yerr = [2, 3, 1]
xerr = [0.5, 1, 1]
fig, (ax1) = plt.subplots(1, 1)
for i in range(len(x)):
ax1.errorbar(x[i], y[i], yerr=yerr[i], xerr=xerr[i], label=label[i], color=color[i], ecolor='black', marker='o', ls='')
handles, labels = ax1.get_legend_handles_labels()
handles = [h[0] if isinstance(h, container.ErrorbarContainer) else h for h in handles]
ax1.legend(handles, labels)
plt.show()
It produces the following plot (on Matplotlib 3.1)
I works for me if I set the label argument as a None type.
plt.errorbar(x, y, yerr, label=None)

matplotlib polar 2d histogram

I am trying to plot some histogrammed data on a polar axis but it wont seem to work properly. An example is below, I use the custom projection found How to make the angles in a matplotlib polar plot go clockwise with 0° at the top? it works for a scatter plot so I think my problem is with the histogram function. This has been driving me nuts all day, does anyone know what I am doing wrong...........
import random
import numpy as np
import matplotlib.pyplot as plt
baz = np.zeros((20))
freq = np.zeros((20))
pwr = np.zeros((20))
for x in range(20):
baz[x] = random.randint(20,25)*10
freq[x] = random.randint(1,10)*10
pwr[x] = random.randint(-10,-1)*10
baz = baz*np.pi/180.
abins = np.linspace(0,2*np.pi,360) # 0 to 360 in steps of 360/N.
sbins = np.linspace(1, 100)
H, xedges, yedges = np.histogram2d(baz, freq, bins=(abins,sbins), weights=pwr)
plt.figure(figsize=(14,14))
plt.subplot(1, 1, 1, projection='northpolar')
#plt.scatter(baz, freq)
plt.pcolormesh(H)
plt.show()
Your code works if you explicitly pass a mgrid (with similar characteristics than your a bins and sbins) to the pcolormesh command.
Below is an example inspired by your code:
import matplotlib.pyplot as plt
import numpy as np
#Generate the data
size = 200
baz = 10*np.random.randint(20, 25, size)*np.pi/180.
freq = 10*np.random.randint(1, 10, size)
pwr = 10*np.random.randint(-10, -1, size)
abins = np.linspace(0, 2*np.pi, 360) # 0 to 360 in steps of 360/N.
sbins = np.linspace(1, 100, 50)
H, xedges, yedges = np.histogram2d(baz, freq, bins=(abins,sbins), weights=pwr)
#Grid to plot your data on using pcolormesh
theta, r = np.mgrid[0:2*np.pi:360j, 1:100:50j]
fig, ax = plt.subplots(figsize=(14,14), subplot_kw=dict(projection='northpolar'))
ax.pcolormesh(theta, r, H)
ax.set_yticklabels([]) #remove yticklabels
plt.show()