Plotting slices in 3D as heatmap - python

How can I visualize 4d data on python, for example i have data like this :
x,y,z = np.mgrid[0:10:10j,20:50:30j,-10:5:15j]
t = np.random.random((10,30,15))
and i want to visualize the data like this :
ps : i have try to use slice function on matlab like this
[x,y,z] = meshgrid(0:1:9,20:1:49,-10:1:4)
temp = rand(30,10,15);
xslice = 5; %can add more slice
yslice = 35;
zslice = 0;
slice(x, y, z, temp, xslice, yslice, zslice)

You can use plot_surface as proposed in this answer in a function like this:
import numpy as np
import scipy.interpolate
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# Plot slices of the data at the given coordinates
def plot_slices(x, y, z, data, xslice, yslice, zslice, ax=None):
if ax is None:
ax = plt.figure().add_subplot(111, projection='3d')
# Normalize data to [0, 1] range
vmin, vmax = data.min(), data.max()
data_n = (data - vmin) / (vmax - vmin)
# Take slices interpolating to allow for arbitrary values
data_x = scipy.interpolate.interp1d(x, data, axis=0)(xslice)
data_y = scipy.interpolate.interp1d(y, data, axis=1)(yslice)
data_z = scipy.interpolate.interp1d(z, data, axis=2)(zslice)
# Pick color map
cmap = plt.cm.plasma
# Plot X slice
xs, ys, zs = data.shape
xplot = ax.plot_surface(xslice, y[:, np.newaxis], z[np.newaxis, :],
rstride=1, cstride=1, facecolors=cmap(data_x), shade=False)
# Plot Y slice
yplot = ax.plot_surface(x[:, np.newaxis], yslice, z[np.newaxis, :],
rstride=1, cstride=1, facecolors=cmap(data_y), shade=False)
# Plot Z slice
zplot = ax.plot_surface(x[:, np.newaxis], y[np.newaxis, :], np.atleast_2d(zslice),
rstride=1, cstride=1, facecolors=cmap(data_z), shade=False)
return xplot, yplot, zplot
You would then use it like this:
import numpy as np
np.random.seed(0)
x = np.linspace(0, 10, 10)
y = np.linspace(20, 50, 30)
z = np.linspace(-10, 5, 15)
t = np.random.random((10, 30, 15))
ax = plt.figure().add_subplot(111, projection='3d')
plot_slices(x, y, z, t, 5, 35, 0, ax=ax)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
Output:
Unfortunately, Matplotlib doesn't handle intersecting 3D objects well and clipping is incorrect, but that is a different kind of issue.

Related

Fill a matplotlib contour plot

With the following code I have obtained the following contour map:
fig, ax = plt.subplots()
x = np.arange(431)
y = np.arange(225)
Y, X = np.meshgrid(y, x)
values = df["Appearance_percentage"].values
values2d = np.reshape(values, (431, 225))
ax.set_ylim(225, 0)
plt.style.use('seaborn-white')
ax.set_title('Mapa contour de probabilitat de trobar núvols')
plt.contour(X, Y, values2d, 30, cmap='RdGy')
plt.colorbar()
plt.savefig("contourmap.png")
I would like to know if I could fill the areas between the lines so that there are no white spaces in the color bar and the map is more attractive.
I tried doing df["Appearance_percentage_contourmap"] = round(df["Appearance_percentage"]) and then values = df["Appearance_percentage_contourmap"].values and I'm still obtaining the same map with lots of white areas.
Just replace plt.contour with plt.contourf, where the "f" at the end means "fill".
Here is an example:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(100)
y = np.arange(100)
Y, X = np.meshgrid(y, x)
values = np.outer(x, y)
fig, ax = plt.subplots()
plt.contourf(X, Y, values, 30, cmap='RdGy')
plt.colorbar()
You may also want to emphasis the contour lines with:
x = np.arange(100)
y = np.arange(100)
Y, X = np.meshgrid(y, x)
values = np.outer(x, y)
fig, ax = plt.subplots()
c1 = plt.contourf(X, Y, values, 30, cmap='RdGy')
c2 = plt.contour(X, Y, values, 30, cmap='Greys')
plt.colorbar(c1)
plt.contour() creates an isoline connecting all the places with an equal interpolated value. So, it searches places where the appearance is e.g. 6% and connects these with a line. If you set levels=30 there will be 30 such lines drawn. plt.contour() does a lot of effort to create a colorbar that shows the value for each line. If you don't want such a colorbar, you can create a custom colorbar using the same values.
You can create a custom colorbar as follows:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.cm import ScalarMappable
fig, ax = plt.subplots()
x = np.arange(431)
y = np.arange(225)
Y, X = np.meshgrid(y, x)
values = np.random.randn(431, 225).cumsum(axis=0).cumsum(axis=1).ravel()
values -= values.min()
values2d = np.reshape(values, (431, 225))
ax.set_ylim(225, 0)
plt.style.use('seaborn-white')
ax.set_title('Mapa contour de probabilitat de trobar núvols')
plt.contour(X, Y, values2d, 30, cmap='RdGy')
sm = ScalarMappable(norm=plt.Normalize(values.min(), values.max()), cmap='RdGy')
plt.colorbar(sm)
plt.show()
PS:Please don't round the values (as in df["Appearance_percentage_contourmap"] = round(df["Appearance_percentage"])), because that introduces artificial inaccuracies.

