Matplotlib plot contourf on 3d surface - python

I am trying to use the colormap feature of a 3d-surface plot in matplotlib to color the surface based on values from another array instead of the z-values.
The surface plot is created and displayed as follows:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
def gauss(x, y, w_0):
r = np.sqrt(x**2 + y**2)
return np.exp(-2*r**2 / w_0**2)
x = np.linspace(-100, 100, 100)
y = np.linspace(-100, 100, 100)
X, Y = np.meshgrid(x, y)
Z = gauss(X, Y, 50)
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.plot_surface(X, Y, Z, cmap='jet')
Now instead of coloring based on elevation of the 3d-surface, I am looking to supply the color data for the surface in form of another array, here as an example a random one:
color_data = np.random.uniform(0, 1, size=(Z.shape))
However, I did not find a solution to colorize the 3d-surface based on those values. Ideally, it would look like a contourf plot in 3d, just on the 3d surface.

You can use matplotlib.colors.from_levels_and_colors to obtain a colormap and normalization, then apply those to the values to be colormapped.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.colors
x = np.linspace(-100, 100, 101)
y = np.linspace(-100, 100, 101)
X, Y = np.meshgrid(x, y)
Z = np.exp(-2*np.sqrt(X**2 + Y**2)**2 / 50**2)
c = X+50*np.cos(Y/20) # values to be colormapped
N = 11 # Number of level (edges)
levels = np.linspace(-150,150,N)
colors = plt.cm.get_cmap("RdYlGn", N-1)(np.arange(N-1))
cmap, norm = matplotlib.colors.from_levels_and_colors(levels, colors)
color_vals = cmap(norm(c))
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.plot_surface(X, Y, Z, facecolors=color_vals, rstride=1, cstride=1)
plt.show()

Related

How to plot property distribution with interpolation?

I have a dataframe like this:
import random
import matplotlib.pyplot as plt
plt.style.use('ggplot')
fig = plt.figure(figsize=(16,8))
import pandas as pd
data = pd.DataFrame({"X":random.sample(range(530000, 560000), 60),
"Y":random.sample(range(8580000, 8620000), 60),
"PROPERTY":random.choices(range(0, 30), k=60)})
I saw an example where I could plot my PROPERTY along X and Y coordinates as a triangle spatial distribution:
x = data["X"]
y = data["Y"]
z = data["PROPERTY"]
# Plot Triangular Color Filled Contour
plt.tricontourf(x, y, z, cmap="rainbow")
plt.colorbar()
plt.tricontour(x, y, z)
# Set well shapes
plt.scatter(x, y, color='black')
plt.xlabel("X")
plt.ylabel("Y")
Althoug I would like to plot it as a different map type, not with these abrupt data transitions. Maybe like kriging or smooth interpolation like this example:
Anyone could show me an example?
I used the pykrige package to interpolate the point data into a grid field.
The code and output figure are here.
import random
import matplotlib.pyplot as plt
plt.style.use('ggplot')
fig = plt.figure(figsize=(6,4))
import pandas as pd
from pykrige import OrdinaryKriging
import numpy as np
random.seed(100)
data = pd.DataFrame({"X":random.sample(range(530000, 560000), 60),
"Y":random.sample(range(8580000, 8620000), 60),
"PROPERTY":random.choices(range(0, 30), k=60)})
x = data["X"]
y = data["Y"]
z = data["PROPERTY"]
x1 = np.linspace(530000.,560000,700)
y1 = np.linspace(8580000,8620000,400)
dict1= {'sill': 1, 'range': 6500.0, 'nugget': .1}
OK = OrdinaryKriging(x,y,z,variogram_model='gaussian',
variogram_parameters=dict1,nlags=6)
zgrid,ss = OK.execute('grid',x1,y1)
xgrid,ygrid = np.meshgrid(x1,y1)
# Plot Triangular Color Filled Contour
# plt.tricontourf(x, y, z, cmap="rainbow")
plt.contourf(xgrid, ygrid, zgrid, cmap="rainbow")
plt.colorbar()
# Set well shapes
plt.scatter(x, y, color='black')
plt.xlabel("X")
plt.ylabel("Y")

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()

Contour graph in python

How would I make a countour grid in python using matplotlib.pyplot, where the grid is one colour where the z variable is below zero and another when z is equal to or larger than zero? I'm not very familiar with matplotlib so if anyone can give me a simple way of doing this, that would be great.
So far I have:
x= np.arange(0,361)
y= np.arange(0,91)
X,Y = np.meshgrid(x,y)
area = funcarea(L,D,H,W,X,Y) #L,D,H and W are all constants defined elsewhere.
plt.figure()
plt.contourf(X,Y,area)
plt.show()
You can do this using the levels keyword in contourf.
import numpy as np
import matplotlib.pyplot as plt
fig, axs = plt.subplots(1,2)
x = np.linspace(0, 1, 100)
X, Y = np.meshgrid(x, x)
Z = np.sin(X)*np.sin(Y)
levels = np.linspace(-1, 1, 40)
zdata = np.sin(8*X)*np.sin(8*Y)
cs = axs[0].contourf(X, Y, zdata, levels=levels)
fig.colorbar(cs, ax=axs[0], format="%.2f")
cs = axs[1].contourf(X, Y, zdata, levels=[-1,0,1])
fig.colorbar(cs, ax=axs[1])
plt.show()
You can change the colors by choosing and different colormap; using vmin, vmax; etc.

matplotlib contourf: get Z value under cursor

When I plot something with contourf, I see at the bottom of the plot window the current x and y values under the mouse cursor.
Is there a way to see also the z value?
Here an example contourf:
import matplotlib.pyplot as plt
import numpy as hp
plt.contourf(np.arange(16).reshape(-1,4))
The text that shows the position of the cursor is generated by ax.format_coord. You can override the method to also display a z-value. For instance,
import matplotlib.pyplot as plt
import numpy as np
import scipy.interpolate as si
data = np.arange(16).reshape(-1, 4)
X, Y = np.mgrid[:data.shape[0], :data.shape[1]]
cs = plt.contourf(X, Y, data)
func = si.interp2d(X, Y, data)
def fmt(x, y):
z = np.take(func(x, y), 0)
return 'x={x:.5f} y={y:.5f} z={z:.5f}'.format(x=x, y=y, z=z)
plt.gca().format_coord = fmt
plt.show()
The documentation example shows how you can insert z-value labels into your plot
Script: http://matplotlib.sourceforge.net/mpl_examples/pylab_examples/contour_demo.py
Basically, it's
plt.figure()
CS = plt.contour(X, Y, Z)
plt.clabel(CS, inline=1, fontsize=10)
plt.title('Simplest default with labels')
Just a variant of wilywampa's answer. If you already have a pre-computed grid of interpolated contour values because your data is sparse or if you have a huge data matrix, this might be suitable for you.
import matplotlib.pyplot as plt
import numpy as np
resolution = 100
Z = np.arange(resolution**2).reshape(-1, resolution)
X, Y = np.mgrid[:Z.shape[0], :Z.shape[1]]
cs = plt.contourf(X, Y, Z)
Xflat, Yflat, Zflat = X.flatten(), Y.flatten(), Z.flatten()
def fmt(x, y):
# get closest point with known data
dist = np.linalg.norm(np.vstack([Xflat - x, Yflat - y]), axis=0)
idx = np.argmin(dist)
z = Zflat[idx]
return 'x={x:.5f} y={y:.5f} z={z:.5f}'.format(x=x, y=y, z=z)
plt.colorbar()
plt.gca().format_coord = fmt
plt.show()
Ex:

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