Consider this y(x) function:
where we can generate these scattered points in a file: dataset_1D.dat:
# x y
0 0
1 1
2 0
3 -9
4 -32
The following is a 1D interpolation code for these points:
Load this scattered points
Create a x_mesh
Perform a 1D interpolation
Code:
import numpy as np
from scipy.interpolate import interp2d, interp1d, interpnd
import matplotlib.pyplot as plt
# Load the data:
x, y = np.loadtxt('./dataset_1D.dat', skiprows = 1).T
# Create the function Y_inter for interpolation:
Y_inter = interp1d(x,y)
# Create the x_mesh:
x_mesh = np.linspace(0, 4, num=10)
print x_mesh
# We calculate the y-interpolated of this x_mesh :
Y_interpolated = Y_inter(x_mesh)
print Y_interpolated
# plot:
plt.plot(x_mesh, Y_interpolated, "k+")
plt.plot(x, y, 'ro')
plt.legend(['Linear 1D interpolation', 'data'], loc='lower left', prop={'size':12})
plt.xlim(-0.1, 4.2)
plt.grid()
plt.ylabel('y')
plt.xlabel('x')
plt.show()
This plots the following:
Now, consider this z(x,y) function:
where we can generate these scattered points in a file: dataset_2D.dat :
# x y z
0 0 0
1 1 0
2 2 -4
3 3 -18
4 4 -48
In this case we would have to perform a 2D interpolation:
import numpy as np
from scipy.interpolate import interp1d, interp2d, interpnd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# Load the data:
x, y, z = np.loadtxt('./dataset_2D.dat', skiprows = 1).T
# Create the function Z_inter for interpolation:
Z_inter = interp2d(x, y, z)
# Create the x_mesh and y_mesh :
x_mesh = np.linspace(1.0, 4, num=10)
y_mesh = np.linspace(1.0, 4, num=10)
print x_mesh
print y_mesh
# We calculate the z-interpolated of this x_mesh and y_mesh :
Z_interpolated = Z_inter(x_mesh, y_mesh)
print Z_interpolated
print type(Z_interpolated)
print Z_interpolated.shape
# plot:
fig = plt.figure()
ax = Axes3D(fig)
ax.scatter(x, y, z, c='r', marker='o')
plt.legend(['data'], loc='lower left', prop={'size':12})
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
plt.show()
This plots the following:
where the scattered data is shown again in red dots, to be consistent with the 2D plot.
I do not know how to interpret the Z_interpolated result:
According to the printing lines for the above code,
Z_interpolated is a n-dimensional numpy array, of shape (10,10). In other words, a 2D matrix with 10 rows and 10 columns.
I would have expected an interpolated z[i] value for each value of x_mesh[i] and y_mesh[i] Why I do not receive this ?
How could I plot also in the 3D plot the interpolated data (just like the black crosses in the 2D plot)?
Interpretation of Z_interpolated: your 1-D x_mesh and y_mesh defines a mesh on which to interpolate. Your 2-D interpolation return z is therefore a 2D array with shape (len(y), len(x)) which matches np.meshgrid(x_mesh, y_mesh). As you can see, your z[i, i], instead of z[i], is the expected value for x_mesh[i] and y_mesh[i]. And it just has a lot more, all values on the mesh.
A potential plot to show all interpolated data:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
from scipy.interpolate import interp2d
# Your original function
x = y = np.arange(0, 5, 0.1)
xx, yy = np.meshgrid(x, y)
zz = 2 * (xx ** 2) - (xx ** 3) - (yy ** 2)
# Your scattered points
x = y = np.arange(0, 5)
z = [0, 0, -4, -18, -48]
# Your interpolation
Z_inter = interp2d(x, y, z)
x_mesh = y_mesh = np.linspace(1.0, 4, num=10)
Z_interpolated = Z_inter(x_mesh, y_mesh)
fig = plt.figure()
ax = fig.gca(projection='3d')
# Plot your original function
ax.plot_surface(xx, yy, zz, color='b', alpha=0.5)
# Plot your initial scattered points
ax.scatter(x, y, z, color='r', marker='o')
# Plot your interpolation data
X_real_mesh, Y_real_mesh = np.meshgrid(x_mesh, y_mesh)
ax.scatter(X_real_mesh, Y_real_mesh, Z_interpolated, color='g', marker='^')
plt.show()
You would need two steps of interpolation. The first interpolates between y data. And the second interpolates between z data. You then plot the x_mesh with the two interpolated arrays.
x_mesh = np.linspace(0, 4, num=16)
yinterp = np.interp(x_mesh, x, y)
zinterp = np.interp(x_mesh, x, z)
ax.scatter(x_mesh, yinterp, zinterp, c='k', marker='s')
In the complete example below I added some variation in y direction as well to make the solution more general.
u = u"""# x y z
0 0 0
1 3 0
2 9 -4
3 16 -18
4 32 -48"""
import io
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# Load the data:
x, y, z = np.loadtxt(io.StringIO(u), skiprows = 1, unpack=True)
x_mesh = np.linspace(0, 4, num=16)
yinterp = np.interp(x_mesh, x, y)
zinterp = np.interp(x_mesh, x, z)
fig = plt.figure()
ax = Axes3D(fig)
ax.scatter(x_mesh, yinterp, zinterp, c='k', marker='s')
ax.scatter(x, y, z, c='r', marker='o')
plt.legend(['data'], loc='lower left', prop={'size':12})
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
plt.show()
For using scipy.interpolate.interp1d the solution is essentially the same:
u = u"""# x y z
0 0 0
1 3 0
2 9 -4
3 16 -18
4 32 -48"""
import io
import numpy as np
from scipy.interpolate import interp1d
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# Load the data:
x, y, z = np.loadtxt(io.StringIO(u), skiprows = 1, unpack=True)
x_mesh = np.linspace(0, 4, num=16)
fy = interp1d(x, y, kind='cubic')
fz = interp1d(x, z, kind='cubic')
fig = plt.figure()
ax = Axes3D(fig)
ax.scatter(x_mesh, fy(x_mesh), fz(x_mesh), c='k', marker='s')
ax.scatter(x, y, z, c='r', marker='o')
plt.legend(['data'], loc='lower left', prop={'size':12})
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
plt.show()
Related
I am attempting a very similar problem to this question:
How to fit a plane to a 3D dataset in Python
I have implemented the second order solution, however I am having trouble applying this in my actual plots, which are displaying linearly.
Full Code:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import pandas as pd
from sklearn import linear_model
x = [0, 1, 2]
y = [0, -1, -2]
Z = np.array([[90,80,90], [85,75,85], [82,73,82]])
X,Y = np.meshgrid(x,y)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
print (X.shape, Y.shape, Z.shape)
x1, y1, z1 = X.flatten(), Y.flatten(), Z.flatten()
x1y1, x1x1, y1y1 = x1*y1, x1*x1, y1*y1
X_data = np.array([x1, y1, x1y1, x1x1, y1y1]).T # X_data shape: n, 5
Y_data = z1
reg = linear_model.LinearRegression().fit(X_data, Y_data)
ax.plot_surface(X, Y, Z)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()
plt.contourf(X, Y, Z, cmap='rainbow')
I want to add constant x, y, z lines into a matplotlib 3D scatter plot in Python which extended from this limit point, may I know how could I do so?
x_limit = [-0.5] y_limit = [151] z_limit = [1090]
Example code:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import pandas as pd
from scipy.stats import multivariate_normal
fig = plt.figure(figsize=(8,8)) # size 4 inches X 4 inches
ax = fig.add_subplot(111, projection='3d')
np.random.seed(42)
xs = np.random.random(100)*-0.8
ys = np.random.random(100)*300
zs = np.random.random(100)*10500
plot = ax.scatter(xs,ys,zs)
ax.set_title("3D plot with limit")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")
x_limit = [-0.5]
y_limit = [151]
z_limit = [1090]
ax.scatter(x_limit, y_limit, z_limit, c = 'g', marker = "s", s = 50)
plt.show()
This code should do the trick. There are a couple important things to note though. First, the mesh grid created only worked because each of the three axes share the same limits. Second, while the x_limit and y_limit values work as the X and Y arguments it appears that the Z argument is expected to be of a higher dimensionality (hence why I used full_like to fill an array of the same shape as x_1 and x_2 with the Z limit).
x_1, x_2 = np.meshgrid(np.arange(0, 1.1, 0.1), np.arange(0, 1.1, 0.1))
ax.plot_surface(x_limit, x_1, x_2, color='r', alpha=0.5)
ax.plot_surface(x_1, y_limit, x_2, color='g', alpha=0.5)
ax.plot_surface(x_1, x_2, np.full_like(x_1, z_limit), color='b', alpha=0.5)
The following code produces a contourf plot.
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0,10)
X, Y = np.meshgrid(x, x)
F = np.sin(X)*np.cos(Y)
v = np.linspace(-2, 2,10)
plt.contourf(X, Y, F, 500)
cb = plt.colorbar()
F ranges from -1 to 1 so my colorbar has these as its minimum and maximum values. I want the colorbar to range from -2 to 2. I have tried to set the ticks to range from -2 to 2 however this does not work. Any suggestions?
works if you create the colorbar manually:
import numpy as np
import matplotlib as mpl
x = np.linspace(0,10)
X, Y = np.meshgrid(x, x)
F = np.sin(X)*np.cos(Y)
v = np.linspace(-2, 2,10)
f, ax = plt.subplots()
cont = ax.contourf(X, Y, F, 500, vmin=-2, vmax=2, ticks=v)
cax, _ = mpl.colorbar.make_axes(ax)
cbar = mpl.colorbar.ColorbarBase(cax, cmap=cont.cmap, norm=cont.norm)
cbar.set_ticks([v.min(), *np.linspace(F.min(), F.max(), 11), v.max()])
These meshgrid is a little confusing to use for me. I'm trying to do a scatter plot with the x and y coordinates with a contour plot overlaid on the scatter with a continuous spread for the z coordinates. Similar to an elevation map.
If I use meshgrid with the x,y, and z coordinates then I get 3D array for each which is still the incorrect input.
df_xyz = pd.read_table("https://pastebin.com/raw/f87krHFK", sep="\t", index_col=0)
x = df_xyz.iloc[:,0].values
y = df_xyz.iloc[:,1].values
z = df_xyz.iloc[:,2].values
XX, YY = np.meshgrid(x,y)
with plt.style.context("seaborn-white"):
fig, ax = plt.subplots(figsize=(13,8))
ax.scatter(x,y, color="black", linewidth=1, edgecolor="ivory", s=50)
ax.contourf(XX,YY,z)
# TypeError: Input z must be a 2D array.
XX, YY, ZZ = np.meshgrid(x,y,z)
with plt.style.context("seaborn-white"):
fig, ax = plt.subplots(figsize=(13,8))
ax.scatter(x,y, color="black", linewidth=1, edgecolor="ivory", s=50)
ax.contourf(XX,YY,ZZ)
# TypeError: Input z must be a 2D array.
Here's my current output:
I am trying to do something similar to this:
import pandas as pd
import numpy as np
from scipy.interpolate import griddata
import matplotlib.pyplot as plt
%matplotlib inline
df_xyz = pd.read_table("https://pastebin.com/raw/f87krHFK", sep="\t", index_col=0)
x = df_xyz.iloc[:,0].values
y = df_xyz.iloc[:,1].values
z = df_xyz.iloc[:,2].values
def plot_contour(x,y,z,resolution = 50,contour_method='linear'):
resolution = str(resolution)+'j'
X,Y = np.mgrid[min(x):max(x):complex(resolution), min(y):max(y):complex(resolution)]
points = [[a,b] for a,b in zip(x,y)]
Z = griddata(points, z, (X, Y), method=contour_method)
return X,Y,Z
X,Y,Z = plot_contour(x,y,z,resolution = 50,contour_method='linear')
with plt.style.context("seaborn-white"):
fig, ax = plt.subplots(figsize=(13,8))
ax.scatter(x,y, color="black", linewidth=1, edgecolor="ivory", s=50)
ax.contourf(X,Y,Z)
If I have specific x and y values corresponding to a z value separated by array, how would I make a contour plot? For example:
Array 1 (X):
1
4
6
7
8
2
6
Array 2 (Y):
7
7
8
9
0
1
2
Array 3 (Z):
8
9
7
1
2
2
3
Would I have to do X1, Y1 = np.meshgrid(X, Y) and shape the Z array somehow? Is there another way to do this without using meshgrid? Also, if I add a fourth array and name it Z1 with the same x and y values corresponding to a particular Z1, can I plot this contour plot together with the first contour plot?
If you do not have a regular grid, using triangular surface interpolation may be a good choice.
In this example and the above one, if you have longer data, you only have to check the boundary of the plot.
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import matplotlib.tri as tri
sns.set(style="white")
x = np.array([1,4,6,7,8,2,6])
y = np.array([7,7,8,9,0,1,2])
z = np.array([8,9,7,1,2,2,3])
fig = plt.figure(figsize=(10, 10))
ax = fig.add_subplot(111)
nptsx, nptsy = 100, 100
xg, yg = np.meshgrid(np.linspace(x.min(), x.max(), nptsx),
np.linspace(y.min(), y.max(), nptsy))
triangles = tri.Triangulation(x, y)
tri_interp = tri.CubicTriInterpolator(triangles, z)
zg = tri_interp(xg, yg)
# change levels here according to your data
levels = np.linspace(0, 10, 5)
colormap = ax.contourf(xg, yg, zg, levels,
cmap=plt.cm.Blues,
norm=plt.Normalize(vmax=z.max(), vmin=z.min()))
# plot data points
ax.plot(x, y, color="#444444", marker="o", linestyle="", markersize=10)
# add a colorbar
fig.colorbar(colormap,
orientation='vertical', # horizontal colour bar
shrink=0.85)
# graph extras: look at xlim and ylim
ax.set_xlim((0, 10))
ax.set_ylim((0, 10))
ax.set_aspect("equal", "box")
plt.show()
This is the output :
I think you need to do an interpolation:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
from scipy import interpolate
x = np.array([1,4,6,7,8,2,6])
y = np.array([7,7,8,9,0,1,2])
z = np.array([8,9,7,1,2,2,3])
points = np.column_stack((x,y))
values = z.T
gridx, gridy = np.mgrid[0:8:100j, 0:8:100j]
gridz = interpolate.griddata(points, values, (gridx, gridy), method='cubic')
fig = plt.figure(figsize=(12,5))
ax1 = fig.add_subplot(121,projection='3d')
ax1.plot3D(x,y,z, 'k.', ms=10)
ax1.contour(gridx,gridy,gridz)
ax2 = fig.add_subplot(122,projection='3d')
ax2.plot3D(x,y,z, 'k.', ms=10)
ax2.plot_wireframe(gridx, gridy, gridz,rstride=5,cstride=5)
plt.savefig('contour_wire.png')
This gives: