Streamflow plot not matching mgrid - python

I am plotting a streamplot of a 2D ODE system using the Python code:
import numpy as np
import matplotlib.pyplot as plt
Y, X = np.mgrid[-4:4:100j, -4:4:100j]
U = -0.5*X - Y
V = X - Y
plt.streamplot(X, Y, U, V, density=[1, 1])
plt.show()
However I am getting the following plot where it is extending the x and y axis by an additional unit, 5 instead of 4 even though the mgrid is -4 to 4 in both directions. I've tried different ranges but without success. Any ideas on how to confine the plot to -4:-4 and -4:4

You could use
ax.set(xlim=(-4,4), ylim=(-4,4))
to manually set the limits:
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
Y, X = np.mgrid[-4:4:100j, -4:4:100j]
U = -0.5*X - Y
V = X - Y
ax.streamplot(X, Y, U, V, density=[1, 1])
ax.set(xlim=(-4,4), ylim=(-4,4))
plt.show()

Related

Evaluating and plotting a function z = f(x,y) with different array size of x and y

Although it's a notebook question, but I am unable to get it correct, my problem is:
I have a function y ranging from 0 to 5.3 with 130 divisions
I have a function z ranging from 0 to 0.3 with 48 divisions
I wanted to plot a function v such that:
v = cos(2* \pi *z)*sin(\pi *y)
I tried to do with Python using the following code:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import math
y = np.arange(0, 5.3, 0.007692)
z = np.arange(0,0.3,0.021)
v = np.cos(2.0*math.pi*z)*np.sin(math.pi*y)
fig = plt.figure(figsize=(6, 6))
ax = fig.add_subplot(111, projection='3d')
ax.scatter( y, z, v,
linewidths=1, alpha=.7,
edgecolor='k',
s = 200,
c=v)
plt.show()
By looking at the problem or at the code itself it's clear that the array size of y and z are different and correspondingly the function "v" could not be evaluated correctly and thus I am getting the following error:
v = np.cos(2.0*math.pi*z)*np.sin(math.pi*y)
ValueError: operands could not be broadcast together with shapes (15,) (690,)
I am unable to get it fixed, also I tried to make different arrays for y and z and then using two different loops for y and z evaluated the value for function z, but again I could not do it correctly. Could any one please help.
Using useful comment by #tmdavison https://stackoverflow.com/users/588071/tmdavison I used the np.meshgrid I got the following contour, which is close to, what I was expecting, but is it possible to get the 3D plot of y,z,v ? The updated code is given as:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import math
y = np.arange(0, 5.3, 0.007692)
z = np.arange(0,0.3,0.021)
xx, yy = np.meshgrid(y, z, sparse=True)
v = np.cos(2.0*math.pi*xx)*np.sin(math.pi*yy)
h = plt.contourf(y,z,v)
plt.colorbar()
plt.show()
fig = plt.figure(figsize=(6, 6))
ax = fig.add_subplot(111, projection='3d')
ax.scatter( y, z, v,
linewidths=1, alpha=.7,
edgecolor='k',
s = 200,
c=v)
plt.show()
But it is giving me error which says:
ValueError: shape mismatch: objects cannot be broadcast to a single shape

3D surface graph with matplotlib using dataframe columns to input the data

