draw functions in 3D data - python

using the below code, I create three-dimensional data to plot in a pcolormesh plot.
n = 100 # size
_min, _max = -10, 10
# generate 2 2d grids for the x & y bounds
x, y = np.meshgrid(np.linspace(_min, _max, n), np.linspace(_min, _max, n))
# generate z values with random noise
z = np.array([np.zeros(n) for i in range(n)])
for i in range(len(z)):
z[i] = z[i] + 0.1 * np.random.randint(0,3, size=len(z[i]))
# plotting
fig, ax = plt.subplots()
c = ax.pcolormesh(x, y, z, cmap='RdBu', vmin=-1, vmax=1)
ax.set_title('pcolormesh')
plt.plot([5,5,-2.5], [5,-5,5], color='darkblue', marker='o', markersize=15, linewidth=0) # dots (outer)
plt.plot([5,5,-2.5], [5,-5,5], color='lightblue', marker='o', markersize=10, linewidth=0) # dots (inner)
plt.grid(b=True) # background grid
# set the limits of the plot to the limits of the data
ax.axis([_min, _max, _min, _max])
fig.colorbar(c, ax=ax)
plt.show()
This gives an image:
However, I would now like to alter z values of specific x/y combinations according to specific functions, e.g. a circle described by (x-5)^2 + (y+5)^2 = 1. I would like to alter the data(!) and then plot it.
The 'goal' would be data producing an image like this:
I can experiment with the functions, it's mostly about the logic of altering the z values according to a mathematical function of the form z = f(x, y) that I cannot figure out.
It would follow the (pseudo code logic):
if the x / y combination of a point is on the function f(x, y): add the value c to the initial z value.
Could someone point me to how I can implement this? I tried multiple times but cannot figure it out... :( Many thanks in advance!!!
NOTE: an earlier version was imprecise. It wrongly explained this as a plotting problem although it seems that the data manipulation is the issue. Apologies for that!

You only need to plot a function, the same way.
With these lines I plot a function on your plot.
# Create the independent points of your plot
x = np.arange(0., 5., 0.2)
# Generate your dependent variables
y = np.exp(x)
# Plot your variables
plt.plot(x, y)
You could then do it multiple time.
In your full example it looks like this:
import numpy as np
import matplotlib.pyplot as plt
n = 100 # size
_min, _max = -10, 10
# generate 2 2d grids for the x & y bounds
x, y = np.meshgrid(np.linspace(_min, _max, n), np.linspace(_min, _max, n))
# generate z values with random noise
z = np.array([np.zeros(n) for i in range(n)])
for i in range(len(z)):
z[i] = z[i] + 0.1 * np.random.randint(0, 3, size=len(z[i]))
# plotting
fig, ax = plt.subplots()
c = ax.pcolormesh(x, y, z, cmap='RdBu', vmin=-1, vmax=1)
ax.set_title('pcolormesh')
plt.plot([5, 5, -2.5], [5, -5, 5], color='darkblue', marker='o', markersize=15, linewidth=0) # dots (outer)
plt.plot([5, 5, -2.5], [5, -5, 5], color='lightblue', marker='o', markersize=10, linewidth=0) # dots (inner)
plt.grid(b=True) # background grid
# set the limits of the plot to the limits of the data
ax.axis([_min, _max, _min, _max])
fig.colorbar(c, ax=ax)
x = np.arange(0., 5., 0.2)
plt.plot(x, np.exp(x))
plt.show()
Of course you need to change the line y = np.exp(x) with whatever function you need.

Related

Interpolate colors in 4-D data with matplotlib [duplicate]

I have some z=f(x,y) data which i would like to plot. The issue is that (x,y) are not part of a "nice" rectangle, but rather arbitrary parallelograms, as shown in the attached image (this particular one is also a rectangle, but you could think of more general cases). So I am having a hard time figuring out how I can use plot_surface in this case, as this usually will take x and y as 2d arrays, and here my x-and y-values are 1d. Thanks.
Abritrary points can be supplied as 1D arrays to matplotlib.Axes3D.plot_trisurf. It doesn't matter whether they follow a specific structure.
Other methods which would depend on the structure of the data would be
Interpolate the points on a regular rectangular grid. This can be accomplished using scipy.interpolate.griddata. See example here
Reshape the input arrays such that they live on a regular and then use plot_surface(). Depending on the order by which the points are supplied, this could be a very easy solution for a grid with "parallelogramic" shape.
As can be seen from the sphere example, plot_surface() also works in cases of very unequal grid shapes, as long as it's structured in a regular way.
Here are some examples:
For completeness, find here the code that produces the above image:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
f = lambda x,y: np.sin(x+0.4*y)*0.23+1
fig = plt.figure(figsize=(5,6))
plt.subplots_adjust(left=0.1, top=0.95,wspace=0.01)
ax0 = fig.add_subplot(322, projection="3d")
ma = 6*(np.random.rand(100)-0.5)
mb = 6*(np.random.rand(100)-0.5)
phi = np.pi/4
x = 1.7*ma*np.cos(phi) + 1.7*mb*np.sin(phi)
y = -1.2*ma*np.sin(phi) +1.2* mb*np.cos(phi)
z = f(x,y)
ax0.plot_trisurf(x,y,z)
ax1 = fig.add_subplot(321)
ax0.set_title("random plot_trisurf()")
ax1.set_aspect("equal")
ax1.scatter(x,y, marker="+", alpha=0.4)
for i in range(len(x)):
ax1.text(x[i],y[i], i , ha="center", va="center", fontsize=6)
n = 10
a = np.linspace(-3, 3, n)
ma, mb = np.meshgrid(a,a)
phi = np.pi/4
xm = 1.7*ma*np.cos(phi) + 1.7*mb*np.sin(phi)
ym = -1.2*ma*np.sin(phi) +1.2* mb*np.cos(phi)
shuf = np.c_[xm.flatten(), ym.flatten()]
np.random.shuffle(shuf)
x = shuf[:,0]
y = shuf[:,1]
z = f(x,y)
ax2 = fig.add_subplot(324, projection="3d")
ax2.plot_trisurf(x,y,z)
ax3 = fig.add_subplot(323)
ax2.set_title("unstructured plot_trisurf()")
ax3.set_aspect("equal")
ax3.scatter(x,y, marker="+", alpha=0.4)
for i in range(len(x)):
ax3.text(x[i],y[i], i , ha="center", va="center", fontsize=6)
x = xm.flatten()
y = ym.flatten()
z = f(x,y)
X = x.reshape(10,10)
Y = y.reshape(10,10)
Z = z.reshape(10,10)
ax4 = fig.add_subplot(326, projection="3d")
ax4.plot_surface(X,Y,Z)
ax5 = fig.add_subplot(325)
ax4.set_title("regular plot_surf()")
ax5.set_aspect("equal")
ax5.scatter(x,y, marker="+", alpha=0.4)
for i in range(len(x)):
ax5.text(x[i],y[i], i , ha="center", va="center", fontsize=6)
for axes in [ax0, ax2,ax4]:
axes.set_xlim([-3.5,3.5])
axes.set_ylim([-3.5,3.5])
axes.set_zlim([0.9,2.0])
axes.axis("off")
plt.savefig(__file__+".png")
plt.show()
If your data is in order, and you know the size of the parallgram, a reshape will probably suffice:
ax.surface(x.reshape(10, 10), y.reshape(10, 10), z.reshape(10, 10))
Will work if the parallelogram has 10 points on each side, and the points are ordered in a zigzag pattern

How to increase color resolution in python matplotlib colormap

I am making a colormap of a 2D numpy meshgrid:
X, Y = np.meshgrid(fields, frequencies)
cs = ax.contourf(X, Y, fields_freqs_abs_grid, cmap="viridis", N=256)
The values in fields_freqs_abs_grid, which are plotted by color, have already been logarithmically scaled.
The colormap produced by python's matplotlib is coarse -- it scales over 8 colors even though I use "N=256" for the number of RGB pixels. Increasing N to 2048 did not change anything. A plot using the MatLab language on the same data produces a colormap with significantly higher color resolution. How do I increase the number of colors mapped in Python?
The result is:
But I want the result to be:
Thank you!
Warren Weckesser's comments definitely works and can give you a high resolution image. I implemented his idea in the example below.
In regarding to use contourf(), I'm not sure if this is a version dependent issue, but in the most recent version,
contourf() doesn't have a kwarg for N.
As you can see in the document, you want to use N as an arg (in syntax: contourf(X,Y,Z,N)) to specify how many levels you want to plot rather than the number of RGB pixels. contourf() draws filled contours and the resolution depends on the number of levels to draw. Your N=256 won't do anything and contourf() will automatically choose 7 levels.
The following code is modified from the official example, comparing resolutions with different N. In case there is a version issue, this code gives the following plot with python 3.5.2; matplotlib 1.5.3:
import numpy as np
import matplotlib.pyplot as plt
delta = 0.025
x = y = np.arange(-3.0, 3.01, delta)
X, Y = np.meshgrid(x, y)
Z1 = plt.mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = plt.mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = 10 * (Z1 - Z2)
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2)
fig.set_size_inches(8, 6)
# Your code sample
CS1 = ax1.contourf(X, Y, Z, cmap="viridis", N=256)
ax1.set_title('Your code sample')
ax1.set_xlabel('word length anomaly')
ax1.set_ylabel('sentence length anomaly')
cbar1 = fig.colorbar(CS1, ax=ax1)
# Contour up to N=7 automatically-chosen levels,
# which should give the same as your code.
N = 7
CS2 = ax2.contourf(X, Y, Z, N, cmap="viridis")
ax2.set_title('N=7')
ax2.set_xlabel('word length anomaly')
ax2.set_ylabel('sentence length anomaly')
cbar2 = fig.colorbar(CS2, ax=ax2)
# Contour up to N=100 automatically-chosen levels.
# The resolution is still not as high as using imshow().
N = 100
CS3 = ax3.contourf(X, Y, Z, N, cmap="viridis")
ax3.set_title('N=100')
ax3.set_xlabel('word length anomaly')
ax3.set_ylabel('sentence length anomaly')
cbar3 = fig.colorbar(CS3, ax=ax3)
IM = ax4.imshow(Z, cmap="viridis", origin='lower', extent=(-3, 3, -3, 3))
ax4.set_title("Warren Weckesser's idea")
ax4.set_xlabel('word length anomaly')
ax4.set_ylabel('sentence length anomaly')
cbar4 = fig.colorbar(IM, ax=ax4)
fig.tight_layout()
plt.show()

