Plotly surface plot with unequal length x and y axes - python-3.x

I have a 10x12 array that I would like to plot using plotly. The length of x-axis is 10 and y-axis is 12. I expect the plot to have a range of 10 in the x-axis and 12 in the y-axis. But that is not the case. I get 10 on both axes. I have included a MWE to reproduce the issue.
import numpy as np
import plotly.graph_objects as go
data_array = np.empty((10, 12))
for i in range(10):
for j in range(12):
data_array[i, j] = i+j
x = np.linspace(1, 10, 10)
y = np.linspace(1, 12, 12)
fig = go.Figure(data=[go.Surface(z=data_array, x=x, y=y)])
fig.show()
The plot that it generates is
I have tried setting the range via update_layout which doesn't solve the problem.
import numpy as np
import plotly.graph_objects as go
data_array = np.empty((10, 12))
for i in range(10):
for j in range(12):
data_array[i, j] = i+j
x = np.linspace(1, 10, 10)
y = np.linspace(1, 12, 12)
fig = go.Figure(data=[go.Surface(z=data_array, x=x, y=y)])
fig.update_layout(scene=dict(xaxis=dict(range=[0, 10]),
yaxis=dict(range=[0, 12])))
fig.show()
Although the range of y-axis in now till 12, there is nothing plotted in that part of the graph.
How can I get this to work? Thanks.
Based on the answer below, I have changed the code.
import numpy as np
import plotly.graph_objects as go
data_array = np.empty((10, 12))
for idx1, i in enumerate(range(0, 50, 5)):
for idx2, j in enumerate(range(0, 24, 2)):
data_array[idx1, idx2] = (i+j) / (50+24)
x = np.linspace(0, 45, 10)
y = np.linspace(0, 22, 12)
fig = go.Figure(data=[go.Surface(z=data_array.T)])
fig.show()
But the axes now have no correspondence with the actual x and y values. The values in the plot go from 0 to 9 and 0 to 11 for x and y axes respectively. How do I set the calibration? Thanks.

The matrix data_array should be transposed (x along columns, y along rows). The limit can be explicitly set by passing the x, y arguments. The code is as follows:
import numpy as np
import plotly.graph_objects as go
data_array = np.empty((10, 12))
for idx1, i in enumerate(range(0, 50, 5)):
for idx2, j in enumerate(range(0, 24, 2)):
data_array[idx1, idx2] = (i+j) / (50+24)
x = np.linspace(0, 45, 10)
y = np.linspace(0, 22, 12)
fig = go.Figure(data=[go.Surface(z=data_array.T, x=x, y=y)])
fig.show()
I am currently unable to save the image with the tag on the last position. The image obtained is as follows:
Documentation: https://plotly.com/python/3d-surface-plots/

Related

plotting of 3-d softmax function using matplotlib

