Plotting a polynomial using Matplotlib and coeffiecients - python

My code is:
import numpy as np
import matplotlib as plt
polyCoeffiecients = [1,2,3,4,5]
plt.plot(PolyCoeffiecients)
plt.show()
The result for this is straight lines that describe the points in 1,2,3,4,5 and the straight lines between them, instead of the polynomial of degree 5 that has 1,2,3,4,5 as its coeffiecients ( P(x) = 1 + 2x + 3x + 4x + 5x)
How am i suppose to plot a polynomial with just its coefficients?

Eyzuky, see if this is what you want:
import numpy as np
from matplotlib import pyplot as plt
def PolyCoefficients(x, coeffs):
""" Returns a polynomial for ``x`` values for the ``coeffs`` provided.
The coefficients must be in ascending order (``x**0`` to ``x**o``).
"""
o = len(coeffs)
print(f'# This is a polynomial of order {o}.')
y = 0
for i in range(o):
y += coeffs[i]*x**i
return y
x = np.linspace(0, 9, 10)
coeffs = [1, 2, 3, 4, 5]
plt.plot(x, PolyCoefficients(x, coeffs))
plt.show()

You could approximately draw the polynomial by getting lots of x-values and using np.polyval() to get the y-values of your polynomial at the x-values. Then you could just plot the x-vals and y-vals.
import numpy as np
import matplotlib.pyplot as plt
curve = np.array([1,2,3,4,5])
x = np.linspace(0,10,100)
y = [np.polyval(curve, i) for i in x]
plt.plot(x,y)

A very pythonic solution is to use list comprehension to calculate the values for the function.
import numpy as np
from matplotlib import pyplot as plt
x = np.linspace(0, 10, 11)
coeffs = [1, 2, 3, 4, 5]
y = np.array([np.sum(np.array([coeffs[i]*(j**i) for i in range(len(coeffs))])) for j in x])
plt.plot(x, y)
plt.show()

Generic, vectorized implementation:
from typing import Sequence, Union
import numpy as np
import matplotlib.pyplot as plt
Number = Union[int, float, complex]
def polyval(coefficients: Sequence[Number], x: Sequence[Number]) -> np.ndarray:
# expand dimensions to allow broadcasting (constant time + inexpensive)
# axis=-1 allows for arbitrarily shaped x
x = np.expand_dims(x, axis=-1)
powers = x ** np.arange(len(coefficients))
return powers # coefficients
def polyplot(coefficients: Sequence[Number], x: Sequence[Number]) -> None:
y = polyval(coefficients, x)
plt.plot(x, y)
polyplot(np.array([0, 0, -1]), np.linspace(-10, 10, 210))
plt.show()

Related

Set range and label for axis

I'm ploting data from an array A of size 10*10, each element A[x,y] is calculated by a function f(x,y) where x and y are in the range (-3, 3)
import numpy as np
import matplotlib.pyplot as plt
def f(x,y):
return ...
s = 10
a = np.linspace(-3, 3, s)
fxy = np.array([f(x,y) for x in a for y in a]).reshape((s, s))
plt.xticks(labels=np.arange(-3, 3), ticks=range(6))
plt.yticks(labels=np.arange(-3, 3), ticks=range(6))
plt.imshow(fxy)
So the labels on xy-axis are not that I want since x and y are taken from the range (-3, 3) (not from (0, 10) which is the size of the 2d-array). How can I set these labels correctly?
You can use the extent argument in imshow and it will handle the tick labels automatically.
import numpy as np
import matplotlib.pyplot as plt
def f(x,y):
return ...
s = 10
a = np.linspace(-3, 3, s)
fxy = np.array([f(x,y) for x in a for y in a]).reshape((s, s))
plt.imshow(fxy, extent = [a[0], a[-1], a[-1], a[0]])

fill the common area under both curve

How I can fill the common area under both the curve?
import matplotlib.pyplot as plt
import numpy as np
import scipy.special as sp
x = np.linspace(-4, 4, num=1000)
r = abs(x)
zeta = 1.0
psi_STO = (zeta**3 / np.pi)**(0.5) * np.exp(-zeta * r)
plt.figure(figsize=(4, 3))
plt.plot(x, psi_STO, color="C0")
plt.plot(x + 3, psi_STO, color="C0")
plt.show()
If I use:
plt.fill_betweenx(psi_STO, -1, 4, color="C1")
I am getting a plot as:
You can use fill_between. As your Xs are not aligned, you need to make a bit of calculations first to find the common range. This will depend on the number of points in the linspace. Here, I computed it manually: as the shift is of 3, there are 375 points difference (250 per unit).
import matplotlib.pyplot as plt
import numpy as np
import scipy.special as sp
x = np.linspace(-4, 4, num=1000)
r = abs(x)
zeta = 1.0
psi_STO = (zeta**3 / np.pi)**(0.5) * np.exp(-zeta * r)
plt.figure(figsize=(4, 3))
plt.plot(x, psi_STO, color="C0")
plt.plot(x + 3, psi_STO, color="C0")
x_common = (x+3)[:-375]
min_common = np.min([psi_STO[:-375], psi_STO[375:]], axis=0)
plt.plot(x_common, min_common, color='r')
plt.fill_between(x_common,min_common, color="#FFB0B0")
plt.show()
output:
This?
dx = 3 # x shift
di = int(dx/(x[1]-x[0])) # index shift
plt.fill_between(x[di:], np.minimum(psi_STO[:-di], psi_STO[di:]))