How to totally remove x-axis (and y-axis) from the plot and also draw tangent lines at certain points using Python or R-programming?

I have to create a plot that has axes suppressed and tangents drawn at regular intervals as shown in figure presented below.
Using R-programming, I know how to suppress tick marks and create the plot.
But I don't know how to suppress the whole axes.
Here, I need to omit the whole a-axis as well other axes such as top and right axes.
My initial try is this:
tau <- seq(-5,5,0.01)
a <- 0.4 # a is a constant parameter
sigma <- a*tau # tau is a variable, sigma = a*tau
x <- 1/a*cosh(sigma)
y <- 1/a*sinh(sigma)
# plot
plot(x,y,type="l",xaxt="n",yaxt="n")
abline(h=0,lty=1)
The plot also requires dots and tangents at points where a*tau = -1,-0.5, 0, 0.5 and 1.
The links I followed are following:
Drawing a Tangent to the Plot and Finding the X-Intercept using R
Lines between certain points in a plot, based on the data? (with R)
The required plot looks like below:
Any suggestion both in python or R are truly appreciated!!
Using Python,
import numpy as np
import matplotlib.pyplot as plt
tau = np.arange(-5, 5, 0.01)
a = 0.4
sigma = a*tau
x = 1/a*np.cosh(sigma)
y = 1/a*np.sinh(sigma)
fig, ax = plt.subplots()
ax.plot(x, y, c='black')
# approximate the curve by a cubic
dxds = np.poly1d(np.polyfit(sigma, x, 3)).deriv()
dyds = np.poly1d(np.polyfit(sigma, y, 3)).deriv()
xs, ys, dxs, dys = [], [], [], []
for s in np.linspace(-1, 1, 5):
# evaluate the derivative at s
dx = np.polyval(dxds, s)
dy = np.polyval(dyds, s)
# record the x, y location and dx, dy tangent vector associated with s
xi = 1/a*np.cosh(s)
yi = 1/a*np.sinh(s)
xs.append(xi)
ys.append(yi)
dxs.append(dx)
dys.append(dy)
if s == 0:
ax.text(xi-0.75, yi+1.5, '$u$', transform=ax.transData)
ax.annotate('$a^{-1}$',
xy=(xi, yi), xycoords='data',
xytext=(25, -5), textcoords='offset points',
verticalalignment='top', horizontalalignment='left',
arrowprops=dict(arrowstyle='-', shrinkB=7))
ax.quiver(xs, ys, dxs, dys, scale=1.8, color='black', scale_units='xy', angles='xy',
width=0.01)
ax.plot(xs, ys, 'ko')
# http://stackoverflow.com/a/13430772/190597 (lucasg)
ax.set_xlim(-0.1, x.max())
left, right = ax.get_xlim()
low, high = ax.get_ylim()
ax.arrow(0, 0, right, 0, length_includes_head=True, head_width=0.15 )
ax.arrow(0, low, 0, high-low, length_includes_head=True, head_width=0.15 )
ax.text(0.03, 1, '$t$', transform=ax.transAxes)
ax.text(1, 0.47, '$x$', transform=ax.transAxes)
plt.axis('off')
ax.set_aspect('equal')
plt.show()
yields

