Plot a 3d plot in Python from elements in a 2D matrix - python

I would like to plot a function f(x1, x2) of two variables x1 and x2 in a 3D plot. The function is containted in a 2D matrix, with x1 forming the rows and x2 the columns. How do I go about plotting this?
I have tried defining my function f as
x1_axis = np.arange(0, 10, 0.1)
x2_axis = np.arange(0, 10, 0.1)
f = [fun[x1, x2] for x1 in x1_axis and x2 in x2_axis]
where 'fun' is my matrix storing the values. This raises the error 'ValueError: The truth value of an array with more than one element is ambiguous.'
Is there any other way to implement this?

You can create a 2d function matrix, by passing a mesh into a funcion.
import numpy as np
import matplotlib.pyplot as plt
def fun(x1, x2):
# Define your function here
return x1 + x2
x1_axis = np.arange(0, 10, 0.1)
x2_axis = np.arange(0, 10, 0.1)
X1, X2 = np.meshgri
d(x1_axis, x2_axis)
F = fun(X1, X2)
You can than plot that using
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(X1, X2, F)
plt.show()

Related

Plot minimum of two arrays

I have two arrays for x-values and two corresponding arrays for y-values which I wish to plot.
import numpy as np
import matplotlib.pyplot as plt
x1 = np.linspace(-2,2,100)
x2 = np.linspace(0,4,100)
y1 = x1**2+1
y2 = (x2-1.5)**2
plt.plot(x1,y1)
plt.plot(x2,y2)
plt.show()
This produces the following plot.
But instead of this, I want to plot only the minima of these two curves, i.e. only the region of y1 where y1<y2 and only the region of y2 where y2<y1. Something like this.
Since x1 and x2 are different, I can't use np.minimum(). Is there an efficient way to do this with numpy and/or matplotlib?
I would like to have a general approach that also works when y1 and y2 are not determined from some function I know, but are taken from e.g. a dataset.
You could interpolate both functions onto a common x, and then take their minimum.
import numpy as np
import matplotlib.pyplot as plt
x1 = np.linspace(-2, 2, 100)
x2 = np.linspace(0, 4, 100)
y1 = x1 ** 2 + 1
y2 = (x2 - 1.5) ** 2
plt.plot(x1, y1, ls=':')
plt.plot(x2, y2, ls=':')
xc = np.sort(np.concatenate([x1, x2]))
y1c = np.interp(xc, x1, y1, left=y2.max(), right=y2.max())
y2c = np.interp(xc, x2, y2, left=y1.max(), right=y1.max())
plt.plot(xc, np.minimum(y1c, y2c), lw=10, alpha=0.4)
plt.show()
One solution is to cross compute the functions with the other xs and mask:
import numpy as np
import matplotlib.pyplot as plt
x1 = np.linspace(-2,2,100)
x2 = np.linspace(0,4,100)
y1 = x1**2+1
y1b = x2**2+1
y2 = (x2-1.5)**2
y2b = (x1-1.5)**2
plt.plot(x1, np.where(y1<y2b, y1, np.nan))
plt.plot(x2, np.where(y2<y1b, y2, np.nan))
plt.show()
output:

Finding intersection of two graphs with different numpy sizes

I would like to find the intersection of two graphs. It took me 674 points to plot the first graph and only 14 points to plot the second graph.
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
df = pd.read_csv("test1.csv",,skiprows=range(9),names=['A', 'B', 'C','D'])
df2 = pd.read_csv("test2.csv",skiprows=range(1),names=['X','Y'])
x1 = df['A'].tolist()
x1 = np.array(x1)
y1 = df['D'].tolist()
y1 = np.array(y1)
x2 = df2['X'].tolist()
x2 = np.array(x2)
y2 = df2['Y'].tolist()
y2 = np.array(y2)
idx = np.argwhere(np.diff(np.sign(y1 - y2))).flatten()
fig, ax = plt.subplots()
ax.plot(x1, y1, 'blue')
ax.plot(x2, y2, 'red')
plt.show()
However, I am getting this error from the code above due to the different sizes of numpy. Any ways I can solve this?
operands could not be broadcast together with shapes (674,) (14,)
You should compute interpolations of both curves with scipy.interpolate.interp1d, then you can calculate indeces of intersection points with the method you used.
I assume you have two curves with x1, x2, y1 and y2 coordinates, with different lengths and x axis limits:
x1 = np.linspace(-2, 12, 674)
x2 = np.linspace(0, 8, 14)
y1 = np.sin(x1)
y2 = np.cos(x2) + 1
So, you have to compute interpolation functions:
f1 = interp1d(x1, y1, kind = 'linear')
f2 = interp1d(x2, y2, kind = 'linear')
Then, you need to evaluate new curves on a common x axis, so new curves will have the same length:
xx = np.linspace(max(x1[0], x2[0]), min(x1[-1], x2[-1]), 1000)
y1_interp = f1(xx)
y2_interp = f2(xx)
Finally, you can compute indices of interpolation points as you already did:
idx = np.argwhere(np.diff(np.sign(y1_interp - y2_interp))).flatten()
Complete Code
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import interp1d
x1 = np.linspace(-2, 12, 674)
x2 = np.linspace(0, 8, 14)
y1 = np.sin(x1)
y2 = np.cos(x2) + 1
f1 = interp1d(x1, y1, kind = 'linear')
f2 = interp1d(x2, y2, kind = 'linear')
xx = np.linspace(max(x1[0], x2[0]), min(x1[-1], x2[-1]), 1000)
y1_interp = f1(xx)
y2_interp = f2(xx)
idx = np.argwhere(np.diff(np.sign(y1_interp - y2_interp))).flatten()
fig, ax = plt.subplots()
ax.plot(x1, y1, 'blue', label = 'y1')
ax.plot(x2, y2, 'red', label = 'y2')
for index in idx:
ax.plot(xx[index], y1_interp[index], marker = 'o', markerfacecolor = 'black', markeredgecolor = 'black')
plt.show()
Plot