Can I draw linear interpolation graph for set of x and y values using numpy

Can I draw linear interpolation graph for set of x and y values using numpy ?
import numpy as np
import matplotlib.pyplot as plt
x =[700,701,702,702]
y =[46,48,45,45]
​
​
x_new= [701]
​
y_new = np.interp(x_new, x, y)
print(y_new)
[48.]
plt.plot(x, y, "og-", x_new, y_new, "or");
How can I print the function used in graph?
Are you referring to the function used in the graph as the equation of the line? If so, to get the equation of the line:
from numpy import ones,vstack
from numpy.linalg import lstsq
x =[700,701,702,702]
y =[46,48,45,45]
A = vstack([x_coords,ones(len(x_coords))]).T
m, c = lstsq(A, y_coords)[0]
print("Line Solution is y = {m}x + {c}".format(m=m,c=c))

Peak finding and analysis on python

I have written a code that reads in my data file and plots it and then fits it and finds the peaks however I have 6 peaks and the code is only currently fitting 2 of the peaks and isn't returning any data on them by code is as follows:
from scipy.optimize import curve_fit
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import find_peaks
data = np.genfromtxt("C:\\Users\\lenovo laptop\\practice_data_ll16ame1.dat", skip_header = 15)
x = data[: , 0]
y = data[: , 1]
plt.plot(x,y)
plt.show()
def func(x, *params):
y = np.zeros_like(x)
for i in range(0, len(params), 3):
ctr = params[i]
amp = params[i+1]
wid = params[i+2]
y = y + amp * np.exp( -((x - ctr)/wid)**2)
return y
guess = [0, 60000, 80, 1000, 60000, 80]
for i in range(12):
guess += [60+80*i, 46000, 25]
popt, pcov = curve_fit(func, x, y, p0=guess)
fit = func(x, *popt)
plt.plot(x, y)
plt.plot(x, fit , 'r-')
plt.show()
When I looked at the plot of your custom function, it was clear that the majority of points were in a more-or-less horizontal line, so the function wouldn't fit well to your peaks. Because there is no noise and the peaks are so prominent, you just need to pass the y values and a threshold to the find_peaks function.
By implementing find_peaks instead of your custom function, you get the following code:
from scipy.optimize import curve_fit
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import find_peaks
data = np.genfromtxt("C:\\Users\\lenovo laptop\\practice_data_ll16ame1.dat", skip_header = 15)
x = data[: , 0]
y = data[: , 1]
points = find_peaks(y, height = 100)
plt.plot(x, y)
for i in points[0]:
plt.scatter(x[i], y[i])
plt.show()
Find_peaks returns a tuple consisting of two things:
1. The index of the peaks ( points[0] in the code above)
2. The height of each peak (points[1])
The code yields the following plot, which I believe is what you want:

3D plot: smooth plot on x axis

