How could I get the desired matplotlib 3d plot style? - python

I copied a snippet from here and run it but didn't get the desired style.
Code for reproduction
#!/usr/bin/evn python
import numpy as np
import scipy.linalg
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
# some 3-dim points
mean = np.array([0.0, 0.0, 0.0])
cov = np.array([[1.0, -0.5, 0.8], [-0.5, 1.1, 0.0], [0.8, 0.0, 1.0]])
data = np.random.multivariate_normal(mean, cov, 50)
# regular grid covering the domain of the data
X, Y = np.meshgrid(np.arange(-3.0, 3.0, 0.5), np.arange(-3.0, 3.0, 0.5))
XX = X.flatten()
YY = Y.flatten()
order = 1 # 1: linear, 2: quadratic
if order == 1:
# best-fit linear plane
A = np.c_[data[:, 0], data[:, 1], np.ones(data.shape[0])]
C, _, _, _ = scipy.linalg.lstsq(A, data[:, 2]) # coefficients
# evaluate it on grid
Z = C[0] * X + C[1] * Y + C[2]
# or expressed using matrix/vector product
#Z = np.dot(np.c_[XX, YY, np.ones(XX.shape)], C).reshape(X.shape)
elif order == 2:
# best-fit quadratic curve
A = np.c_[np.ones(data.shape[0]), data[:, :2],
np.prod(data[:, :2], axis=1), data[:, :2]**2]
C, _, _, _ = scipy.linalg.lstsq(A, data[:, 2])
# evaluate it on a grid
Z = np.dot(np.c_[np.ones(XX.shape), XX, YY, XX * YY, XX**2, YY**2],
C).reshape(X.shape)
# plot points and fitted surface
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_surface(X, Y, Z, rstride=1, cstride=1, alpha=0.2)
ax.scatter(data[:, 0], data[:, 1], data[:, 2], c='r', s=50)
plt.xlabel('X')
plt.ylabel('Y')
ax.set_zlabel('Z')
ax.axis('equal')
ax.axis('tight')
plt.show()
Actual outcome
see this link
Expected outcome
see this link
The two styles are very different: the grid color, the wireframe, the surface color, etc. Is the style of this image from previous version of matplotlib? If so, how could I get that style?
Matplotlib version
Operating system: Linux Mint 18.3
Matplotlib version: 2.2.2
Matplotlib backend: Qt4Agg
Python version: 2.7.12
I installed matplotlib via pip in a virtual environment.

in my Python 3.5, matplotlib 2.2.2 installation plt.style.use('classic') seems to work
Why matplotlib graphs and icons look different on two computers with the same OS? is similar but the Q was about icons

Related

How to find out the value y for a specific x after fitting a polynomial line using polyfit?

I have fitted a polynomial line on a graph using poly1D. How can I determine the value of y of this polynomial line for a specific value of x?
draw_polynomial = np.poly1d(np.polyfit(x, y, 8))
polyline = np.linspace(min_x, max_x, 300)
plt.plot(polyline, draw_polynomial(polyline), color='purple')
plt.show()
Here, I want to find out the y if x = 6.
You can directly call the fitted result p (polyline in your case) to get the y value. For example, x_val = 3.5, y_val_interp = round(p(x_val), 2) will give a y value of -0.36 in the code example below. I also added some annotations to visualize the result better.
import numpy as np
import numpy.polynomial.polynomial as npp
import matplotlib.pyplot as plt
# Since numpy version 1.4, the new polynomial API
# defined in numpy.polynomial is preferred.
x = np.array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0])
y = np.array([0.0, 0.8, 0.9, 0.1, -0.8, -1.0])
z = npp.polyfit(x, y, 4)
p = np.poly1d(np.flip(z))
xp = np.linspace(-2, 6, 100)
plt.plot(x, y, '.', markersize=12, zorder=2.01)
plt.plot(xp, p(xp), '-')
plt.xlim(-1, 6)
plt.ylim(-1.5, 1)
# interrupting y value based on x value
x_val = 3.5
y_val_interp = round(p(x_val), 2)
# add dashed lines
plt.plot([x_val, xp[0]], [y_val_interp, y_val_interp], '--', color='k')
plt.plot([x_val, x_val], [p(xp[0]), y_val_interp], '--', color='k')
# add annotation and marker
plt.annotate(f'(x={x_val}, y={y_val_interp})', (x_val, y_val_interp), size=12, xytext=(x_val * 1.05, y_val_interp))
plt.plot(x_val, y_val_interp, 'o', color='r', zorder=2.01)
print(f'x = {x_val}, y = {y_val_interp}')
plt.tight_layout()
plt.show()
References:
https://numpy.org/doc/stable/reference/generated/numpy.polyfit.html
https://numpy.org/doc/stable/reference/generated/numpy.polynomial.polynomial.Polynomial.fit.html#numpy.polynomial.polynomial.Polynomial.fit
https://numpy.org/doc/stable/reference/generated/numpy.poly1d.html

