Простая линейная интерполяция в Swift

Я конвертирую скрипт с 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, и мне кажется, что я упускаю что-то очень очевидное.

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
0
439
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это исполнение 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
}

Другие вопросы по теме