I have a 3D polygon plot and want to smooth the plot on the y axis (i.e. I want it to look like 'slices of a surface plot').
Consider this MWE (taken from here):
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.collections import PolyCollection
import matplotlib.pyplot as plt
from matplotlib import colors as mcolors
import numpy as np
from scipy.stats import norm
fig = plt.figure()
ax = fig.gca(projection='3d')
xs = np.arange(-10, 10, 2)
verts = []
zs = [0.0, 1.0, 2.0, 3.0]
for z in zs:
ys = np.random.rand(len(xs))
ys[0], ys[-1] = 0, 0
verts.append(list(zip(xs, ys)))
poly = PolyCollection(verts, facecolors=[mcolors.to_rgba('r', alpha=0.6),
mcolors.to_rgba('g', alpha=0.6),
mcolors.to_rgba('b', alpha=0.6),
mcolors.to_rgba('y', alpha=0.6)])
poly.set_alpha(0.7)
ax.add_collection3d(poly, zs=zs, zdir='y')
ax.set_xlabel('X')
ax.set_xlim3d(-10, 10)
ax.set_ylabel('Y')
ax.set_ylim3d(-1, 4)
ax.set_zlabel('Z')
ax.set_zlim3d(0, 1)
plt.show()
Now, I want to replace the four plots with normal distributions (to ideally form continuous lines).
I have created the distributions here:
def get_xs(lwr_bound = -4, upr_bound = 4, n = 80):
""" generates the x space betwee lwr_bound and upr_bound so that it has n intermediary steps """
xs = np.arange(lwr_bound, upr_bound, (upr_bound - lwr_bound) / n) # x space -- number of points on l/r dimension
return(xs)
xs = get_xs()
dists = [1, 2, 3, 4]
def get_distribution_params(list_):
""" generates the distribution parameters (mu and sigma) for len(list_) distributions"""
mus = []
sigmas = []
for i in range(len(dists)):
mus.append(round((i + 1) + 0.1 * np.random.randint(0,10), 3))
sigmas.append(round((i + 1) * .01 * np.random.randint(0,10), 3))
return mus, sigmas
mus, sigmas = get_distribution_params(dists)
def get_distributions(list_, xs, mus, sigmas):
""" generates len(list_) normal distributions, with different mu and sigma values """
distributions = [] # distributions
for i in range(len(list_)):
x_ = xs
z_ = norm.pdf(xs, loc = mus[i], scale = sigmas[0])
distributions.append(list(zip(x_, z_)))
#print(x_[60], z_[60])
return distributions
distributions = get_distributions(list_ = dists, xs = xs, mus = mus, sigmas = sigmas)
But adding them to the code (with poly = PolyCollection(distributions, ...) and ax.add_collection3d(poly, zs=distributions, zdir='z') throws a ValueError (ValueError: input operand has more dimensions than allowed by the axis remapping) I cannot resolve.
The error is caused by passing distributions to zs where zs expects that when verts in PolyCollection has shape MxNx2 the object passed to zs has shape M. So when it reaches this check
cpdef ndarray broadcast_to(ndarray array, shape):
# ...
if array.ndim < len(shape):
raise ValueError(
'input operand has more dimensions than allowed by the axis '
'remapping')
# ...
in the underlying numpy code, it fails. I believe this occurs because the number of dimensions expected (array.ndim) is less than the number of dimensions of zs (len(shape)). It is expecting an array of shape (4,) but receives an array of shape (4, 80, 2).
This error can be resolved by using an array of the correct shape - e.g. zs from the original example or dists from your code. Using zs=dists and adjusting the axis limits to [0,5] for x, y, and z gives
This looks a bit odd for two reasons:
There is a typo in z_ = norm.pdf(xs, loc = mus[i], scale = sigmas[0]) which gives all the distributions the same sigma, it should be z_ = norm.pdf(xs, loc = mus[i], scale = sigmas[i])
The viewing geometry: the distributions have the positive xz plane as their base, this is also the plane we are looking through.
Changing the viewing geometry via ax.view_init will yield a clearer plot:
Edit
Here is the complete code which generates the plot shown,
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.collections import PolyCollection
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import numpy as np
from scipy.stats import norm
np.random.seed(8)
def get_xs(lwr_bound = -4, upr_bound = 4, n = 80):
return np.arange(lwr_bound, upr_bound, (upr_bound - lwr_bound) / n)
def get_distribution_params(list_):
mus = [round((i+1) + 0.1 * np.random.randint(0,10), 3) for i in range(len(dists))]
sigmas = [round((i+1) * .01 * np.random.randint(0,10), 3) for i in range(len(dists))]
return mus, sigmas
def get_distributions(list_, xs, mus, sigmas):
return [list(zip(xs, norm.pdf(xs, loc=mus[i], scale=sigmas[i] if sigmas[i] != 0.0
else 0.1))) for i in range(len(list_))]
dists = [1, 2, 3, 4]
xs = get_xs()
mus, sigmas = get_distribution_params(dists)
distributions = get_distributions(dists, xs, mus, sigmas)
fc = [mcolors.to_rgba('r', alpha=0.6), mcolors.to_rgba('g', alpha=0.6),
mcolors.to_rgba('b', alpha=0.6), mcolors.to_rgba('y', alpha=0.6)]
poly = PolyCollection(distributions, fc=fc)
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.add_collection3d(poly, zs=np.array(dists).astype(float), zdir='z')
ax.view_init(azim=115)
ax.set_zlim([0, 5])
ax.set_ylim([0, 5])
ax.set_xlim([0, 5])
I based it off the code you provide in the question, but made some modifications for brevity and to be more consistent with the usual styling.
Note   -   The example code you have given will fail depending on the np.random.seed(), in order to ensure it works I have added a check in the call to norm.pdf which ensures the scale is non-zero: scale = sigma[i] if sigma[i] != 0.0 else 0.1.
Using ax.add_collection3d(poly, zs=dists, zdir='z') instead of ax.add_collection3d(poly, zs=distributions, zdir='z') should fix the issue.
Additionally, you might want to replace
def get_xs(lwr_bound = -4, upr_bound = 4, n = 80):
""" generates the x space betwee lwr_bound and upr_bound so that it has n intermediary steps """
xs = np.arange(lwr_bound, upr_bound, (upr_bound - lwr_bound) / n) # x space -- number of points on l/r dimension
return(xs)
xs = get_xs()
by
xs = np.linspace(-4, 4, 80)
Also, I believe the scale = sigmas[0] should actually be scale = sigmas[i] in the line
z_ = norm.pdf(xs, loc = mus[i], scale = sigmas[0])
Finally, I believe you should adjust the xlim, ylim and zlim appropriatly, as you swapped the y and z dimensions of the plot and changed its scales when comparing to the reference code.

Categories