Matplotlib colormap bug with length-4 arrays

I have some arrays that I need to plot in a loop with a certain colormap. However, one of my arrays is length-4, and I run into this problem:
import numpy as np
import matplotlib as plt
ns = range(2,8)
cm = plt.cm.get_cmap('spectral')
cmap = [cm(1.*i/len(ns)) for i in range(len(ns))]
for i,n in enumerate(ns):
x = np.linspace(0, 10, num=n)
y = np.zeros(n) + i
plt.scatter(x, y, c=cmap[i], edgecolor='none', s=50, label=n)
plt.legend(loc='lower left')
plt.show()
For n=4, it looks like Matplotlib is applying each element of the cmap RGBA-tuple to each value of the array. For the other length arrays, the behavior is expected.
Now, I actually have a much more complicated code and do not want to spend time rewriting the loop. Is there a workaround for this?
It looks like you've bumped into an unfortunate API design in the handling of the c argument. One way to work around the problem is to make c an array with shape (len(x), 4) containing len(x) copies of the desired color. E.g.
ns = range(2,8)
cm = plt.cm.get_cmap('spectral')
cmap = [cm(1.*i/len(ns)) for i in range(len(ns))]
for i,n in enumerate(ns):
x = np.linspace(0, 10, num=n)
y = np.zeros(n) + i
c = np.tile(cmap[i], (len(x), 1))
plt.scatter(x, y, c=c, edgecolor='none', s=50, label=n)
plt.legend(loc='lower left')
plt.show()
Another alternative is to convert the RBG values into a hex string, and pass the alpha channel of the color using the alpha argument. As #ali_m pointed out in a comment, the function matplotlib.colors.rgb2hex makes this easy. If you know the alpha channel of the color is always 1.0, you can remove the code that creates the alpha argument.
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
ns = range(2,8)
cm = plt.cm.get_cmap('spectral')
cmap = [cm(1.*i/len(ns)) for i in range(len(ns))]
for i,n in enumerate(ns):
x = np.linspace(0, 10, num=n)
y = np.zeros(n) + i
c = mpl.colors.rgb2hex(cmap[i])
alpha = cmap[i][3]
plt.scatter(x, y, c=c, edgecolor='none', s=50, label=n, alpha=alpha)
plt.legend(loc='lower left')
plt.show()

