Related
This is the real function I am looking to represent in 3D:
y = f(x) = x^2 + 1
The complex function would be as follows:
w = f(z) = z^2 + 1
Where z = x + iy and w = u + iv. These are four dimentions (x, y, u, v), but one can use u for 3D graphing.
We get:
f(x + iy) = x^2 + 2xyi - y^2 + 1
So:
u = x^2 - y^2 + 1
and v = 2xy
This u is what is being used in the code below.
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-100, 101, 150)
y = np.linspace(-100, 101, 150)
X, Y = np.meshgrid(x,y)
U = (X**2) - (Y**2) + 1
fig = plt.figure(dpi = 300)
ax = plt.axes(projection='3d')
ax.plot_surface(X, Y, Z)
plt.show()
The following images are the side-view of the 3D function and the 2D plot for reference. I do not think they are alike.
Likewise, here is the comparison between the 3 side-view and the 2D plot of w = z^3 + 1. They seem to differ as well.
I have not been able to find too many resources regarding plotting in 3D using complex numbers. Because of this and the possible discrepancies mentioned before, I think the code must be flawed, but I can't figure out why. I would be grateful if you could correct me or advise me on any changes.
The inspiration came from Welch Labs' 'Imaginary Numbers are Real' YouTube series where he shows a jaw-dropping representation of the complex values of the function I have been tinkering with.
I was just wondering if anybody could point out any flaws in my reasoning or the execution of my idea since this code would be helpful in explaining the importance of complex numbers to HS students.
Thank you very much for your time.
The f(z) = z^2 + 1 projection (that is, side-view) looks OK to me. You can use this technique to add the projections; this code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
def f(z):
return z**2 + 1
def freal(x, y):
return x**2 - y**2 + 1
x = np.linspace(-100, 101, 150)
y = np.linspace(-100, 101, 150)
yproj = 0 # value of y for which to project xu axes
xproj = 0 # value of x to project onto yu axes
X, Y = np.meshgrid(x,y)
Z = X + 1j * Y
W = f(Z)
U = W.real
fig = plt.figure()
ax = plt.axes(projection='3d')
## surface
ax.plot_surface(X, Y, U, alpha=0.7)
# xu projection
xuproj = freal(x, yproj)
ax.plot(x, xuproj, zs=101, zdir='y', color='red', lw=5)
ax.plot(x, xuproj, zs=yproj, zdir='y', color='red', lw=5)
# yu projection
yuproj = freal(xproj, y)
ax.plot(y, yuproj, zs=101, zdir='x', color='green', lw=5)
ax.plot(y, yuproj, zs=xproj, zdir='x', color='green', lw=5)
# partially reproduce https://www.youtube.com/watch?v=T647CGsuOVU&t=107s
x = np.linspace(-3, 3, 150)
y = np.linspace(0, 3, 150)
X, Y = np.meshgrid(x,y)
U = f(X + 1j*Y).real
fig = plt.figure()
ax = plt.axes(projection='3d')
## surface
ax.plot_surface(X, Y, U, cmap=cm.jet)
ax.set_box_aspect( (np.diff(ax.get_xlim())[0],
np.diff(ax.get_ylim())[0],
np.diff(ax.get_zlim())[0]))
#ax.set_aspect('equal')
plt.show()
gives this result:
and
The axis ticks don't look very good: you can investigate plt.xticks or ax.set_xticks (and yticks, zticks) to fix this.
There is a way to visualize complex functions using colour as a fourth dimension; see complex-analysis.com for examples.
I try to simulate the confidence limit of the mean value in the figure above.
But something wrong with my code. The result is far different than the above figure. I used confidence interval to obtain the slope and intercept. Could someone one give me hints ? Thanks
Here is my code.
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import t
from scipy import stats
x = np.array([3,7,11,15,18,27,29,30,30,31,31, 32,33,33,34,36,36,36,37,38,39,39,39,40,41,42,42,43,44,45,46,47,50])
y = np.array([5,11,21,16,16,28,27,25,35,30,40,32,34,32,34,37,38,34,36,38,37,36,45,39,41,40,44,37,44,46,46,49,51])
mean_x = np.mean(x)
n = len(x)
res = stats.linregress(x, y)
tinv = lambda p, df: abs(t.ppf(p/2, df))
ts = tinv(0.05, n-2)
bins = np.linspace(0,3,54)
plt.plot(x, y, 'o', label='Data Points')
plt.plot(x,res.intercept + res.slope*x, 'c', label='fitted line')
plt.plot(x,(res.intercept+ts*res.intercept_stderr)+(res.slope + ts*res.stderr)*x,'b', label='Upper Limit')
plt.plot(x,(res.intercept-ts*res.intercept_stderr)+(res.slope - ts*res.stderr)*x, 'g' ,label='Lower Limit')
plt.legend()
plt.show()
There are lots of ways to estimate the error of your model. As an example, here is a linear fit of the error:
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import t
from scipy import stats
x = np.array([3,7,11,15,18,27,29,30,30,31,31, 32,33,33,34,36,36,36,37,38,39,39,39,40,41,42,42,43,44,45,46,47,50])
y = np.array([5,11,21,16,16,28,27,25,35,30,40,32,34,32,34,37,38,34,36,38,37,36,45,39,41,40,44,37,44,46,46,49,51])
n = len(x)
res = stats.linregress(x, y)
tinv = lambda p, df: abs(t.ppf(p/2, df))
ts = tinv(0.05, n - 2)
bins = np.linspace(0, 3, 54)
pred = res.intercept + res.slope * x
errors = np.abs(y - pred)
plt.scatter(x, y, label='Data Points')
plt.plot(x, pred, 'c', label='fitted line')
error_res = stats.linregress(x, errors)
pred_err = error_res.intercept + error_res.slope * x
plt.plot(x, pred + 1 * pred_err, label='Upper confidence')
plt.plot(x, pred - 1 * pred_err, label='Lower confidence')
plt.legend()
plt.savefig('hey')
Based on this question, How to plot a function ax^2+bxy+cy^2+d=0 in Python?. I cite code from #Michael Szczesny's answer:
import matplotlib.pyplot as plt
def f(x,y):
return 0.01*x**2+0.008*x*y-0.013*y**2+0.15*x+0.003*y+1.0097
x = np.arange(-10.0,10.0,0.1)
y = np.arange(-10.0,10.0,0.1)
X, Y = np.meshgrid(x,y)
plt.contour(x, y, f(X, Y), [0]);
If I try to add a label for curve f(X,Y):
import matplotlib.pyplot as plt
def f(x,y):
return 0.01*x**2+0.008*x*y-0.013*y**2+0.15*x+0.003*y+1.0097
x = np.arange(-10.0,10.0,0.1)
y = np.arange(-10.0,10.0,0.1)
X, Y = np.meshgrid(x,y)
plt.contour(x, y, f(X, Y), [0], label='class 1')
plt.legend()
plt.show()
Error: UserWarning: The following kwargs were not used by contour:
The answer from 2021 no longer works on Matplotlib 3.5.2. It will not render correctly:
Instead, we should use legend_elements with custom labels:
import matplotlib.pyplot as plt
import numpy as np
def f(x, y):
return 0.01 * x ** 2 + 0.008 * x * y - 0.013 * y ** 2 + 0.15 * x + 0.003 * y + 1.0097
x = np.arange(-10.0, 10.0, 0.1)
y = np.arange(-10.0, 10.0, 0.1)
X, Y = np.meshgrid(x, y)
cnt = plt.contour(x, y, f(X, Y), [0])
artists, labels = cnt.legend_elements()
plt.legend(artists, ['class 1'])
plt.show()
# multiple levels
levels = [0, 1, 2, 3]
cnt = plt.contour(x, y, f(X, Y), levels)
artists, labels = cnt.legend_elements()
custom_labels = []
for level, contour in zip(levels, cnt.collections):
custom_labels.append(f'level {level}')
plt.legend(artists, custom_labels, bbox_to_anchor=[1.01, 1], loc='upper left')
plt.tight_layout()
plt.show()
It will produce the correct result:
Matplotlib's contour seems to be designed with the idea of drawing a colorbar instead of a legend. However, in this case, with only one level, a legend can be forced assigning a label to individual contours:
import matplotlib.pyplot as plt
import numpy as np
def f(x, y):
return 0.01 * x ** 2 + 0.008 * x * y - 0.013 * y ** 2 + 0.15 * x + 0.003 * y + 1.0097
x = np.arange(-10.0, 10.0, 0.1)
y = np.arange(-10.0, 10.0, 0.1)
X, Y = np.meshgrid(x, y)
cnt = plt.contour(x, y, f(X, Y), [0])
cnt.collections[0].set_label('class 1')
plt.legend()
plt.show()
Here is an example of the same approach with multiple levels:
levels = [0, 1, 2, 3]
cnt = plt.contour(x, y, f(X, Y), levels)
for level, contour in zip(levels, cnt.collections):
contour.set_label('level {level}')
plt.legend(bbox_to_anchor=[1.01, 1], loc='upper left')
plt.tight_layout()
plt.show()
import matplotlib.pyplot as plt
import matplotlib.pyplot as plt
def f(x):
return (1 + np.exp(-x)) / (1 + x ** 4)
def lagrange(x, y, x0):
ret = []
for j in range(len(y)):
numerator = 1.0
denominator = 1.0
for k in range(len(x)):
if k != j:
numerator *= (x0 - x[k])
denominator *= (x[j] - x[k])
ret.append(y[j] * (numerator / denominator))
return ret
plt.plot(x, lagrange(x, f(x), 5), label="Polynom")
plt.plot(x, f(x), label="Function")
plt.legend(loc='upper left')
plt.show()
I need build graph with original function and with Lagrange polynomial. I newbie to matplotlib library(and to python in general), so i want to confirm that i am right
SciPy has lagrange, here is an example:
import matplotlib.pyplot as plt
import numpy as np
from scipy.interpolate import lagrange
def f(x):
return (1 + np.exp(-x)) / (1 + x ** 4)
x = np.linspace(-1, 1, 10)
x2 = np.linspace(-1, 1, 100)
y = f(x)
p = lagrange(x, y)
plt.plot(x, f(x), "o", label="Function")
plt.plot(x2, np.polyval(p, x2), label="Polynom")
plt.legend(loc='upper right')
the output:
If you want to calculate the interpolated curve by the lagrange formula:
den = x[:, None] - x[None, :]
num = x2[:, None] - x[None, :]
with np.errstate(divide='ignore', invalid='ignore'):
div = num[:, None, :] / den[None, :, :]
div[~np.isfinite(div)] = 1
y2 = (np.product(div, axis=-1) * y).sum(axis=1)
plt.plot(x, y, "o")
plt.plot(x2, y2)
The code uses numpy broadcast to speedup the calculation.
import matplotlib.pyplot as plt
import numpy as np
def domain():
x = np.arange(0, 10, 0.001)
f1 = lambda x: (2*x - x**2)**0.5
plt.plot(x, f1(x), label = '$y = \sqrt{2x - x^2}$')
plt.plot(f1(x), x, label = '$x = \sqrt{2y - y^2}$')
plt.xlabel('X')
plt.ylabel('Y')
plt.legend(loc='best')
axes = plt.gca()
axes.set_xlim([0, 5])
axes.set_ylim([0, 5])
plt.show()
domain()
How can I make use of the fill_between() to fill the area between the 2 lines? In other words, how can I fill the small flower petal between the green and blue lines?
#user 5061 was right on the code, inverse function was off there
import matplotlib.pyplot as plt
import numpy as np
def domain():
x = np.arange(0, 10, 0.001)
f1 = lambda x: (2*x - x**2)**0.5
f2 = lambda x: 1 - (1-x*x)**0.5 # other part is f2 = lambda x: 1 + (1-x*x)**0.5
plt.plot(x, f1(x), label = '$y = \sqrt{2x - x^2}$')
plt.plot(f1(x), x, label = '$x = \sqrt{2y - y^2}$')
plt.fill_between(x, f1(x), f2(x), where=f1(x)>=f2(x), interpolate=True, color='yellow')
plt.xlabel('X')
plt.ylabel('Y')
plt.legend(loc='best')
axes = plt.gca()
axes.set_xlim([0, 5])
axes.set_ylim([0, 5])
plt.show()
domain()
Not taking the positive component 1 + (1-x*x)**0.5 since it doesn't affect the intersection.
You can use fill_between() and fill between your two lines when a condition is met.
(I altered a bit your code, since the way you wrote it i had to find the inverse function of f1)
import matplotlib.pyplot as plt
import numpy as np
def domain():
x = np.arange(0, 2, 0.001)
f = lambda x: x**0.5
g = lambda x: x**2
plt.plot(x, f(x), label = '$y = \sqrt{2x - x^2}$')
plt.plot(x, g(x), label = '$x = \sqrt{2y - y^2}$')
plt.xlabel('X')
plt.ylabel('Y')
plt.legend(loc='best')
plt.fill_between(x, f(x), g(x),where=f(x) > g(x))
axes = plt.gca()
axes.set_xlim([0, 2])
axes.set_ylim([0, 2])
plt.show()
domain()