In matplotlib, how to use a secondary axis that is input to the function together with the primary axis?

I have a function with two arguments (x1, x2). I would like to plot the function with its result on the y axis, x1 on the x axis and x2 on the right y axis (secondary axis).
My problem is that the values of the x1 and x2 axes do not correspond together to the function point.
For example:
I want the value read on the primary y axis to match the x1 and x2 inputs of the other two axes.
Code:
x1 = np.linspace(0, 10, 10)
x2 = np.linspace(0, 5, 10)
f = lambda x1, x2: np.exp(-x1) / 10 + np.exp(-x2) / 10
resp = []
for i, j in zip(x1, x2):
resp.append(f(i, j))
resp = np.array(resp)
fig, ax1 = plt.subplots()
ax2 = ax1.twinx()
ax1.plot(x1, resp)
ax1.set_xlabel('(x1)')
ax1.grid(True)
ax1.set_ylabel('(y)')
ax2.set_ylabel('(x2)')
ax2.set_yticks(x2)
plt.xticks(np.arange(min(x1), max(x1)))
plt.show()
If your input arguments are in lockstep, you do not have a function of two arguments. A function of two arguments has two independent inputs. Your inputs are dependent, so rather than writing f(x1, x2), you have f(x, g(x)), which is just f'(x). In the specific example that you have x1 = np.linspace(0, 10, 10). Rather than writing x2 = np.linspace(0, 5, 10), you can just write x2 = 0.5 * x1. The exponential you have can be written as
x = np.linspace(0, 10, 10)
y = np.exp(-x) / 10 + np.exp(-x / 2) / 10
Notice that you do not need a function definition or a loop to compute the y values. Using a loop defeats the entire purpose of using numpy. Your original five lines could have been reduced to y = np.exp(-x1) / 10 + np.exp(-x2) / 10 in the same way.
Now if you want to see the secondary x values in the plot of y vs x, you can take a page out of the tutorials and do something like:
fig, ax1 = plt.subplots()
ax1.plot(x, y)
ax1.set_xlabel('(x1)')
ax1.grid(True)
ax1.set_ylabel('(y)')
ax2 = ax1.secondary_xaxis('top', functions=(lambda x: x / 2, lambda x: 2 * x))
ax2.set_xlabel('(x2)')
plt.show()
The result shows "both" inputs in lockstep:
Now if you really did want to have a function of two variables, then any combination of the inputs would produce a valid y value. In this case, you would have to use a feature of numpy called broadcasting, which matches array dimensions by lining them up on the right.
Let's say you defined one of the inputs as a transpose:
x1 = np.linspace(0, 10, 10) # Shape (10,)
x2 = np.linspace(0, 5, 10).reshape(-1, 1) # Shape (10, 1)
The result of an operation on these values will be a (10, 10) 2D array. Now you can meaningfully compute y as function of two independent variables:
y = np.exp(-x1) / 10 + np.exp(-x2) / 10
To plot such an array, you will need two x-axes and a y-axis, in other words a 3D plot. Here is one way to display something like that in matplotlib:
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
fig, ax = plt.subplots(subplot_kw={'projection': '3d'})
s = ax.plot_surface(x1, x2, y, cm=cm.jet)
ax.set_xlabel('(x1)')
ax.set_ylabel('(x2)')
ax.set_zlabel('(y)')
fig.colorbar(s)
Here is the resulting plot:
It is up to you which representation of a function you want.

python add values to Line3DCollection

