2017-12-29 44 views
0

우선, 이런 종류의 질문을하기에 좋은 장소인지 잘 모르겠습니다.
만약 물어 보지 않으시면 어디에서 물어봐야하니 제거하겠습니다. 나는 내 문제를 해결하기 어떤 게시물을 찾을 수 없습니다 이후 난 단지 유지하기 위해 노력하지만비 - 모노톤 곡선에서 y 값에 해당하는 x 값 가져 오기

그러나, 내 질문 아래에있는 내 코드는

코드 부분은 조금 긴 ... 다른 사람에게 유용 할 수 있습니다 최소값. 그러나이 부분은 내가 약간의 연구를했으며 내가 더 나은 것을 찾고 있음을 보여줄뿐입니다.

질문

하십시오 x, y 값 목록에서

, 나는 주어진 y 값에 해당하는 x 값을 찾을 수 있습니다 (또는 생각)하고 싶다. I 못해,

그러나
import numpy as np 
from scipy import interpolate 

class AxisCam: 
    def __init__(self, x=None, y=None): 
     self.x = x if x else [] 
     self.y = y if y else [] 

     if len(self.x): 
      self.xMin = min(self.x) 
      self.xMax = max(self.x) 
     else: 
      self.xMin = None 
      self.xMax = None 

     if len(self.y): 
      self.yMin = min(self.y) 
      self.yMax = max(self.y) 
     else: 
      self.yMin = None 
      self.yMax = None 

     self._interpolX, self._interpolY = self.setInterpolator() 

    def setInterpolator(self, interpolator=interpolate.interp1d): 
     """ 
     Define the interpolator to use to approximate the axis cam positions 
     :param interpolator: interpolator function to use, default is scipy.interpolate.interp1d 
     :return: a tuple with the interpolator functions for x and y values 
     """ 
     if len(self.x) <= 0 or len(self.y) <= 0: 
      return None, None 
     with np.errstate(divide='ignore', invalid='ignore'): # silent the warnings caused by the interpolator 
      self._interpolX = interpolator(self.y, self.x) # x = f(y) 
      self._interpolY = interpolator(self.x, self.y) # y = f(x) 
     return self._interpolX, self._interpolY 

    def getX(self, yValue): 
     """ 
     Return x-value corresponding to a y-value using the interpolator 
     :param yValue: y-value we want to know the corresponding x-value 
     :return: x-value corresponding to the given y-value 
     """ 
     if yValue < self.yMin: 
      raise ValueError("value should be greater than the minimum y-value") 
     elif yValue > self.yMax: 
      raise ValueError("value should be lesser than the maximum y-value") 
     return float(self._interpolX(yValue)) 

    def getY(self, value): 
     """ 
     Return a y-value corresponding to a x-value using the interpolator 
     :param value: x-value we want to know the corresponding y-value 
     :return: the y-value corresponding to the given x-value 
     """ 
     if value < self.xMin: 
      raise ValueError("value should be greater than the minimum x-value") 
     elif value > self.xMax: 
      raise ValueError("value should be lesser than the maximum x-value") 
     return float(self._interpolY(value)) 

x = [0, 0.351906, 0.703812, 1.055718] # The 1024 values for X and Y can be retrieved here : https://pastebin.com/5eHsRjZ3 
y = [0.0, 0.000306, 0.002419, 0.008111] 
ac = AxisCam(x, y) 
print(ac.getX(100)) # returns 30.124163768271398 

곡선 인 비 단순 :

X 및 Y 값에 의해 정의 된 곡선 단조이다

는 단순히 함수 x = f(y)을 보간 할 수있다. 예외가 지금,

ValueError: x must be strictly increasing

그래서

올려 져, I는 아래 방법 getMonotonicParts 사용 monotonics 부로 곡선 스플릿 및 I는 각각의 단조 부품의 기능 x = f(y)을 보간 할 수있다.

import numpy as np 
from scipy import interpolate 