I would like to illustrate a detailed function similar to the softmax function.
The formula is as shown in the image.
I wrote it in python based on the following blog. Sorry, written in Japanese.
https://www.anarchive-beta.com/entry/2020/06/07/180000
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import matplotlib
from mpl_toolkits.mplot3d.axes3d import Axes3D
%matplotlib notebook
def softmax(x, a, b, d):
x = x - np.max(x, axis=-1, keepdims=True)
return (np.exp(a * (x - d))*b / np.sum(np.exp(a * (x - d)), axis=-1, keepdims=True))
# input
x_vals = np.arange(0, 10, 0.01)
x2_vals = np.arange(0, 10, 0.01)
X0_vals, X1_vals = np.meshgrid(x_vals, x2_vals)
X_vals = np.array([
X0_vals.flatten(),
X1_vals.flatten()
]).T
print(X_vals)
print(X_vals[:5])
print(X_vals.shape)
input_shape = X0_vals.shape
print(input_shape)
Y_vals = softmax(X_vals, 12, 0.8, [10,10])
print(np.round(Y_vals[:5], 3))
print(np.sum(Y_vals[:5], axis=1))
Y0_vals = np.array(Y_vals[:, 0]).reshape(input_shape)
fig = plt.figure(figsize=(5, 5))
ax = Axes3D(fig)
ax.plot_wireframe(X0_vals, X1_vals, Y0_vals, label='$y_0$')
ax.set_xlabel('$x_0$')
ax.set_ylabel('$x_1$')
ax.set_zlabel('$y_0$')
ax.set_title('Softmax Function', fontsize=20)
ax.legend()
ax.set_zlim(0, 1)
ax.view_init(elev=20, azim=240)
plt.show()
Y_vals = softmax(X_vals, 12, 0.8, 0)
print(np.round(Y_vals[:5], 3))
print(np.sum(Y_vals[:5], axis=1))
Y0_vals = np.array(Y_vals[:, 0]).reshape(input_shape)
fig = plt.figure(figsize=(5, 5))
ax = Axes3D(fig)
ax.plot_wireframe(X0_vals, X1_vals, Y0_vals, label='$y_0$')
ax.set_xlabel('$x_0$')
ax.set_ylabel('$x_1$')
ax.set_zlabel('$y_0$')
ax.set_title('Softmax Function', fontsize=20)
ax.legend()
ax.set_zlim(0, 1)
ax.view_init(elev=20, azim=240)
plt.show()
(1)
I try to plot the picture with a_1 = a_2 = 12, b = 0.8, c_1 = c_2 = 12. However, I could not find the differences with a_1 = a_2 = 12, b = 0.8, c_1 = c_2 = 0.
How should I write the code?
(2)
I have no idea to plot when a_1 is not equal to a_2, or c_1 is not equal to c_2.
(3)
I would like to overlap a scatterplot of any point on a function, but it does not overlap properly.
There might be similar questions, but I'm not so familiar with the 3-D plot, so I would be glad to show me the details.

Change colorbar limits without changing the values of the data it represents in scatter

