I have a collection of measuring points and I want to interpolate between them, for which I use SciPy's griddata():
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from scipy.interpolate import griddata
data = pd.DataFrame({
'time': [0, 1, 2, 3, 4, 0.1, 0.9, 2, 3.05, 4, 0, 1, 2.2, 3, 3.95],
'force': [1, 2, 4, 9, 16, 0, 0, 0, 0, 0, -1, -2, -4, -9, -16]
})
Times, Forces = np.meshgrid(
np.linspace(0, 4, 100),
np.linspace(-16, 16, 100)
)
data['work'] = data['time'] * data['force']
interpolation = griddata(
(data['time'], data['force']),
data['work'],
(Times, Forces),
method= 'linear'
)
fig, ax = plt.subplots()
contour = ax.contourf(
Times, Forces, interpolation
)
ax.scatter(data['time'], data['force'])
fig.show()
My problem is, that my measuring points already follow the borders of the physical possibilities, but the interpolation will nontheless interpolate for every spannable area, including those which aren't reachable to measure.
How can I limit the interpolation or at least the plot of the interpolation to 'within' the shape of the outer points? Unfortunately the 'time' measurement has small deviations.
Any advise would be greatly appreciated!
CodePudding user response:
Firstly, you need to get the concave polygon for all the points. Secondly, Using the polygon to clip the contour fill. Although, this may be a bit complicated, some useful packages can help these tasks.
Below is the code.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from scipy.interpolate import griddata
import matplotlib.path as mpath
import alphashape
from descartes import PolygonPatch
Path = mpath.Path
data = pd.DataFrame({
'time': [0, 1, 2, 3, 4, 0.1, 0.9, 2, 3.05, 4, 0, 1, 2.2, 3, 3.95],
'force': [1, 2, 4, 9, 16, 0, 0, 0, 0, 0, -1, -2, -4, -9, -16]
})
Times, Forces = np.meshgrid(
np.linspace(0, 4, 100),
np.linspace(-16, 16, 100)
)
data['work'] = data['time'] * data['force']
interpolation = griddata(
(data['time'], data['force']),
data['work'],
(Times, Forces),
method= 'linear')
fig, ax = plt.subplots()
plt.xlim(-.3,4.3)
plt.ylim(-18,18)
#contour = ax.contourf( Times, Forces, interpolation)
contour = ax.tricontourf( data['time'], data['force'], data['work'])
ax.scatter(data['time'], data['force'])
x = data['time']
y = data['force']
points = np.vstack([x, y]).T
alpha = 0.95 * alphashape.optimizealpha(points)
hull = alphashape.alphashape(points, alpha)
hull_pts = hull.exterior.coords.xy
ax.scatter(hull_pts[0], hull_pts[1], color='red')
ax.add_patch(PolygonPatch(hull, fill=False, color='red'))
plt.savefig("clip_before.png")
#make clip path
vertices = []
codes = []
xpts,ypts = hull_pts
## convert polygon to path for cliping contour fill
for ix,iy in zip(xpts,ypts):
vertices.append((ix,iy))
codes = [Path.MOVETO]
codes = [Path.LINETO] * (len(xpts) -2)
codes = [Path.CLOSEPOLY]
clip = Path(vertices, codes)
for collection in contour.collections:
collection.set_clip_path(clip,transform=ax.transData)
plt.savefig("clip_after.png")
plt.show()
And here is the output figures.
After clipping.