How to combine two matplotlib (python) colormaps from scatter plot

I am trying to combine two colourmap legends in one. Colour values are defined from third (z) data.
I am trying plot one legend colormap with two color scheme.
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
df = pd.read_excel('C:\\Users\user1\\PycharmProjects\\untitled\\Python_test.xlsx')
x = df['Vp_dry']
y = df['Vs_dry']
q = df['Vp_wet']
w = df['Vs_wet']
fig, ax = plt.subplots()
popt, pcov = curve_fit(lambda fx, a, b: a * fx ** -b, x, y)
x_linspace = np.linspace(min(x - 100), max(x + 100), 100)
power_y = popt[0]*x_linspace ** -popt[1]
ax1 = plt.scatter(x, y, c=df['Porosity'], cmap=plt.cm.Greys, vmin=2, vmax=df['Porosity'].max(), edgecolors="#B6BBBD")
plt.plot(x_linspace, power_y, color='grey', label='Dry')
popt, pcov = curve_fit(lambda fx, a, b: a * fx ** -b, q, w)
q_linspace = np.linspace(min(q - 100), max(q + 100), 100)
power_w = popt[0]*q_linspace ** -popt[1]
ax2 = plt.scatter(q, w, c=df['Porosity'], cmap=plt.cm.Blues, vmin=2, vmax=df['Porosity'].max(), edgecolors="#3D83C1")
plt.plot(q_linspace, power_w, label='Wet')
cbar = fig.colorbar(ax2)
cbar = fig.colorbar(ax1)
cbar.set_label("Porosity (%)")
plt.xlabel('Vp (m/s)')
plt.ylabel('Vs (m/s)')
plt.grid()
plt.legend()
plt.show()
Desired result:
You seem to need a colorbar with two color maps combined, one of them reversed, and have the ticks changed to percentage values.
An approach is to manually create a second subplot, use two images and make it look like a colorbar:
import matplotlib.pyplot as plt
import matplotlib.ticker as mtick
import numpy as np
# first create some dummy data to plot
N = 100
x = np.random.uniform(0, 10, N)
y = np.random.normal(15, 2, N)
q = np.random.uniform(0, 10, N)
w = np.random.normal(10, 2, N)
df_porosity = np.random.uniform(0, 5, N)
fig, (ax, ax2) = plt.subplots(ncols=2, figsize=(6, 4), gridspec_kw={"width_ratios": [1, 0.08]})
plot1 = ax.scatter(x, y, c=df_porosity, cmap=plt.cm.Greys, vmin=2, vmax=df_porosity.max(), edgecolors="#B6BBBD")
plot2 = ax.scatter(q, w, c=df_porosity, cmap=plt.cm.Blues, vmin=2, vmax=df_porosity.max(), edgecolors="#3D83C1")
img_cbar = np.linspace(0, 1, 256).reshape(256, 1)
ax2.imshow(img_cbar, cmap=plt.cm.Blues, extent=[0, 1, 1, 0]) # aspect='auto')
ax2.imshow(img_cbar, cmap=plt.cm.Greys, extent=[0, 1, -1, 0])
ax2.set_ylim(-1, 1)
ax2.set_aspect(10)
ax2.set_ylabel("Porosity (%)")
ax2.yaxis.set_label_position("right")
ax2.set_xticks([])
ax2.yaxis.tick_right()
# optionally show the ticks as percentage, where 1.0 corresponds to 100 %
ax2.yaxis.set_major_formatter(mtick.PercentFormatter(1.0))
plt.tight_layout()
plt.show()