I'm trying to change a colorbar attached to a scatter plot so that the minimum and maximum of the colorbar are the minimum and maximum of the data, but I want the data to be centred at zero as I'm using a colormap with white at zero. Here is my example
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 1, 61)
y = np.linspace(0, 1, 61)
C = np.linspace(-10, 50, 61)
M = np.abs(C).max() # used for vmin and vmax
fig, ax = plt.subplots(1, 1, figsize=(5,3), dpi=150)
sc=ax.scatter(x, y, c=C, marker='o', edgecolor='k', vmin=-M, vmax=M, cmap=plt.cm.RdBu_r)
cbar=fig.colorbar(sc, ax=ax, label='$R - R_0$ (mm)')
ax.set_xlabel('x')
ax.set_ylabel('y')
As you can see from the attached figure, the colorbar goes down to -M, where as I want the bar to just go down to -10, but if I let vmin=-10 then the colorbar won't be zerod at white. Normally, setting vmin to +/- M when using contourf the colorbar automatically sorts to how I want. This sort of behaviour is what I expect when contourf uses levels=np.linspace(-M,M,61) rather than setting it with vmin and vmax with levels=62. An example showing the default contourf colorbar behaviour I want in my scatter example is shown below
plt.figure(figsize=(6,5), dpi=150)
plt.contourf(x, x, np.reshape(np.linspace(-10, 50, 61*61), (61,61)),
levels=62, vmin=-M, vmax=M, cmap=plt.cm.RdBu_r)
plt.colorbar(label='$R - R_0$ (mm)')
Does anyone have any thoughts? I found this link which I thought might solve the problem, but when executing the cbar.outline.set_ydata line I get this error AttributeError: 'Polygon' object has no attribute 'set_ydata' .
EDIT a little annoyed that someone has closed this question without allowing me to clarify any questions they might have, as none of the proposed solutions are what I'm asking for.
As for Normalize.TwoSlopeNorm, I do not want to rescale the smaller negative side to use the entire colormap range, I just want the colorbar attached to the side of my graph to stop at -10.
This link also does not solve my issue, as it's the TwoSlopeNorm solution again.
After changing the ylim of the colorbar, the rectangle formed by the surrounding spines is too large. You can make this outline invisible. And then add a new rectangular border:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 1, 61)
y = np.linspace(0, 1, 61)
C = np.linspace(-10, 50, 61)
M = np.abs(C).max() # used for vmin and vmax
fig, ax = plt.subplots(1, 1, figsize=(5, 3), dpi=150)
sc = ax.scatter(x, y, c=C, marker='o', edgecolor='k', vmin=-M, vmax=M, cmap=plt.cm.RdBu_r)
cbar = fig.colorbar(sc, ax=ax, label='$R - R_0$ (mm)')
cb_ymin = C.min()
cb_ymax = C.max()
cb_xmin, cb_xmax = cbar.ax.get_xlim()
cbar.ax.set_ylim(cb_ymin, cb_ymax)
cbar.outline.set_visible(False) # hide the surrounding spines, which are too large after set_ylim
cbar.ax.add_patch(plt.Rectangle((cb_xmin, cb_ymin), cb_xmax - cb_xmin, cb_ymax - cb_ymin,
fc='none', ec='black', clip_on=False))
plt.show()
Another approach until v3.5 is released is to make a custom colormap that does what you want (see also https://matplotlib.org/stable/tutorials/colors/colormap-manipulation.html#sphx-glr-tutorials-colors-colormap-manipulation-py)
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.cm as cm
from matplotlib.colors import ListedColormap
fig, axs = plt.subplots(2, 1)
X = np.random.randn(32, 32) + 2
pc = axs[0].pcolormesh(X, vmin=-6, vmax=6, cmap='RdBu_r')
fig.colorbar(pc, ax=axs[0])
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.cm as cm
from matplotlib.colors import ListedColormap
fig, axs = plt.subplots(2, 1)
X = np.random.randn(32, 32) + 2
pc = axs[0].pcolormesh(X, vmin=-6, vmax=6, cmap='RdBu_r')
fig.colorbar(pc, ax=axs[0])
def keep_center_colormap(vmin, vmax, center=0):
vmin = vmin - center
vmax = vmax - center
dv = max(-vmin, vmax) * 2
N = int(256 * dv / (vmax-vmin))
RdBu_r = cm.get_cmap('RdBu_r', N)
newcolors = RdBu_r(np.linspace(0, 1, N))
beg = int((dv / 2 + vmin)*N / dv)
end = N - int((dv / 2 - vmax)*N / dv)
newmap = ListedColormap(newcolors[beg:end])
return newmap
newmap = keep_center_colormap(-2, 6, center=0)
pc = axs[1].pcolormesh(X, vmin=-2, vmax=6, cmap=newmap)
fig.colorbar(pc, ax=axs[1])
plt.show()

How to color space between two lines with a colour transition?

How to colour space between two lines with a colour transition? For instance, with grey colour - the dark grey from the upper line should become lighter as proceeding to the lower line. Thank you
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
plt.rcParams["figure.figsize"] = [8, 8]
x = np.linspace(0, 1, 100)
y = 0.3*x
ax.set_ylim(-0.2, 0.6)
ax.plot(x, y)
width_l = ax.get_ylim()[1] - ax.get_ylim()[0]
ax.plot(x, y - 0.1*width_l)
plt.show()
Edit
And this, please? How to make the width of the coloured part the same?
import matplotlib.pyplot as plt
import numpy as np
import pywt
fig, ax = plt.subplots()
plt.rcParams["figure.figsize"] = [8, 8]
wavelet = pywt.ContinuousWavelet('morl')
psi, x = wavelet.wavefun(level=2)
cmap = plt.get_cmap('Greys_r')
ax.plot(x, psi)
ax.set_xlim(ax.get_xlim()[0], ax.get_xlim()[1])
y_a = ax.get_ylim()
ax.set_ylim(y_a[0],y_a[1]*1.3)
width_l = ax.get_ylim()[1] - ax.get_ylim()[0]
x_range = ax.get_xlim()[1] - ax.get_xlim()[0]
x_shift = x_range * 0.1
ax.plot([x[0]+x_shift, x[1]+x_shift], [psi[0], psi[1]])
ax.plot([x[2]-x_shift, x[3]-x_shift], [psi[2], psi[3]])
ax.plot([x[1], x[2]], [psi[1]-width_l*0.1, psi[2]-width_l*0.1])
for t in np.linspace(0, 1, 40):
ax.plot(x, psi - t * 0.1 * width_l, color=cmap(t/2 + 0.25))
plt.show()
You could draw a lot of parallel lines (or curves) using a color from a gray-scale colormap. The example code below uses a transformation u = t/2 + 0.25, so when t goes from 0 to 1, u would just go between 0.25 and 0.75 to select of specific range from the colormap, avoiding the very dark and very light parts.
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
plt.rcParams["figure.figsize"] = [8, 8]
x = np.linspace(0, 1, 100)
y = 0.3 * x
width_l = ax.get_ylim()[1] - ax.get_ylim()[0]
ax.set_ylim(-0.2, 0.6)
cmap = plt.get_cmap('Greys_r')
for t in np.linspace(0, 1, 40):
u = t/2 + 0.25
ax.plot(x, y - t * 0.1 * width_l, color=cmap(u))
ax.plot(x, y)
ax.plot(x, y - 0.1 * width_l)
plt.show()

Draw curves with triple colors and width by using matplotlib and LineCollection [duplicate]

The figure above is a great artwork showing the wind speed, wind direction and temperature simultaneously. detailedly:
The X axes represent the date
The Y axes shows the wind direction(Southern, western, etc)
The variant widths of the line were stand for the wind speed through timeseries
The variant colors of the line were stand for the atmospheric temperature
This simple figure visualized 3 different attribute without redundancy.
So, I really want to reproduce similar plot in matplotlib.
My attempt now
## Reference 1 http://stackoverflow.com/questions/19390895/matplotlib-plot-with-variable-line-width
## Reference 2 http://stackoverflow.com/questions/17240694/python-how-to-plot-one-line-in-different-colors
def plot_colourline(x,y,c):
c = plt.cm.jet((c-np.min(c))/(np.max(c)-np.min(c)))
lwidths=1+x[:-1]
ax = plt.gca()
for i in np.arange(len(x)-1):
ax.plot([x[i],x[i+1]], [y[i],y[i+1]], c=c[i],linewidth = lwidths[i])# = lwidths[i])
return
x=np.linspace(0,4*math.pi,100)
y=np.cos(x)
lwidths=1+x[:-1]
fig = plt.figure(1, figsize=(5,5))
ax = fig.add_subplot(111)
plot_colourline(x,y,prop)
ax.set_xlim(0,4*math.pi)
ax.set_ylim(-1.1,1.1)
Does someone has a more interested way to achieve this? Any advice would be appreciate!
Using as inspiration another question.
One option would be to use fill_between. But perhaps not in the way it was intended. Instead of using it to create your line, use it to mask everything that is not the line. Under it you can have a pcolormesh or contourf (for example) to map color any way you want.
Look, for instance, at this example:
import matplotlib.pyplot as plt
import numpy as np
from scipy.interpolate import interp1d
def windline(x,y,deviation,color):
y1 = y-deviation/2
y2 = y+deviation/2
tol = (y2.max()-y1.min())*0.05
X, Y = np.meshgrid(np.linspace(x.min(), x.max(), 100), np.linspace(y1.min()-tol, y2.max()+tol, 100))
Z = X.copy()
for i in range(Z.shape[0]):
Z[i,:] = c
#plt.pcolormesh(X, Y, Z)
plt.contourf(X, Y, Z, cmap='seismic')
plt.fill_between(x, y2, y2=np.ones(x.shape)*(y2.max()+tol), color='w')
plt.fill_between(x, np.ones(x.shape) * (y1.min() - tol), y2=y1, color='w')
plt.xlim(x.min(), x.max())
plt.ylim(y1.min()-tol, y2.max()+tol)
plt.show()
x = np.arange(100)
yo = np.random.randint(20, 60, 21)
y = interp1d(np.arange(0, 101, 5), yo, kind='cubic')(x)
dv = np.random.randint(2, 10, 21)
d = interp1d(np.arange(0, 101, 5), dv, kind='cubic')(x)
co = np.random.randint(20, 60, 21)
c = interp1d(np.arange(0, 101, 5), co, kind='cubic')(x)
windline(x, y, d, c)
, which results in this:
The function windline accepts as arguments numpy arrays with x, y , a deviation (like a thickness value per x value), and color array for color mapping. I think it can be greatly improved by messing around with other details but the principle, although not perfect, should be solid.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
x = np.linspace(0,4*np.pi,10000) # x data
y = np.cos(x) # y data
r = np.piecewise(x, [x < 2*np.pi, x >= 2*np.pi], [lambda x: 1-x/(2*np.pi), 0]) # red
g = np.piecewise(x, [x < 2*np.pi, x >= 2*np.pi], [lambda x: x/(2*np.pi), lambda x: -x/(2*np.pi)+2]) # green
b = np.piecewise(x, [x < 2*np.pi, x >= 2*np.pi], [0, lambda x: x/(2*np.pi)-1]) # blue
a = np.ones(10000) # alpha
w = x # width
fig, ax = plt.subplots(2)
ax[0].plot(x, r, color='r')
ax[0].plot(x, g, color='g')
ax[0].plot(x, b, color='b')
# mysterious parts
points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
# mysterious parts
rgba = list(zip(r,g,b,a))
lc = LineCollection(segments, linewidths=w, colors=rgba)
ax[1].add_collection(lc)
ax[1].set_xlim(0,4*np.pi)
ax[1].set_ylim(-1.1,1.1)
fig.show()
I notice this is what I suffered.

More areas in contourf using logscale

I'm currently trying to get an impression of continuous change in my contour plot. I have to use a logscale for the values, because some of them are some orders of magnitude bigger than the others.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import ticker
K = np.linspace(-0.99, 5, 100)
x = np.linspace(1, 5, 100)
K, x = np.meshgrid(K, x)
static_diff = 1 / (1 + K)
fig = plt.figure()
plot = plt.contourf(K, x, static_diff, locator=ticker.LogLocator(numticks=300))
plt.grid(True)
plt.xlabel('K')
plt.ylabel('x')
plt.xlim([-0.99, 5])
plt.ylim([1, 5])
fig.colorbar(plot)
plt.show()
Despite the number of ticks given to be 300 it returns a plot like:
Is there a way to get more of these lines? I also tried adding the number of parameters as the fourth parameter of the plt.contourf function.
To specify the levels of a contourf plot you may
use the levels argument and supply a list of values for the levels. E.g for 20 levels,
plot = plt.contourf(K, x, static_diff, levels=np.logspace(-2, 3, 20))
use the locator argument to which you would supply a matplotlib ticker
plt.contourf(K, x, static_diff, locator=ticker.LogLocator(subs=range(1,10)))
Note however that the LogLocator does not use a numticks argument but instead a base and a subs argument to determine the locations of the ticks. See documentation.
Complete example for the latter case, which also uses a LogNormto distribute the colors better in logspace:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import ticker
import matplotlib.colors
K = np.linspace(-0.99, 5, 100)
x = np.linspace(1, 5, 100)
K, x = np.meshgrid(K, x)
static_diff = 1 / (1 + K)
fig = plt.figure()
norm= matplotlib.colors.LogNorm(vmin=static_diff.min(), vmax=static_diff.max())
plot = plt.contourf(K, x, static_diff, locator=ticker.LogLocator(subs=range(1,10)), norm=norm)
#plot = plt.contourf(K, x, static_diff, levels=np.logspace(-2, 3, 20), norm=norm)
plt.grid(True)
plt.xlabel('K')
plt.ylabel('x')
plt.xlim([-0.99, 5])
plt.ylim([1, 5])
fig.colorbar(plot)
plt.show()

Resources