I want to plot multiple lines in a 3d axis plot.
I'm plotting the trajectory of a light ray, each line should be colored depending on the time of propagation for each path.
I want to plot them independently, since I saved them in a binary three structure in which any ray may follow two different trajectories.
I used Line3DCollection, but it doesn't seem to be the right way.
In few words,
import numpy as np
x = np.linspace(0,1,100)
y = x
z = x
t = np.linspace(0,1,100)
#here I need to plot these points coloring them in function of t
x1 = np.linspace(1,2,100)
y1 = x
z1 = x
t1 = np.linspace(1,2,100)
#I need to plot also this used the same colorbar used for former values
x2 = -np.linspace(1,2,100)
y2 = x
z2 = x
t2 = np.linspace(1,2,100)
#idem
having all the lines colored with the same colorbar scale, like this
plot.
I tried with the following but the output is not like I expected
import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d.art3d import Line3DCollection
x = np.linspace(0,1,100)
y = x
z = x
t = np.linspace(0,1,100)
points = np.array([x,y,z]).transpose().reshape(-1,1,3)
segs = np.concatenate([points[:-1],points[1:]],axis=1)
lc = Line3DCollection(segs, cmap=plt.get_cmap('jet'))
lc.set_array(t)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.add_collection3d(lc)
x1 = np.linspace(1,2,100)
y1 = x1
z1 = x1
t1 = np.linspace(1,2,100)
points1 = np.array([x1,y1,z1]).transpose().reshape(-1,1,3)
segs1 = np.concatenate([points1[:-1],points1[1:]],axis=1)
lc = Line3DCollection(segs1, cmap=plt.get_cmap('jet'))
lc.set_array(t1)
lc.update_scalarmappable()
ax.add_collection3d(lc)
x2 = np.linspace(1,2,100)
y2 = -x2+2
z2 = -x2+2
t2 = np.linspace(1,2,100)
points2 = np.array([x2,y2,z2]).transpose().reshape(-1,1,3)
segs2 = np.concatenate([points2[:-1],points2[1:]],axis=1)
lc = Line3DCollection(segs2, cmap=plt.get_cmap('jet'))
lc.set_array(t1)
lc.update_scalarmappable()
ax.add_collection3d(lc)
ax.set_xlim(0, 2)
ax.set_ylim(0, 2)
ax.set_zlim(0, 2)
fig.colorbar(lc)
plt.show()
plotting this plot
Thanks in advance

Plot equation showing a circle

The following formula is used to classify points from a 2-dimensional space:
f(x1,x2) = np.sign(x1^2+x2^2-.6)
All points are in space X = [-1,1] x [-1,1] with a uniform probability of picking each x.
Now I would like to visualize the circle that equals:
0 = x1^2+x2^2-.6
The values of x1 should be on the x-axis and values of x2 on the y-axis.
It must be possible but I have difficulty transforming the equation to a plot.
You can use a contour plot, as follows (based on the examples at http://matplotlib.org/examples/pylab_examples/contour_demo.html):
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-1.0, 1.0, 100)
y = np.linspace(-1.0, 1.0, 100)
X, Y = np.meshgrid(x,y)
F = X**2 + Y**2 - 0.6
plt.contour(X,Y,F,[0])
plt.show()
This yields the following graph
Lastly, some general statements:
x^2 does not mean what you think it does in python, you have to use x**2.
x1 and x2 are terribly misleading (to me), especially if you state that x2 has to be on the y-axis.
(Thanks to Dux) You can add plt.gca().set_aspect('equal') to make the figure actually look circular, by making the axis equal.
The solution of #BasJansen certainly gets you there, it's either very inefficient (if you use many grid points) or inaccurate (if you use only few grid points).
You can easily draw the circle directly. Given 0 = x1**2 + x**2 - 0.6 it follows that x2 = sqrt(0.6 - x1**2) (as Dux stated).
But what you really want to do is to transform your cartesian coordinates to polar ones.
x1 = r*cos(theta)
x2 = r*sin(theta)
if you use these substitions in the circle equation you will see that r=sqrt(0.6).
So now you can use that for your plot:
import numpy as np
import matplotlib.pyplot as plt
# theta goes from 0 to 2pi
theta = np.linspace(0, 2*np.pi, 100)
# the radius of the circle
r = np.sqrt(0.6)
# compute x1 and x2
x1 = r*np.cos(theta)
x2 = r*np.sin(theta)
# create the figure
fig, ax = plt.subplots(1)
ax.plot(x1, x2)
ax.set_aspect(1)
plt.show()
Result:
How about drawing x-values and calculating the corresponding y-values?
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-1, 1, 100, endpoint=True)
y = np.sqrt(-x**2. + 0.6)
plt.plot(x, y)
plt.plot(x, -y)
produces
This can obviously be made much nicer, but this is only for demonstration...
# x**2 + y**2 = r**2
r = 6
x = np.linspace(-r,r,1000)
y = np.sqrt(-x**2+r**2)
plt.plot(x, y,'b')
plt.plot(x,-y,'b')
plt.gca().set_aspect('equal')
plt.show()
produces:
Plotting a circle using complex numbers
The idea: multiplying a point by complex exponential () rotates the point on a circle
import numpy as np
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.pyplot as plt
num_pts=20 # number of points on the circle
ps = np.arange(num_pts+1)
# j = np.sqrt(-1)
pts = np.exp(2j*np.pi/num_pts *(ps))
fig, ax = plt.subplots(1)
ax.plot(pts.real, pts.imag , '-o')
ax.set_aspect(1)
plt.show()

Categories

Resources