class AxisCam: 
    def __init__(self, x=None, y=None): 
     self.x = x if x else [] 
     self.y = y if y else [] 

     if len(self.y): 
      self.yMin = min(self.y) 
      self.yMax = max(self.y) 
     else: 
      self.yMin = None 
      self.yMax = None 

     self._monotonicParts = self.getMonotonicParts() 

    def getMonotonicParts(self, interpolator=interpolate.interp1d): 
     parts = [] 
     prevY = None # will store the previous value of y to compare with at each iteration 
     startIdx = None # will store the index of self.x and self.y where the monotonic part start from 
     direction = 0 # 0: Unknown - 1 : constant - 2: ascending - 3: descending 
     lenY = len(self.y) 
     for i, (x, y) in enumerate(zip(self.x, self.y)): 
      if prevY is None: 
       prevY = y 
      if startIdx is None: 
       startIdx = i 

      prevDir = direction 
      direction = 1 if y == prevY else 2 if y > prevY else 3 
      if prevDir != 0 and prevDir != direction: # Direction has changed => we have a new monotonic part 
       endIdx = i - 1 
       if direction == 3: # y values are increasing => we can interpolate on it 
        interp_func = interpolator(self.y[startIdx:endIdx], self.x[startIdx:endIdx]) 
       elif direction == 1: # y values are decreasing => we need to reverse it to interpolate on it 
        xValues = self.x[startIdx:endIdx] 
        xValues.reverse() 
        yValues = self.y[startIdx:endIdx] 
        yValues.reverse() 
        interp_func = interpolator(yValues, xValues) 
       else: # y values are the same on the range => return one of these 
        def interp_func(value): return self.y[startIdx] 
       parts.append({'start': startIdx, 
           'end': endIdx, 
           'x0': self.x[startIdx], 
           'y0': self.y[startIdx], 
           'x1': self.x[endIdx], 
           'y1': self.y[endIdx], 
           'interp': interp_func}) 
       startIdx = i 
      elif i == lenY - 1: # Add element on the last iteration 
       endIdx = i 
       if direction == 2: 
        interp = interpolator(self.y[startIdx:endIdx], self.x[startIdx:endIdx]) 
       else: 
        interp = None 
       parts.append({'start': startIdx, 
           'end': endIdx, 
           'x0': self.x[startIdx], 
           'y0': self.y[startIdx], 
           'x1': self.x[endIdx], 
           'y1': self.y[endIdx], 
           'interp': interp}) 
      prevY = y 
     return parts 

    def getX(self, yValue): 
     """ 
     Return a list of x-values corresponding to a y-value using the interpolator 
     :param yValue: y-value we want to know the corresponding x-value 
     :return: a list of x-values corresponding to the given y-value 
     """ 
     if yValue < self.yMin: 
      raise ValueError("value should be greater than the minimum y-value") 
     elif yValue > self.yMax: 
      raise ValueError("value should be lesser than the maximum y-value") 
     xValues = [] 
     for part in self._monotonicParts: 
      if part['y0'] <= yValue <= part['y1'] or part['y0'] >= yValue >= part['y1']: 
       xValues.append(float(part['interp'](yValue))) 
     return xValues 

x = [] # The 1024 values for X and Y can be retrieved here : https://pastebin.com/SL9RYYxY 
y = [] # /!\ It is not the same values that the previous example /!\ 
ac = AxisCam(x, y) 
print(ac.getX(100)) # returns [122.96996037206237, 207.6239552142487] 

내 솔루션은 아주 잘 작동하지만 나에게 무리한 조금 보인다 다른 더 좋은 방법이 작업을 수행하기 위해이 있는지 궁금 하군요.

+0

필요한 것은 스플라인 보간입니다. 'scipy.interpolate.InterpolatedUnivariateSpline'을 사용할 수 있습니다 (예 : – tiago

+0

@tiago no). 어떤 종류의 인터폴 레이터를 "this as"라고 사용하면, ValueError : x는 엄격하게 증가해야합니다. –

+0

그럴 경우, 주어진 x의 값에 대해 다음과 같이 할 수 있기 때문에'f (y)'는 함수가 아닙니다. y의 값이 두 개 이상있다. – tiago

답변

2

이 종류의 다중 보간을 수행하는 표준 루틴을 알지 못합니다. 그러나 이것을 확장하려는 경우 numpy가 제공하는 모든 것을 사용하기 위해 코드를 약간 리팩터링해야합니다. 예를 들어, 당신이 뭔가를 할 수 있습니다 :

여기 nan
import numpy as np 
from scipy.interpolate import interp1d 

# convert data lists to arrays 
x, y = np.array(x), np.array(y) 

# sort x and y by x value 
order = np.argsort(x) 
xsort, ysort = x[order], y[order] 

# compute indices of points where y changes direction 
ydirection = np.sign(np.diff(ysort)) 
changepoints = 1 + np.where(np.diff(ydirection) != 0)[0] 

# find groups of x and y within which y is monotonic 
xgroups = np.split(xsort, changepoints) 
ygroups = np.split(ysort, changepoints) 
interps = [interp1d(y, x, bounds_error=False) for y, x in zip(ygroups, xgroups)] 

# interpolate all y values 
yval = 100 
xvals = np.array([interp(yval) for interp in interps]) 

print(xvals) 
# array([   nan, 122.96996037, 207.62395521,   nan]) 

범위 (별도의 그룹으로 값을 반복이 알고리즘 취급)를 벗어난 값을 나타냅니다.

+0

오, 좋은! 나는 무언가를 놓쳤다는 것을 알았지 만 열등한 감정은 여전히 ​​나에게는 조금 애매하다. 감사 ! –

+0

'y'가 2 개의 똑같은 연속 값을 가지고있을 때이 알고리즘에 문제가 하나 있음을 발견했습니다. 이 경우,'ygroups'는 하나의 원소만을 가진 배열을 포함하고'interp1d'는 적어도 2 원소를 필요로합니다. 다음과 같이 list-comp를 수정했습니다. len (y)> = 2 인 경우'interps = [보간 기 (y, x, bounds_error = False), zip (yGroups, xGroups) –