I have a spreadsheet file that I would like to input to create a 3D surface graph using Matplotlib in Python.
I used plot_trisurf and it worked, but I need the projections of the contour profiles onto the graph that I can get with the surface function, like this example.
I'm struggling to arrange my Z data in a 2D array that I can use to input in the plot_surface method. I tried a lot of things, but none seems to work.
Here it is what I have working, using plot_trisurf
import matplotlib
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import pandas as pd
df=pd.read_excel ("/Users/carolethais/Desktop/Dissertação Carol/Códigos/Resultados/res_02_0.5.xlsx")
fig = plt.figure()
ax = fig.gca(projection='3d')
# I got the graph using trisurf
graf=ax.plot_trisurf(df["Diametro"],df["Comprimento"], df["temp_out"], cmap=matplotlib.cm.coolwarm)
ax.set_xlim(0, 0.5)
ax.set_ylim(0, 100)
ax.set_zlim(25,40)
fig.colorbar(graf, shrink=0.5, aspect=15)
ax.set_xlabel('Diâmetro (m)')
ax.set_ylabel('Comprimento (m)')
ax.set_zlabel('Temperatura de Saída (ºC)')
plt.show()
This is a part of my df, dataframe:
Diametro Comprimento temp_out
0 0.334294 0.787092 34.801994
1 0.334294 8.187065 32.465551
2 0.334294 26.155976 29.206090
3 0.334294 43.648591 27.792126
4 0.334294 60.768219 27.163233
... ... ... ...
59995 0.437266 14.113660 31.947302
59996 0.437266 25.208851 30.317583
59997 0.437266 33.823035 29.405461
59998 0.437266 57.724209 27.891616
59999 0.437266 62.455890 27.709298
I tried this approach to use the imported data with plot_surface, but what I got was indeed a graph but it didn't work, here it's the way the graph looked with this approach:
Thank you so much
A different approach, based on re-gridding the data, that doesn't require that the original data is specified on a regular grid [deeply inspired by this example;-].
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.tri as tri
from mpl_toolkits.mplot3d import Axes3D
np.random.seed(19880808)
# compute the sombrero over a cloud of random points
npts = 10000
x, y = np.random.uniform(-5, 5, npts), np.random.uniform(-5, 5, npts)
z = np.cos(1.5*np.sqrt(x*x + y*y))/(1+0.33*(x*x+y*y))
# prepare the interpolator
triang = tri.Triangulation(x, y)
interpolator = tri.LinearTriInterpolator(triang, z)
# do the interpolation
xi = yi = np.linspace(-5, 5, 101)
Xi, Yi = np.meshgrid(xi, yi)
Zi = interpolator(Xi, Yi)
# plotting
fig = plt.figure()
ax = fig.gca(projection='3d')
norm = plt.Normalize(-1,1)
ax.plot_surface(Xi, Yi, Zi,
cmap='inferno',
norm=plt.Normalize(-1,1))
plt.show()
plot_trisurf expects x, y, z as 1D arrays while plot_surface expects X, Y, Z as 2D arrays or as x, y, Z with x, y being 1D array and Z a 2D array.
Your data consists of 3 1D arrays, so plotting them with plot_trisurf is immediate but you need to use plot_surface to be able to project the isolines on the coordinate planes... You need to reshape your data.
It seems that you have 60000 data points, in the following I assume that you have a regular grid 300 points in the x direction and 200 points in y — but what is important is the idea of regular grid.
The code below shows
the use of plot_trisurf (with a coarser mesh), similar to your code;
the correct use of reshaping and its application in plot_surface;
note that the number of rows in reshaping corresponds to the number
of points in y and the number of columns to the number of points in x;
and 4. incorrect use of reshaping, the resulting subplots are somehow
similar to the plot you showed, maybe you just need to fix the number
of row and columns.
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
x, y = np.arange(30)/3.-5, np.arange(20)/2.-5
x, y = (arr.flatten() for arr in np.meshgrid(x, y))
z = np.cos(1.5*np.sqrt(x*x + y*y))/(1+0.1*(x*x+y*y))
fig, axes = plt.subplots(2, 2, subplot_kw={"projection" : "3d"})
axes = iter(axes.flatten())
ax = next(axes)
ax.plot_trisurf(x,y,z, cmap='Reds')
ax.set_title('Trisurf')
X, Y, Z = (arr.reshape(20,30) for arr in (x,y,z))
ax = next(axes)
ax.plot_surface(X,Y,Z, cmap='Reds')
ax.set_title('Surface 20×30')
X, Y, Z = (arr.reshape(30,20) for arr in (x,y,z))
ax = next(axes)
ax.plot_surface(X,Y,Z, cmap='Reds')
ax.set_title('Surface 30×20')
X, Y, Z = (arr.reshape(40,15) for arr in (x,y,z))
ax = next(axes)
ax.plot_surface(X,Y,Z, cmap='Reds')
ax.set_title('Surface 40×15')
plt.tight_layout()
plt.show()

Boolean masking of arrays in 3D surface plot destroys colormap

I try to 3D-plot function fun and use colormap to show the level of function values. I'd like to plot this function on a non-sqaured area and hence I used boolean mask to set np.nan to certain values in meshgrid. But I got
RuntimeWarning: invalid value encountered in less
cbook._putmask(xa, xa < 0.0, -1)
whenever I added boolean mask. It seems the bug is due to that np.nan cannot be compared in colormap. But I can't find a way to fix this.
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
%matplotlib inline
fun = lambda x: np.sin(x[0])*np.exp(1-np.cos(x[1]))**2 + np.cos(x[1])*np.exp(1-np.sin(x[0]))**2 + (x[0]-x[1])**2
fig = plt.figure(figsize=(8, 5))
ax = fig.gca(projection='3d')
x = np.arange(-6, 6, 3e-2)
y = np.arange(-6, 6, 3e-2)
# A constraint on x and y
x, y = np.meshgrid(x, y)
r2 = (x+5)**2 + (y+5)**2
scope = r2 < 25
# Mask is the cause of the problem
x[scope] = np.nan
y[scope] = np.nan
z = fun(np.array([x, y]))
surf=ax.plot_surface(x, y, z, cmap=cm.jet)
ax.contourf(x, y, z, offset=-120, cmap=cm.jet)
fig.colorbar(surf)
ax.view_init(elev=30, azim=60)
You cannot fix the runtime warning. It's a warning based on the fact that there are nan values in the array.
In order to still get a colorcoded surface plot, you can however use a matplotlib.colors.Normalize instance to tell the surface plot which colors to use.
See full code below:
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
import matplotlib.colors
fun = lambda x: np.sin(x[0])*np.exp(1-np.cos(x[1]))**2 + np.cos(x[1])*np.exp(1-np.sin(x[0]))**2 + (x[0]-x[1])**2
fig = plt.figure(figsize=(8, 5))
ax = fig.gca(projection='3d')
x = np.arange(-6, 6, 3e-2)
y = np.arange(-6, 6, 3e-2)
# A constraint on x and y
x, y = np.meshgrid(x, y)
r2 = (x+5)**2 + (y+5)**2
scope = r2 < 25
# Mask is the cause of the problem
x[scope] = np.nan
y[scope] = np.nan
z = fun(np.array([x, y]))
norm = matplotlib.colors.Normalize(vmin=-120, vmax=120)
cm.jet.set_under((0,0,0,0))
ax.contourf(x, y, z, offset=-120, cmap=cm.jet, norm=norm)
surf=ax.plot_surface(x, y, z, cmap=cm.jet, norm=norm)
fig.colorbar(surf)
#ax.view_init(elev=30, azim=60)
plt.show()

