I have the following data:
x = array([ 0, 0.08885313, 0.05077321, 0.05077321, 0.03807991, 0.03807991, 0.03807991, 0.02538661, 0.02538661, 0.0126933 , 0.0126933 , -0. , -0. , -0.0126933 , -0.0126933 , -0.02538661, -0.02538661, -0.02538661, -0.03807991, -0.05077321, -0.05077321, -0.05077321, -0.06346652, -0.07615982, -0.11423973, -0.12693304, -0.13962634])y = array([ 0, -0.15231964, -0.08885313, -0.17770625, -0.08885313, -0.10154643, -0.12693304, -0.07615982, -0.08885313, -0.07615982, -0.08885313, -0.07615982, -0.10154643, -0.08885313, -0.10154643, -0.07615982, -0.08885313, -0.17770625, -0.10154643, -0.08885313, -0.11423973, -0.12693304, -0.10154643, -0.08885313, -0.17770625, -0.12693304, -0.11423973])z = array([ 0, 1.21839241, 0.78673339, 1.21839241, 0.70318648, 0.82850684, 0.96078945, 0.64748854, 0.71014872, 0.63356406, 0.71711096, 0.65445078, 0.77977115, 0.73103545, 0.77977115, 0.68926199, 0.73799769, 1.19750569, 0.85635581, 0.84243133, 0.96078945, 1.02344963, 0.93294048, 0.97471393, 1.24624138, 1.20446793, 1.22535466])
I have used the following code which fits a surface in the form of points in the (x, y, z) space to my data.
# Find the maximum and minimum x valuesi_max, = np.where(np.isclose(x, np.max(x)))i_min, = np.where(np.isclose(x, np.min(x)))x_max = x[i_max][0]x_min = x[i_min][0]# Find the corresponding y values (i.e. y values of those points where x is maximum)y_max = y[i_max][0]y_min = y[i_min][0]# Function finding 2D-line coefficients, based on this answer https://stackoverflow.com/a/21566184/3715182def line_coeffs(points): x_coords, y_coords = zip(*points) A = vstack([x_coords, ones(len(x_coords))]).T # y = a*x + b a, b = lstsq(A, y_coords, rcond=None)[0] return (a, b)# Find coefficients of the two lines "limiting" all points left and right across the x-axis in the XY-planek1_max, k2_max = line_coeffs([(x_max, y_max), (0, 0)])k1_min, k2_min = line_coeffs([(x_min, y_min), (0, 0)])# Define mathematical function for curve fitting def func(xy, a, b, c, d, e, f): x, y = xy return a + b*x + c*y + d*x**2 + e*y**2 + f*x*y# Create a grid of points over the surfacex_fill = np.linspace(-0.2, 0.2, 40)y_fill = np.linspace(-0.3, 0, 40)X_fill, Y_fill = np.meshgrid(x_fill, y_fill)# Evaluate the function at each point on the surfaceZ_fill = func((X_fill, Y_fill), *popt)# Plot the data points and the filled surfacefig = plt.figure()ax = fig.add_subplot(111, projection='3d')ax.scatter(x, y, z, color='blue', label='Data Points')ax.scatter(np.where(X_fill >= (Y_fill - k2_min)/k1_min, np.where(X_fill <= (Y_fill - k2_max)/k1_max, X_fill, np.nan), np.nan), Y_fill, Z_fill, color='red', alpha=0.5, s= 2)ax.set_xlim([-0.2, 0.2])ax.set_ylim([-0.3, 0.2])ax.set_zlim([0, 1.2])ax.set_xlabel('x')ax.set_ylabel('y')ax.set_zlabel('z')plt.show()
This gives my the following result:
You can see that the red points fit my blue data points well. However, I would be expected the data to continue upwards rather than going back down around y=-0.3. How can I limit (or change) my fitting function so that the function is continuously increasing in z. I would expect something a bit more like a section of the following plot (only between 2 angles):