The requirement is to define this function f(x) = x^3 - 15x^2 - 18x + 1 and then plot it in such a way that the plotting region shows all of the turning points.
I think that my calculations below are wrong because if you look at the graph there are at least 2 turning points.
By that I mean that the function is first increasing and then is decreasing, but in my calculation I found a single solution for x, where x = 0.5.
How can I solve this problem?
%matplotlib notebook
import matplotlib.pyplot as plt
plt.rcParams.update({'font.size': 14})
#1. define the function
def f(x):
return x**3 - 15*x**2 - 18*x + 1
#find the turning points of a polynomial of 3rd degree
#2. find the derivative of x**3 - 15*x**2 - 18*x + 1
# the derivative is -15(2x + 1)
#3. by the null factor law, get the value of x:
# 2x + 1 = 0
# 2x = 1
# x = 0.5
#4. check what is f(x) for x = 0.5
0.5**3 - 15*0.5**2 - 18*0.5 + 1 = -39/4 = -9.75
#5. Therefore, we can conclude that there is a single turning point at A(0.5, -9.75)
x = np.linspace(-5,5,100)
plt.plot(x, f(x))
plt.plot(0.5,f(0.5),'.r', ms=20,label='x_1') #plot A(0.5, -9.75)
plt.xlabel('x')
plt.ylabel('f(x)')
plt.grid()
plt.show()
EDIT_1
Based on Picarus suggestion, I have recalculated the derivative, but the solution of my equation don't look as correct on the graph.
def f(x):
return x**3 - 15*x**2 - 18*x + 1
#find the turning points of a polynomial of 3rd degree
#2. find the derivative of x**3 - 15*x**2 - 18*x + 1
# Workout the derivative
# (x**3)' = 3*x**2
# (-15*x**2)' = -30*x
# (-18x)' = -18
# (1)' = 0
# Therefore our derviative equation is 3*x**2 -30*x -18
#Bring it to a more concise form
# 3(x**2 - 10*x -6) = 0
#Find the x solution for x**2 - 10*x -6
#Delta = b**2 - 4*a*c
#x1 = (10 + sqr(76))/2 = 9.358
#x2 = (10 - sqr(76))/2 = -3.84
x1 = (10 + np.sqrt(76))/2
x2 = (10 - np.sqrt(76))/2
print(x1)
print(x2)
#4. check what is f(x) for: x1 = 9.358, x2 = 0.641
x = np.linspace(-5,5,100)
plt.plot(x, f(x))
plt.plot(x1,f(x1),'.r', ms=20,label='x_1')
plt.plot(x2,f(x2),'.b', ms=20,label='x_2')
plt.xlabel('x')
plt.ylabel('f(x)')
plt.grid()
plt.show()
import numpy as np
from matplotlib import pyplot as plt
# generate some toy data
n = 600
t = np.linspace(0, 600, n)
y = (300 * np.exp(-0.1 * t) + 1) + 20 * (np.random.random(n))
# get the gradient
dy = np.gradient(y)
# search gradient for first occurrence of thresh value:
thresh = 0.01
idx_thresh = np.argmax(dy > thresh)
# y[idx_thresh] would be the "turning point"
# visualization
plt.plot(t, y, 'b', label='y')
plt.plot(t, dy, 'g', label='dy')
plt.plot(t[idx_thresh:], y[idx_thresh:], 'r', label=f'y[dy > {thresh}]')
plt.legend()
Related
I have fitted a 2-D cubic spline using scipy.interpolate.RectBivariateSpline. I would like to access/reconstruct the underlying polynomials within each rectangular cell. How can I do this? My code so far is written below.
I have been able to get the knot points and the coefficients with get_knots() and get_coeffs() so it should be possible to build the polynomials, but I do not know the form of the polynomials that the coefficients correspond to. I tried looking at the SciPy source code but I could not locate the underlying dfitpack.regrid_smth function.
A code demonstrating the fitting:
import numpy as np
from scipy.interpolate import RectBivariateSpline
# Evaluate a demonstration function Z(x, y) = sin(sin(x * y)) on a mesh
# of points.
x0 = -1.0
x1 = 1.0
n_x = 11
x = np.linspace(x0, x1, num = n_x)
y0 = -2.0
y1 = 2.0
n_y = 21
y = np.linspace(y0, y1, num = n_y)
X, Y = np.meshgrid(x, y, indexing = 'ij')
Z = np.sin(np.sin(X * Y))
# Fit the sampled function using SciPy's RectBivariateSpline.
order_spline = 3
smoothing = 0.0
spline_fit_func = RectBivariateSpline(x, y, Z,
kx = order_spline, ky = order_spline, s = smoothing)
And to plot it:
import matplotlib.pyplot as plt
# Make axes.
fig, ax_arr = plt.subplots(1, 2, sharex = True, sharey = True, figsize = (12.0, 8.0))
# Plot the input function.
ax = ax_arr[0]
ax.set_aspect(1.0)
d_x = x[1] - x[0]
x_edges = np.zeros(n_x + 1)
x_edges[:-1] = x - (d_x / 2.0)
x_edges[-1] = x[-1] + (d_x / 2.0)
d_y = y[1] - y[0]
y_edges = np.zeros(n_y + 1)
y_edges[:-1] = y - (d_y / 2.0)
y_edges[-1] = y[-1] + (d_y / 2.0)
ax.pcolormesh(x_edges, y_edges, Z.T)
ax.set_title('Input function')
# Plot the fitted function.
ax = ax_arr[1]
ax.set_aspect(1.0)
n_x_span = n_x * 10
x_span_edges = np.linspace(x0, x1, num = n_x_span)
x_span_centres = (x_span_edges[1:] + x_span_edges[:-1]) / 2.0
#
n_y_span = n_y * 10
y_span_edges = np.linspace(y0, y1, num = n_y_span)
y_span_centres = (y_span_edges[1:] + y_span_edges[:-1]) / 2.0
Z_fit = spline_fit_func(x_span_centres, y_span_centres)
ax.pcolormesh(x_span_edges, y_span_edges, Z_fit.T)
x_knot, y_knot = spline_fit_func.get_knots()
X_knot, Y_knot = np.meshgrid(x_knot, y_knot)
# Plot the knots.
ax.scatter(X_knot, Y_knot, s = 1, c = 'r')
ax.set_title('Fitted function and knots')
plt.show()
I have data as follows.
import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as stats
x = np.array([50,52,53,54,58,60,62,64,66,67,68,70,72,74,76,55,50,45,65])
y = np.array([25,50,55,75,80,85,50,65,85,55,45,45,50,75,95,65,50,40,45])
I can calculate overall R^2 as follows.
slope, intercept = np.polyfit(x, y, 1) # linear model adjustment
y_model = np.polyval([slope, intercept], x) # modeling...
x_mean = np.mean(x)
y_mean = np.mean(y)
n = x.size # number of samples
m = 2 # number of parameters
dof = n - m # degrees of freedom
t = stats.t.ppf(0.975, dof) # Students statistic of interval confidence
residual = y - y_model
std_error = (np.sum(residual**2) / dof)**.5 # Standard deviation of the error
numerator = np.sum((x - x_mean)*(y - y_mean))
denominator = ( np.sum((x - x_mean)**2) * np.sum((y - y_mean)**2) )**.5
correlation_coef = numerator / denominator
r2 = correlation_coef**2
# mean squared error
MSE = 1/n * np.sum( (y - y_model)**2 )
# to plot the adjusted model
x_line = np.linspace(np.min(x), np.max(x), 100)
y_line = np.polyval([slope, intercept], x_line)
# confidence interval
ci = t * std_error * (1/n + (x_line - x_mean)**2 / np.sum((x - x_mean)**2))**.5
# predicting interval
pi = t * std_error * (1 + 1/n + (x_line - x_mean)**2 / np.sum((x - x_mean)**2))**.5
############### Ploting
plt.rcParams.update({'font.size': 14})
fig = plt.figure()
ax = fig.add_axes([.1, .1, .8, .8])
ax.plot(x, y, 'o', color = 'royalblue')
ax.plot(x_line, y_line, color = 'royalblue')
ax.fill_between(x_line, y_line + pi, y_line - pi, color = 'lightcyan', label = '95% prediction interval')
ax.fill_between(x_line, y_line + ci, y_line - ci, color = 'skyblue', label = '95% confidence interval')
ax.set_xlabel('x')
ax.set_ylabel('y')
# rounding and position must be changed for each case and preference
a = str(np.round(intercept))
b = str(np.round(slope,2))
r2s = str(np.round(r2,2))
MSEs = str(np.round(MSE))
ax.text(45, 110, 'y = ' + a + ' + ' + b + ' x')
ax.text(45, 100, '$r^2$ = ' + r2s + ' MSE = ' + MSEs)
plt.legend(bbox_to_anchor=(1, .25), fontsize=12)
enter link description here
I want to calculate R^2 value for the data that fall within 95% prediction interval. How can I do it ?
Credit: code adapted from, Show confidence limits and prediction limits in scatter plot
Considering the following functions
def calculate_limits(y_fitted, pred_interval):
"""Calculate upper and lower bound prediction interval."""
return (y_fitted - pi).min(), (y_fitted + pi).max()
def calculate_within_limits(x_val, y_val, lower_bound, upper_bound):
"""Return x, y arrays with values within prediction interval."""
# Indices of values within limits
within_pred_indices = np.argwhere((y_val > lower_bound) & (y_val < upper_bound)).reshape(-1)
x_within_pred = x_val[within_pred_indices]
y_within_pred = y_val[within_pred_indices]
return x_within_pred, y_within_pred
def calculate_r2(x, y):
"""Calculate the r2 coefficient."""
# Calculate means
x_mean = x.mean()
y_mean = y.mean()
# Calculate corr coeff
numerator = np.sum((x - x_mean)*(y - y_mean))
denominator = ( np.sum((x - x_mean)**2) * np.sum((y - y_mean)**2) )**.5
correlation_coef = numerator / denominator
return correlation_coef**2
and an array similar to the one you provided but with an added value that fall outside the prediction interval.
x = np.array([50,52,53,54,58,60,62,64,66,67,68,70,72,74,76,55,50,45,65,73])
y = np.array([25,50,55,75,80,85,50,65,85,55,45,45,50,75,95,65,50,40,45,210])
the r2 is 0.1815064.
Now, to calculate the r2 with the values within the pred interval, follow the steps:
1. Calculate lower and upper bounds
# Pass the fitted y line and the prediction interval
lb_pred, ub_pred = calculate_limits(y_fitted=y_line, pred_interval=pi)
2. Filter values outside the interval
# Pass x, y values and predictions interval upper and lower bounds
x_within, y_within = calculate_within_limits(x, y, lb_pred, ub_pred)
3. Calculate R^2
calculate_r2(x_within, y_within)
>>>0.1432605082
I have a nonuniformly sampled data that I am trying to apply a Gaussian filter to. I am using python's numpy library to solve this. The data is of XY type, here is how it looks like:
[[ -0.96 390.63523024]
[ -1.085 390.68523024]
[ -1.21 390.44023023]
...
[-76.695 390.86023024]
[-77.105 392.51023024]
[-77.155 392.10023024]]
And here is a link to the whole *.npz file.
Here is my approach:
I start with defining a Gaussian function
Then I start scanning the data with a while loop along the X axis
Within each step of the loop:
I select a portion of data that is within two cutoff lengths
shift the X axis of the selected data portion to make it symmetrical around 0
calculate my Gaussian function at every point, multiply with corresponding Y values, sum and divide by number of elements
Move to next point
Here is how code looks like:
import numpy as np
import matplotlib.pyplot as plt
xy = np.load('1D_data.npz')['arr_0']
def g_func(xx, w=1.0):
a = 0.47 * w
return (1 / a) * np.exp((xx / a) ** 2 * (-np.pi))
x, y, x_, y_ = xy[:, 0], xy[:, 1], [], []
counter, xi, ww = 0, x[0], 1.0
while xi > np.amin(x):
curr_x = x[(x < xi) & (x >= xi - 2 * ww)]
g, ysel = [], []
for i, els in enumerate(curr_x):
xil = els - curr_x[0] + abs(curr_x[0] - curr_x[-1]) / 2
g.append(g_func(xil, ww))
ysel.append(y[counter + i])
y_.append(np.sum(np.multiply(g, ysel)) / len(g))
x_.append(xi)
counter += 1
xi = x[counter]
plt.plot(x, y, '-k')
plt.plot(x_, y_, '-r')
plt.show()
The output doesn't look right though. (See the fig below) Even if discarding the edges, the convolution is very noisy and the values do not seem to correspond to the data. What am I possibly doing wrong?
You made one mistake in your code:
Before multiplying g with y_sel, y_sel is not centered.
The reason why y_sel should be centered is because we want to add the relative differences weighted by the Gaussian to the entry at the center. If you multiply g with y_sel directly, not just the values of the neighboring entries within the window, but also the value of the center entry will be weighted by the Gaussian. This will definitely change the function values dramatically.
Below is my solution using numpy
def g_func(xx, w=1.0):
mean = np.mean(xx)
a = 0.47 * w
return (1 / a) * np.exp(((xx-mean) / a) ** 2 * (-np.pi))
def get_convolution(array,half_window_size):
array = np.concatenate((np.repeat(array[0],half_window_size),
array,
np.repeat(array[-1],half_window_size)))
window_inds = [list(range(ind-half_window_size,ind+half_window_size+1)) \
for ind in range(half_window_size,len(array)-half_window_size)]
return np.take(array,window_inds)
xy = np.load('1D_data.npz')['arr_0']
x, y = xy[:, 0], xy[:, 1]
half_window_size = 4
x_conv = np.apply_along_axis(g_func,axis=1,arr=get_convolution(x,half_window_size=half_window_size))
y_conv = get_convolution(y,half_window_size=half_window_size)
y_mean = np.mean(y_conv,axis=1)
y_centered = y_conv - y_mean[:,None]
smoothed = np.sum(x_conv*y_centered,axis=1) / (half_window_size*2) + y_mean
fig,ax = plt.subplots(figsize=(10,6))
ax.plot(x, y, '-k')
ax.plot(x, smoothed, '-r')
running the code, the output is
UPDATE
In order to unify w with half_window_size, here is one possibility, the idea is to let the standard deviation of the Gaussian to be 2*half_window_size
def g_func(xx):
std = len(xx)
mean = np.mean(xx)
return 1 / (std*np.sqrt(2*np.pi)) * np.exp(-1/2*((xx-mean)/std)**2)
def get_convolution(array,half_window_size):
array = np.concatenate((np.repeat(array[0],half_window_size),
array,
np.repeat(array[-1],half_window_size)))
window_inds = [list(range(ind-half_window_size,ind+half_window_size+1)) \
for ind in range(half_window_size,len(array)-half_window_size)]
return np.take(array,window_inds)
xy = np.load('1D_data.npz')['arr_0']
x, y = xy[:, 0], xy[:, 1]
half_window_size = 4
x_conv = np.apply_along_axis(g_func,axis=1,arr=get_convolution(x,half_window_size=half_window_size))
y_conv = get_convolution(y,half_window_size=half_window_size)
y_mean = np.mean(y_conv,axis=1)
y_centered = y_conv - y_mean[:,None]
smoothed = np.sum(x_conv*y_centered,axis=1) / (half_window_size*2) + y_mean
fig,ax = plt.subplots(figsize=(10,6))
ax.plot(x, y, '-k')
ax.plot(x, smoothed, '-r')
I am using the below codes to plot a line with two slopes as shown in the picture.The slope should should decline after certain limit [limit=5]. I am using vectorisation method to set the slope values.Is there any other method to set the slope values.Could anyone help me in this?
import matplotlib.pyplot as plt
import numpy as np
#Setting the condition
L=5 #Limit
m=1 #Slope
c=0 #Intercept
x=np.linspace(0,10,1000)
#Calculate the y value
y=m*x+c
#plot the line
plt.plot(x,y)
#Set the slope values using vectorisation
m[(x<L)] = 1.0
m[(x>L)] = 0.75
# plot the line again
plt.plot(x,y)
#Display with grids
plt.grid()
plt.show()
You may be overthinking the problem. There are two line segments in the picture:
From (0, 0) to (A, A')
From (A, A') to (B, B')
You know that A = 5, m = 1, so A' = 5. You also know that B = 10. Given that (B' - A') / (B - A) = 0.75, we have B' = 8.75. You can therefore make the plot as follows:
from matplotlib import pyplot as plt
m0 = 1
m1 = 0.75
x0 = 0 # Intercept
x1 = 5 # A
x2 = 10 # B
y0 = 0 # Intercept
y1 = y0 + m0 * (x1 - x0) # A'
y2 = y1 + m1 * (x2 - x1) # B'
plt.plot([x0, x1, x2], [y0, y1, y2])
Hopefully you see the pattern for computing y values for a given set of limits. Here is the result:
Now let's say you really did want to use vectorization for some obscure reason. You would want to compute all the y values up front and plot once, otherwise you will get weird results. Here are some modifications to your original code:
from matplotlib import pyplot as plt
import numpy as np
#Setting the condition
L = 5 #Limit
x = np.linspace(0, 10, 1000)
lMask = (x<=L) # Avoid recomputing this mask
# Compute a vector of slope values for each x
m = np.zeros_like(x)
m[lMask] = 1.0
m[~lMask] = 0.75
# Compute the y-intercept for each segment
b = np.zeros_like(x)
#b[lMask] = 0.0 # Already set to zero, so skip this step
b[~lMask] = L * (m[0] - 0.75)
# Compute the y-vector
y = m * x + b
# plot the line again
plt.plot(x, y)
#Display with grids
plt.grid()
plt.show()
Following your code, you should modify the main part like this:
x=np.linspace(0,10,1000)
m = np.empty(x.shape)
c = np.empty(x.shape)
m[(x<L)] = 1.0
c[x<L] = 0
m[(x>L)] = 0.75
c[x>L] = L*(1.0 - 0.75)
y=m*x+c
plt.plot(x,y)
Note that c needs to change as well for the line to be continuous. This is the result:
I need to make an offset parallel enclosure of an airfoil profile curve, but I cant figure out how to make all the points be equidistant to the points on the primary profile curve at desired distance.
this is my example airfoil profile
this is my best and not good approach
EDIT #Patrick Solution for distance 0.2
You'll have to special-case slopes of infinity/zero, but the basic approach is to use interpolation to calculate the slope at a point, and then find the perpendicular slope, and then calculate the point at that distance.
I have modified the example from here to add a second graph. It works with the data file you provided, but you might need to change the sign calculation for a different envelope.
EDIT As per your comments about wanting the envelope to be continuous, I have added a cheesy semicircle at the end that gets really close to doing this for you. Essentially, when creating the envelope, the rounder and more convex you can make it, the better it will work. Also, you need to overlap the beginning and the end, or you'll have a gap.
Also, it could almost certainly be made more efficient -- I am not a numpy expert by any means, so this is just pure Python.
def offset(coordinates, distance):
coordinates = iter(coordinates)
x1, y1 = coordinates.next()
z = distance
points = []
for x2, y2 in coordinates:
# tangential slope approximation
try:
slope = (y2 - y1) / (x2 - x1)
# perpendicular slope
pslope = -1/slope # (might be 1/slope depending on direction of travel)
except ZeroDivisionError:
continue
mid_x = (x1 + x2) / 2
mid_y = (y1 + y2) / 2
sign = ((pslope > 0) == (x1 > x2)) * 2 - 1
# if z is the distance to your parallel curve,
# then your delta-x and delta-y calculations are:
# z**2 = x**2 + y**2
# y = pslope * x
# z**2 = x**2 + (pslope * x)**2
# z**2 = x**2 + pslope**2 * x**2
# z**2 = (1 + pslope**2) * x**2
# z**2 / (1 + pslope**2) = x**2
# z / (1 + pslope**2)**0.5 = x
delta_x = sign * z / ((1 + pslope**2)**0.5)
delta_y = pslope * delta_x
points.append((mid_x + delta_x, mid_y + delta_y))
x1, y1 = x2, y2
return points
def add_semicircle(x_origin, y_origin, radius, num_x = 50):
points = []
for index in range(num_x):
x = radius * index / num_x
y = (radius ** 2 - x ** 2) ** 0.5
points.append((x, -y))
points += [(x, -y) for x, y in reversed(points)]
return [(x + x_origin, y + y_origin) for x, y in points]
def round_data(data):
# Add infinitesimal rounding of the envelope
assert data[-1] == data[0]
x0, y0 = data[0]
x1, y1 = data[1]
xe, ye = data[-2]
x = x0 - (x0 - x1) * .01
y = y0 - (y0 - y1) * .01
yn = (x - xe) / (x0 - xe) * (y0 - ye) + ye
data[0] = x, y
data[-1] = x, yn
data.extend(add_semicircle(x, (y + yn) / 2, abs((y - yn) / 2)))
del data[-18:]
from pylab import *
with open('ah79100c.dat', 'rb') as f:
f.next()
data = [[float(x) for x in line.split()] for line in f if line.strip()]
t = [x[0] for x in data]
s = [x[1] for x in data]
round_data(data)
parallel = offset(data, 0.1)
t2 = [x[0] for x in parallel]
s2 = [x[1] for x in parallel]
plot(t, s, 'g', t2, s2, 'b', lw=1)
title('Wing with envelope')
grid(True)
axes().set_aspect('equal', 'datalim')
savefig("test.png")
show()
If you are willing (and able) to install a third-party tool, I'd highly recommend the Shapely module. Here's a small sample that offsets both inward and outward:
from StringIO import StringIO
import matplotlib.pyplot as plt
import numpy as np
import requests
import shapely.geometry as shp
# Read the points
AFURL = 'http://m-selig.ae.illinois.edu/ads/coord_seligFmt/ah79100c.dat'
afpts = np.loadtxt(StringIO(requests.get(AFURL).content), skiprows=1)
# Create a Polygon from the nx2 array in `afpts`
afpoly = shp.Polygon(afpts)
# Create offset airfoils, both inward and outward
poffafpoly = afpoly.buffer(0.03) # Outward offset
noffafpoly = afpoly.buffer(-0.03) # Inward offset
# Turn polygon points into numpy arrays for plotting
afpolypts = np.array(afpoly.exterior)
poffafpolypts = np.array(poffafpoly.exterior)
noffafpolypts = np.array(noffafpoly.exterior)
# Plot points
plt.plot(*afpolypts.T, color='black')
plt.plot(*poffafpolypts.T, color='red')
plt.plot(*noffafpolypts.T, color='green')
plt.axis('equal')
plt.show()
And here's the output; notice how the 'bowties' (self-intersections) on the inward offset are automatically removed: