Home > Back-end >  Keeping 2D interpolation within shape
Keeping 2D interpolation within shape

Time:12-15

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.

Befor clipping. before clip

After clipping.

after clip

  • Related