Why can't a quiver be drawn over a surface in matplotlib?

I am trying to draw 3 arrows over a surface using quiver. The arrows seem to always be draw behind the surface. This is the result:
And this is the code to generate this result:
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
def fun(x, y):
return x ** 2 - y ** 2
if __name__ == '__main__':
fig = plt.figure(dpi=160)
ax = fig.add_subplot(111, projection='3d')
x = y = np.arange(-3.0, 3.0, 0.05)
X, Y = np.meshgrid(x, y)
zs = np.array(fun(np.ravel(X), np.ravel(Y)))
Z = zs.reshape(X.shape)
ax.plot_surface(X, Y, Z, cmap=plt.get_cmap('Blues'))
ax.quiver([0], [0], [1], [0, -1, 0], [-1, 0, 0], [0, 0, 2.5], lw=4, color=['r', 'g', 'b']) # The z is 1 unit above the surface
ax.set_xlim3d(-3.5, 3.5)
ax.set_ylim3d(-3.5, 3.5)
ax.set_zlim3d(-8.5, 8.5)
plt.show()
How do I draw these arrows over a surface? I am using matplotlib 3.1.1, which is the latest version at the time of this question.
A hacky you solution you can use, while not ideal, is reduce the alpha of the surface.
ax.plot_surface(X, Y, Z, cmap=plt.get_cmap('Blues'), alpha=0.5)

Get best linear function which approximate some dots in 3D

I have 4 dots which are represented with these coordinates:
X = [0.1, 0.5, 0.9, 0.18]
Y = [0.7, 0.5, 0.7, 0.3]
Z = [4.2, 3.3, 4.2, 2.5]
and I have to get the best linear function (plane) which approximate these 4 dots.
I'm aware of numpy.polyfit, but polyfitworks only with x and y (2D),
What can I do?
while not completely general, if the the data points can be reasonably represented as a surface relative to a coordinate plane, say z = ax + by + c then np.linalg.lstsq can be used
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
X = np.array([0.1, 0.5, 0.9, 0.18])
Y = np.array([0.7, 0.5, 0.7, 0.3])
Z = np.array([4.2, 3.3, 4.2, 2.5])
# least squares fit
A = np.vstack([X, Y, np.ones(len(X))]).T
a,b,c= np.linalg.lstsq(A, Z)[0]
# plots
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# plot data as big red crosses
ax.scatter(X, Y, Z, color='r', marker='+', linewidth=10)
# plot plane fit as grid of green dots
xs = np.linspace(min(X), max(X), 10)
ys = np.linspace(min(Y), max(Y), 10)
xv, yv = np.meshgrid(xs, ys)
zv = a*xv + b*yv + c
ax.scatter(xv, yv, zv, color = 'g')
# ax.plot_wireframe(xv, yv, zv, color = 'g') # alternative fit plane plot
plt.show()
plotting the data 1st, you could select a different coordinate pair for the "independent variable" plane to avoid ill conditioned result if necessary, if the data points appeared to lie in a plane containing the z axis, then use xz or yz
and of course you could have degenerate points on a line or the vertices of a regular tetrahedron
for a better "geometric fit" the 1st fitted plane could be used as the base for a 2nd least square fit of the data rotated into that coordinate system (if the data is "reasonably" plane like)

