How to plot a vertical thermal plot? - python

How to plot this kind of thermal plot in Python? I tried to search for any sample plot like this but didn't find one.
This image I got from the internet. I want to plot something same like this:

FROM
TO
To represent this type of data the canonical solution is, of course, a heat map. Here it is the code to produce both the figures at the top of this post.
import numpy as np
import matplotlib.pyplot as plt
t = np.linspace(0, 5, 501)
x = np.linspace(0, 1, 201)[:, None]
T = 50 + (30-6*t)*(4*x*(1-x)) + 4*t
fig, ax = plt.subplots(layout='constrained')
hm = ax.imshow(T, cmap='plasma',
aspect='auto', origin='lower', extent=(0, 5, 0, 1))
fig.colorbar(hm)
def heat_lines(x, t, T, n):
from matplotlib.cm import ScalarMappable
from matplotlib.collections import LineCollection
lx, lt = T.shape
ones = np.ones(lx)
norm = plt.Normalize(np.min(T), np.max(T))
plasma = plt.cm.plasma
fig, ax = plt.subplots(figsize=(1+1.2*n, 9), layout='constrained')
ax.set_xlim((-0.6, n-0.4))
ax.set_ylim((x[0], x[-1]))
ax.set_xticks(range(n))
ax.tick_params(right=False,top=False, bottom=False)
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
ax.spines["bottom"].set_visible(False)
ax.grid(axis='y')
fig.colorbar(ScalarMappable(cmap=plasma, norm=norm))
dt = round(lt/(n-1))
for pos, ix in enumerate(range(0, len(t)+dt//2, dt)):
points = np.array([ones*pos, x[:,0]]).T.reshape(-1,1,2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
lc = LineCollection(segments, linewidth=72, ec=None,
color=plasma(norm(T[:,ix])))
lc.set_array(T[:,ix])
ax.add_collection(lc)
heat_lines(x, t, T, 6)

Related

Color overlapped lines with plt.plot (Matplotlib)

How can I configure plt.plot such that overlapped lines will have darker colors?
For example, I would like to use plt.plot to display the samples in such a way that the density that can be seen in the upper plot will be clear in the lower plot.
From the lower plot it's hard to understand where most of the samples are located
Here is the code I used in order to generate the example:
import numpy as np
import matplotlib.pyplot as plt
time = 100
n_samples = 7000
x = np.linspace(0, time, n_samples)
r1 = np.random.normal(0, 1, x.size)
r2 = np.random.uniform(-6, 6, x.size)
data = np.dstack((r1, r2)).flatten()
fig, axs = plt.subplots(2, 1, figsize=(9, 6))
axs[0].scatter(np.arange(len(data)), data, alpha=0.1)
axs[1].plot(np.arange(len(data)), data, alpha=0.2)
plt.show()
Update: segmentation and plotting into separated function
Instead of drawing one large curve, you could create each line segment separately and then draw these. That way, the overlapping segments will be blended via the transparency.
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
import numpy as np
def plot_line_as_segments(xs, ys=None, ax=None, **kwargs):
ax = ax or plt.gca()
if ys is None:
ys = xs
xs = np.arange(len(ys))
segments = np.c_[xs[:-1], ys[:-1], xs[1:], ys[1:]].reshape(-1, 2, 2)
added_collection = ax.add_collection(LineCollection(segments, **kwargs))
ax.autoscale()
return added_collection
time = 100
n_samples = 7000
x = np.linspace(0, time, n_samples)
r1 = np.random.normal(0, 1, x.size)
r2 = np.random.uniform(-6, 6, x.size)
data = np.dstack((r1, r2)).flatten()
fig, axs = plt.subplots(2, 1, figsize=(9, 6))
axs[0].scatter(np.arange(len(data)), data, alpha=0.1)
axs[0].margins(x=0)
plot_line_as_segments(data, ax=axs[1], alpha=0.05)
axs[1].margins(x=0)
plt.show()

How to create a multi-colored curve in 3d?

I'm trying to plot a 3d curve that has different colors depending on one of its parameters. I tried this method similar to this question, but it doesn't work. Can anyone point me in the right direction?
import matplotlib.pyplot as plt
from matplotlib import cm
T=100
N=5*T
x=np.linspace(0,T,num=N)
y=np.cos(np.linspace(0,T,num=N))
z=np.sin(np.linspace(0,T,num=N))
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot(x,y,z,cmap = cm.get_cmap("Spectral"),c=z)
plt.show()
To extend the approach in this tutorial to 3D, use x,y,z instead of x,y.
The desired shape for the segments is (number of segments, 2 points, 3 coordinates per point), so N-1,2,3. First the array of points is created with shape N, 3. Then start (xyz[:-1, :]) and end points (xyz[1:, :]) are stacked together.
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Line3DCollection
T = 100
N = 5 * T
x = np.linspace(0, T, num=N)
y = np.cos(np.linspace(0, T, num=N))
z = np.sin(np.linspace(0, T, num=N))
xyz = np.array([x, y, z]).T
segments = np.stack([xyz[:-1, :], xyz[1:, :]], axis=1) # shape is 499,2,3
cmap = plt.cm.get_cmap("Spectral")
norm = plt.Normalize(z.min(), z.max())
lc = Line3DCollection(segments, linewidths=2, colors=cmap(norm(z[:-1])))
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.add_collection(lc)
ax.set_xlim(-10, 110)
ax.set_ylim(-1.1, 1.1)
ax.set_zlim(-1.1, 1.1)
plt.show()

Append data with different colour in matplotlib in real time

I'm updating dynamically a plot in a loop:
dat=[0, max(X[:, 0])]
fig = plt.figure()
ax = fig.add_subplot(111)
Ln, = ax.plot(dat)
Ln2, = ax.plot(dat)
plt.ion()
plt.show()
for i in range(1, 40):
ax.set_xlim(int(len(X[:i])*0.8), len(X[:i])) #show last 20% data of X
Ln.set_ydata(X[:i])
Ln.set_xdata(range(len(X[:i])))
Ln2.set_ydata(Y[:i])
Ln2.set_xdata(range(len(Y[:i])))
plt.pause(0.1)
But now I want to update it in a different way: append some values and show them in other colour:
X.append(other_data)
# change colour just to other_data in X
The result should look something like this:
How could I do that?
Have a look at the link I posted. Linesegments can be used to plot colors at a particular location differently. If you want to do it in real-time you can still use line-segments. I leave that up to you.
# adjust from https://stackoverflow.com/questions/38051922/how-to-get-differents-colors-in-a-single-line-in-a-matplotlib-figure
import numpy as np, matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from matplotlib.colors import ListedColormap, BoundaryNorm
# my func
x = np.linspace(-2 * np.pi, 2 * np.pi, 100)
y = 3000 * np.sin(x)
# select how to color
cmap = ListedColormap(['r','b'])
norm = BoundaryNorm([2000,], cmap.N)
# get segments
xy = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.hstack([xy[:-1], xy[1:]])
# control which values have which colors
n = y.shape[0]
c = np.array([plt.cm.RdBu(0) if i < n//2 else plt.cm.RdBu(255) for i in range(n)])
# c = plt.cm.Reds(np.arange(0, n))
# make line collection
lc = LineCollection(segments,
colors = c
# norm = norm,
)
# plot
fig, ax = plt.subplots()
ax.add_collection(lc)
ax.autoscale()
ax.axvline(x[n//2], linestyle = 'dashed')
ax.annotate("Half-point", (x[n//2], y[n//2]), xytext = (4, 1000),
arrowprops = dict(headwidth = 30))
fig.show()

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 rescale a plot in a subplot with matplotlib

I have 4 subplots with a different 3D plot with a colorbar.
I want to plot a XY view of my 3D plot, remove the x,y,z axis and resize my plot to use all the space available in the subplot such that the XY view has the same height as the colorbar. I can remove the axis but I do not know how to resize the image. I attached a working code to illustrate this.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib.tri as mtri
import matplotlib
import numpy as np
# Create 3D function
n_radii = 8
n_angles = 36
radii = np.linspace(0.125, 1.0, n_radii)
angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)[..., np.newaxis]
x = np.append(0, (radii*np.cos(angles)).flatten())
y = np.append(0, (radii*np.sin(angles)).flatten())
z = np.sin(-x*y)
fig = plt.figure()
for ii in range(1, 4):
#Plot
# ============================================================================
ax = fig.add_subplot(2,2, ii, projection='3d')
cs =ax.plot_trisurf(x, y, z, linewidth=0.2, antialiased=True)
ax.view_init(90, 0)
plt.title(ii)
# ax.axis('off')
plt.grid(b=None)
# Create color bar
# ============================================================================
norm = matplotlib.colors.Normalize(vmin = 0, vmax = 1, clip = False)
m = plt.cm.ScalarMappable(norm=norm)
m.set_array([])
plt.colorbar(m)
plt.tight_layout()
plt.show()
#plt.savefig("test.pdf",bbox_inches='tight')
Any idea how can I do this?
I have added
plt.gca().set_axis_off()
plt.axis([0.6 * x for x in plt.axis()])
to your code which hides the axes and sets the view to 60% of its previous value. The result looks like this:
Full code:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib.tri as mtri
import matplotlib
import numpy as np
# Create 3D function
n_radii = 8
n_angles = 36
radii = np.linspace(0.125, 1.0, n_radii)
angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)[..., np.newaxis]
x = np.append(0, (radii*np.cos(angles)).flatten())
y = np.append(0, (radii*np.sin(angles)).flatten())
z = np.sin(-x*y)
fig = plt.figure()
for ii in range(1, 4):
#Plot
# ============================================================================
ax = fig.add_subplot(2,2, ii, projection='3d')
cs =ax.plot_trisurf(x, y, z, linewidth=0.2, antialiased=True)
ax.view_init(90, 0)
plt.title(ii)
# ax.axis('off')
plt.grid(b=None)
# Create color bar
# ============================================================================
norm = matplotlib.colors.Normalize(vmin = 0, vmax = 1, clip = False)
m = plt.cm.ScalarMappable(norm=norm)
m.set_array([])
plt.colorbar(m)
plt.gca().set_axis_off()
plt.axis([0.6 * x for x in plt.axis()])
plt.tight_layout()
plt.show()
#plt.savefig("test.pdf",bbox_inches='tight')

Categories