Я конвертирую скрипт с Python на Swift. В исходном коде Python я выполняю простую интерполяцию между двумя списками, используя Numpy interp
в следующем формате:
>>> xp = [1, 2, 3]
>>> fp = [3, 2, 0]
>>> np.interp(2.5, xp, fp)
1.0
Это из документации Numpy, а не моего фактического кода, но это именно то, что мне нужно и что я использовал в скрипте Python. Короче говоря, у меня есть оси x и y в виде массивов, и я хочу выполнить одномерную интерполяцию для оценки координаты x.
Все это прекрасно работает с Python/Numpy, но я не могу заставить его работать в Swift. У платформы Accelerate vDSP есть linearInterpolate(VectorA, VectorB, using: X)
, но она возвращает массив, а не одно значение.
var y = vDSP.linearInterpolate([1.0, 2.0, 3.0], [3.0, 2.0, 0.0], using: 2.5)
Результат:
[6, 2, -4.5]
Это должно быть очень просто, и я думаю, что один из вариантов — просто написать свою собственную функцию. Но наверняка должен быть простой однострочный способ сделать это? Я новичок в Swift, и мне кажется, что я упускаю что-то очень очевидное.
Это исполнение linearInterpolate(_:_:using:) предназначено для решения совсем другой проблемы:
Например, следующий код создает два массива,
vectorA
иvectorB
, которые содержат синусоидальные волны:let n = 1024 let vectorA: [Float] = (0 ... n).map { return 2 + sin(Float($0) * 0.07) } let vectorB: [Float] = (0 ... n).map { return -2 + sin(Float($0) * 0.03) }
Используйте
linearInterpolate(_:_:using:)
с константой интерполяции0.5
, чтобы сгенерировать новый вектор, который является средним значением двух синусоидальных волн:let result = vDSP.linearInterpolate(vectorA, vectorB, using: 0.5)
На следующем рисунке показаны два исходных вектора: синие линии вверху и внизу и результат интерполяции: красная линия в центре:
linearInterpolate(elementsOf:using:) делает что-то более близкое к тому, что вы ищете (хотя вы указываете только значения y, и он предполагает целочисленные значения индекса x).
Итак, подумайте:
>>> xp = [0, 1, 2]
>>> fp = [3, 2, 0]
>>> np.interp(1.5, xp, fp)
1.0
Эквивалентный вызов vDSP может быть:
let z = vDSP.linearInterpolate(elementsOf: [3.0, 2.0, 0.0], using: [1.5])
[1.0]
Теперь, когда используются неявные значения x, которые являются индексом с отсчетом от нуля (например, 0, 1, 2 и т. д.), поэтому я настроил ваш пример Python, чтобы использовать значения индекса от нуля для x.
Таким образом, это решение vDSP является частным случаем интерполяции между соседними значениями, но я не знаю, достаточно ли этого или вам нужно более общее решение (для произвольных значений x на входе). Если это так, то да, возможно, вам просто придется написать свою маленькую вспомогательную функцию.
Вот пример более общего решения, которое извлекает только одно значение x:
func linearInterpolate<S, T>(_ sequenceX: S, _ sequenceY: S, at index: T) -> T? where S: Sequence, S.Element == T, T: FloatingPoint {
var xIterator = sequenceX.makeIterator()
var yIterator = sequenceY.makeIterator()
guard
var previousX = xIterator.next(),
var previousY = yIterator.next()
else {
return nil
}
while true {
if index == previousX { return previousY }
guard
let x = xIterator.next(),
let y = yIterator.next()
else {
return nil
}
if (index > previousX && index < x) || (index > x && index < previousX) {
let percent = (index - previousX) / (x - previousX)
return previousY + percent * (y - previousY)
}
previousX = x
previousY = y
}
}
и
let xp: [Double] = [1, 2, 3]
let fp: [Double] = [3, 2, 0]
if let y = linearInterpolate(xp, fp, at: 2.5) {
print(y) // 1.0
}