Plot normal distribution in 3D

I am trying to plot the comun distribution of two normal distributed variables.
The code below plots one normal distributed variable. What would the code be for plotting two normal distributed variables?
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.mlab as mlab
import math
mu = 0
variance = 1
sigma = math.sqrt(variance)
x = np.linspace(-3, 3, 100)
plt.plot(x,mlab.normpdf(x, mu, sigma))
plt.show()
It sounds like what you're looking for is a Multivariate Normal Distribution. This is implemented in scipy as scipy.stats.multivariate_normal. It's important to remember that you are passing a covariance matrix to the function. So to keep things simple keep the off diagonal elements as zero:
[X variance , 0 ]
[ 0 ,Y Variance]
Here is an example using this function and generating a 3D plot of the resulting distribution. I add the colormap to make seeing the curves easier but feel free to remove it.
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import multivariate_normal
from mpl_toolkits.mplot3d import Axes3D
#Parameters to set
mu_x = 0
variance_x = 3
mu_y = 0
variance_y = 15
#Create grid and multivariate normal
x = np.linspace(-10,10,500)
y = np.linspace(-10,10,500)
X, Y = np.meshgrid(x,y)
pos = np.empty(X.shape + (2,))
pos[:, :, 0] = X; pos[:, :, 1] = Y
rv = multivariate_normal([mu_x, mu_y], [[variance_x, 0], [0, variance_y]])
#Make a 3D plot
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_surface(X, Y, rv.pdf(pos),cmap='viridis',linewidth=0)
ax.set_xlabel('X axis')
ax.set_ylabel('Y axis')
ax.set_zlabel('Z axis')
plt.show()
Giving you this plot:
Edit the method used below was deprecated in Matplotlib v2.2 and removed in v3.1
A simpler version is available through matplotlib.mlab.bivariate_normal
It takes the following arguments so you don't need to worry about matrices
matplotlib.mlab.bivariate_normal(X, Y, sigmax=1.0, sigmay=1.0, mux=0.0, muy=0.0, sigmaxy=0.0)
Here X, and Y are again the result of a meshgrid so using this to recreate the above plot:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.mlab import bivariate_normal
from mpl_toolkits.mplot3d import Axes3D
#Parameters to set
mu_x = 0
sigma_x = np.sqrt(3)
mu_y = 0
sigma_y = np.sqrt(15)
#Create grid and multivariate normal
x = np.linspace(-10,10,500)
y = np.linspace(-10,10,500)
X, Y = np.meshgrid(x,y)
Z = bivariate_normal(X,Y,sigma_x,sigma_y,mu_x,mu_y)
#Make a 3D plot
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_surface(X, Y, Z,cmap='viridis',linewidth=0)
ax.set_xlabel('X axis')
ax.set_ylabel('Y axis')
ax.set_zlabel('Z axis')
plt.show()
Giving:
The following adaption to #Ianhi's code above returns a contour plot version of the 3D plot above.
import matplotlib.pyplot as plt
from matplotlib import style
style.use('fivethirtyeight')
import numpy as np
from scipy.stats import multivariate_normal
#Parameters to set
mu_x = 0
variance_x = 3
mu_y = 0
variance_y = 15
x = np.linspace(-10,10,500)
y = np.linspace(-10,10,500)
X,Y = np.meshgrid(x,y)
pos = np.array([X.flatten(),Y.flatten()]).T
rv = multivariate_normal([mu_x, mu_y], [[variance_x, 0], [0, variance_y]])
fig = plt.figure(figsize=(10,10))
ax0 = fig.add_subplot(111)
ax0.contour(X, Y, rv.pdf(pos).reshape(500,500))
plt.show()
While the other answers are great, I wanted to achieve similar results while also illustrating the distribution with a scatter plot of the sample.
More details can be found here: Python 3d plot of multivariate gaussian distribution
The results looks like:
And is generated using the following code:
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from scipy.stats import multivariate_normal
# Sample parameters
mu = np.array([0, 0])
sigma = np.array([[0.7, 0.2], [0.2, 0.3]])
rv = multivariate_normal(mu, sigma)
sample = rv.rvs(500)
# Bounds parameters
x_abs = 2.5
y_abs = 2.5
x_grid, y_grid = np.mgrid[-x_abs:x_abs:.02, -y_abs:y_abs:.02]
pos = np.empty(x_grid.shape + (2,))
pos[:, :, 0] = x_grid
pos[:, :, 1] = y_grid
levels = np.linspace(0, 1, 40)
fig = plt.figure()
ax = fig.gca(projection='3d')
# Removes the grey panes in 3d plots
ax.xaxis.set_pane_color((1.0, 1.0, 1.0, 0.0))
ax.yaxis.set_pane_color((1.0, 1.0, 1.0, 0.0))
ax.zaxis.set_pane_color((1.0, 1.0, 1.0, 0.0))
# The heatmap
ax.contourf(x_grid, y_grid, 0.1 * rv.pdf(pos),
zdir='z', levels=0.1 * levels, alpha=0.9)
# The wireframe
ax.plot_wireframe(x_grid, y_grid, rv.pdf(
pos), rstride=10, cstride=10, color='k')
# The scatter. Note that the altitude is defined based on the pdf of the
# random variable
ax.scatter(sample[:, 0], sample[:, 1], 1.05 * rv.pdf(sample), c='k')
ax.legend()
ax.set_title("Gaussian sample and pdf")
ax.set_xlim3d(-x_abs, x_abs)
ax.set_ylim3d(-y_abs, y_abs)
ax.set_zlim3d(0, 1)
plt.show()