How to take draw an average line for a scatter plot

My data is the following:
x = [3,4,5,6,7,8,9,9]
y = [6,5,4,3,2,1,1,2]
And I can obtain the following two graphs.
and
However, what I want is this (an average of all the points along the way):
Is it possible in matplotlib? Or do I have to change the list manually and somehow create:
x = [3,4,5,6,7,8,9]
y = [6,5,4,3,2,1,1.5]
RELEVANT CODE
ax.plot(x, y, 'o-', label='curPerform')
x1,x2,y1,y2 = ax.axis()
x1 = min(x) - 1
x2 = max(x) + 1
ax.axis((x1,x2,(y1-1),(y2+1)))
This can done by generating a new y_mean from your data, then plotting this on the same plot axis using an additional call to ax.plot(), where:
x is the same x used in your scatter plot
y is an iterable with "mean" value you calculate repeated so that its length is equal to x, i.e. y_mean = [np.mean(y) for i in x].
Example:
import matplotlib.pyplot as plt
import random
import numpy as np
# Create some random data
x = np.arange(0,10,1)
y = np.zeros_like(x)
y = [random.random()*5 for i in x]
# Calculate the simple average of the data
y_mean = [np.mean(y)]*len(x)
fig,ax = plt.subplots()
# Plot the data
data_line = ax.plot(x,y, label='Data', marker='o')
# Plot the average line
mean_line = ax.plot(x,y_mean, label='Mean', linestyle='--')
# Make a legend
legend = ax.legend(loc='upper right')
plt.show()
Resulting figure:
Yes, you must do the calculation yourself. plot plots the data you give it. If you want to plot some other data, you need to calculate that data yourself and then plot that instead.
Edit: A quick way to do the calculation:
>>> x, y = zip(*sorted((xVal, np.mean([yVal for a, yVal in zip(x, y) if xVal==a])) for xVal in set(x)))
>>> x
(3, 4, 5, 6, 7, 8, 9)
>>> y
(6.0, 5.0, 4.0, 3.0, 2.0, 1.0, 1.5)

Categories

Resources