How to do a contour plot from x,y,z coordinates in matplotlib? (plt.contourf or plt.contour)

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)

How to make my surface plot appear using Axes3D?

I'm trying to create a surface plot using Python Matplotlib. I've read the documentation in an attempt to figure out where my code was wrong or if I've left anything out, but was having trouble.
The code that I've written is
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
def computeCost(X, y, theta):
m = len(y)
predictions = np.dot(X, theta)
squareErros = (predictions - y) ** 2
J = (1 / (2 * m)) * sum(squareErrors)
return J
data = np.loadtxt("./data1.txt", delimiter=',')
X = data[:, 0].reshape(-1, 1)
y = data[:, 1].reshape(-1, 1)
m = len(y)
X = np.concatenate((np.ones((m, 1)), X), axis=1)
theta0_vals = np.linspace(-10, 10, 100) # size (100,)
theta1_vals = np.linspace(-1, 4, 100) # size (100,)
J_vals = np.zeros((len(theta0_vals), len(theta1_vals)))
for i in range(len(x_values)):
for j in range(len(y_values)):
t = np.array([theta0_vals[i], theta1_vals[j]]).reshape(-1, 1)
J_vals[i][j] = computeCost(X, y, t) # size (100, 100)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
surf = ax.plot_surface(theta0_vals, theta1_vals, J_vals)
plt.show()
When I invoke plt.show() I get no output. The surface plot that I'm expecting to see is similar to this:
Would anybody be kind enough to let me know where my usage of the surface plot library went wrong? Thank you.
EDIT
I've tried to run the demo code provided here and it works fine. Here's the code for that:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
# Make data.
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)
# Plot the surface.
surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
# Customize the z axis.
ax.set_zlim(-1.01, 1.01)
ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))
# Add a color bar which maps values to colors.
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
I think I've figured out the issue by changing a couple of the last lines of code from
ax = fig.add_subplot(111, projection='3d')
surf = ax.plot_surface(theta0_vals, theta1_vals, J_vals)
to
ax = plt.axes(projection='3d')
surf = ax.plot_surface(theta0_vals, theta1_vals, J_vals, rstride=1, cstride=1, cmap='viridis', edgecolor='none')
Making this change gives me a surface plot such that:
The link that gave me reference to this was this.

no points appearring in 2d histogram

I have been trying to input data from arrays into a 2d histogram and use plt.imshow to show it. However I have been unsuccessful so far. I get an empty array with the correct labels but there are no points to be detected. I have looked up examples online, to no avail.
d[0]= array([ 559.31299349, 507.44063212, 596.05952403, ..., 531.48861237,
525.03097371, 512.51860453])
d[1]= array([ 604.44753343, 513.26418859, 658.79946406, ..., 543.09749822,
522.69953756, 579.40805154])
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
d = np.load('XandY.npy')
x = d[0]
y = d[1]
gridx = np.linspace(min(x),max(x),10)
gridy = np.linspace(min(y),max(y),10)
H, xedges, yedges = np.histogram2d(x, y, bins=[gridx, gridy])
fig1 = plt.figure()
plt.plot=(x,y,'ro')
plt.grid(True)
plt.xlabel('array X')
plt.ylabel('array y')
plt.figure()
myextent =[xedges[0],xedges[-1],yedges[0],yedges[-1]]
plt.imshow(H.T,origin='low',extent=myextent,aspect='auto')
plt.plot(x,y,'ro')
plt.colorbar()
plt.show()
Where have my points gone?
The following simplified code worked for me.
def main():
#output image
outpath=os.path.join('data', 'matplot_hist2d_example.png')
#get 100 random scatter points in the range(500.0-700.0)
np.random.seed(1702)
rand_pts=np.random.uniform(low=500.0, high=700.0, size=(100,2))
x = rand_pts[:, 0]
y = rand_pts[:, 1]
#ensure 10 bins along each axis
gridx = np.linspace(min(x), max(x), 11)
gridy = np.linspace(min(y), max(y), 11)
#histogram 2d
H, xedges, yedges = np.histogram2d(x, y, bins=[gridx, gridy])
#plotting
fig1 = plt.figure()
plt.xlabel('array X')
plt.ylabel('array Y')
myextent =[xedges[0],xedges[-1],yedges[0],yedges[-1]]
plt.imshow(H.T, origin='low', extent=myextent,aspect='auto')
plt.colorbar()
#show points as well
plt.scatter(x,y)
plt.show()
#save
fig1.savefig(outpath)
plt.close(fig1)
pass
See the results below

Categories