How do I create a 3D line plot in matplotlib from the data in arrays?

I have numerically solved the Lorenz equations using SciPy with the script:
# Lorenz Equations SciPy solver
import numpy as np
from scipy import integrate
from math import cos
from matplotlib import pyplot as plt
a, b = 0, 100
sigma, rho, beta = 10, 28, 8/3
N = 1000000
h = (b-a) / float(N)
def solvr(Y, t):
return [sigma*(Y[1]-Y[0]), Y[0]*(rho-Y[2])-Y[1], Y[0]*Y[1]-beta*Y[2]]
t = np.arange(a, b, h)
asol = integrate.odeint(solvr, [0, 1, 1], t)
x = asol[:,0]
y = asol[:,1]
z = asol[:,2]
Now what I would like to do is plot x, y and z (which are all Numpy ndarrays, if you are unsure) against each other in a 3D line (or wireframe) plot. I think this would have to be done using matplotlib, but I am not picky, so long as you give me a solution that will plot the data in 3D I do not care what modules I need to import.
Here is the Lorenz attractor both in 3D and animated. The script is in the following link (along with many goodies) in Jake VanderPlas' Pythonic Perambulations. You can learn a lot by going line-by-line through the script - it's an elegant use of matplotlib objects.
https://jakevdp.github.io/blog/2013/02/16/animating-the-lorentz-system-in-3d/
I added these two lines just before return in the animate function, and then used ImageJ to import the "image stack" and save the "animated GIF":
fname = "Astro_Jake_" + str(i+10000)[1:]
fig.savefig(fname)
Note: For OSX it seems to be necessary to set blit = False in animation.FuncAnimation.
Here is a minimal, simplified example of plotting lines in 3D based on the above:
def lorentz_deriv((x, y, z), t0, sigma=10., beta=8./3, rho=28.0):
"""Compute the time-derivative of a Lorentz system."""
return [sigma * (y - x), x * (rho - z) - y, x * y - beta * z]
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from scipy.integrate import odeint as ODEint
x = np.linspace(0, 20, 1000)
y, z = 10.*np.cos(x), 10.*np.sin(x) # something simple
fig = plt.figure()
ax = fig.add_subplot(1,2,1,projection='3d')
ax.plot(x, y, z)
# now Lorentz
times = np.linspace(0, 4, 1000)
start_pts = 30. - 15.*np.random.random((20,3)) # 20 random xyz starting values
trajectories = []
for start_pt in start_pts:
trajectory = ODEint(lorentz_deriv, start_pt, times)
trajectories.append(trajectory)
ax = fig.add_subplot(1,2,2,projection='3d')
for trajectory in trajectories:
x, y, z = trajectory.T # transpose and unpack
# x, y, z = zip(*trajectory) # this also works!
ax.plot(x, y, z)
plt.show()
There is a brief example / tutorial on how to do wireframe plots (as well as 3d scatter) at the matplotlib site http://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html#wireframe-plots

3D plots using maplot3d from matplotlib-

I have to plot data which is in the following format :
x = range(6)
y = range(11)
and z depends on x, y
For each value of x, there should be a continuous curve that shows the variation of z w.r.t y and the curves for different values of x must be disconnected
I am using mplot3d and it is not very clear how to plot disconnected curves.
This is what it looks like using bar plots.
You could overlay multiple plots using Axes3D.plot:
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d.axes3d as axes3d
import numpy as np
x = np.arange(6)
y = np.linspace(0, 11, 50)
z = x[:, np.newaxis] + y**2
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection = '3d')
for xval, zrow in zip(x, z):
ax.plot(xval*np.ones_like(y), y, zrow, color = 'black')
plt.show()

Categories

Resources