axis limits for scatter plot not holding in matplotlib

I am trying to overlay a scatter plot onto a contour plot using matplotlib, which contains
plt.contourf(X, Y, XYprof.T, self.nLevels, extent=extentYPY, \
origin = 'lower')
if self.doScatter == True and len(xyScatter['y']) != 0:
plt.scatter(xyScatter['x'], xyScatter['y'], \
s=dSize, c=myColor, marker='.', edgecolor='none')
plt.xlim(-xLimHist, xLimHist)
plt.ylim(-yLimHist, yLimHist)
plt.xlabel(r'$x$')
plt.ylabel(r'$y$')
What ends up happening is the resulting plots extend to include all of the scatter points, which can exceed the limits for the contour plot. Is there any way to get around this?
I used the following example to try and replicate your problem. If left to default, the range for x and y was -3 to 3. I input the xlim and ylim so the range for both was -2 to 2. It worked.
import numpy as np
import matplotlib.pyplot as plt
from pylab import *
# the random data
x = np.random.randn(1000)
y = np.random.randn(1000)
fig = plt.figure(1, figsize=(5.5,5.5))
X, Y = meshgrid(x, y)
Z1 = bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = 10 * (Z1 - Z2)
origin = 'lower'
CS = contourf(x, y, Z, 10, # [-1, -0.1, 0, 0.1],
cmap=cm.bone,
origin=origin)
title('Nonsense')
xlabel('x-stuff')
ylabel('y-stuff')
# the scatter plot:
axScatter = plt.subplot(111)
axScatter.scatter(x, y)
# set axes range
plt.xlim(-2, 2)
plt.ylim(-2, 2)
show()

Categories