Python Scipy: интерполяция RBF дает неверный результат

Это мои данные:

a   b   c
732018  2.501   95.094
732018  3.001   91.658
732018  3.501   89.164
732018  3.751   88.471
732018  4.001   88.244
732018  4.251   88.53
732018  4.501   89.8
732018  4.751   90.66
732018  5.001   92.429
732018  5.251   94.58
732018  5.501   97.043
732018  6.001   102.64
732018  6.501   108.798
732079  2.543   94.153
732079  3.043   90.666
732079  3.543   88.118
732079  3.793   87.399
732079  4.043   87.152
732079  4.293   87.425
732079  4.543   88.643
732079  4.793   89.551
732079  5.043   91.326
732079  5.293   93.489
732079  5.543   95.964
732079  6.043   101.587
732079  6.543   107.766
732170  2.597   95.394
732170  3.097   91.987
732170  3.597   89.515
732170  3.847   88.83
732170  4.097   88.61
732170  4.347   88.902
732170  4.597   90.131
732170  4.847   91.035
732170  5.097   92.803
732170  5.347   94.953
732170  5.597   97.414
732170  6.097   103.008
732170  6.597   109.164
732353  4.685   91.422

Я пытаюсь получить c для a=732107 и b=4.92. Я ожидаю ~ 90,79 на основе следующего расчета с использованием базовой линейной интерполяции (светло-зеленый - исходные данные, темно-зеленые промежуточные шаги и жирный черный - результат):

введите здесь описание изображения

Но когда я подаю всю поверхность Rbf, я получаю странные результаты:

import pandas
from scipy.interpolate import Rbf

interp_fun = Rbf(df["a"], df["b"], df["c"], function='cubic',smooth=0)
vol = interp_fun(732107,4.92)
print(vol)

array(207.6631648)

Похоже, он экстраполирует, где не должен.

Что мне не хватает?


person Chapo    schedule 01.05.2020    source источник


Ответы (1)


Я думаю, что есть проблема с данными, и ваш прогноз может быть немного оптимистичным. Чтобы убедиться в этом, я использовал KrigingAlgorithm. чтобы получить как значение, так и доверительный интервал. Более того, я нанес данные на график, чтобы получить представление о ситуации.

Во-первых, я превратил данные в полезный массив Numpy:

import openturns as ot
import numpy as np
data = [
    732018,  2.501,   95.094,
    732018,  3.001,   91.658,
    732018,  3.501,   89.164,
    732018,  3.751,   88.471,
    732018,  4.001,   88.244,
    732018,  4.251,   88.53,
    732018,  4.501,   89.8,
    732018,  4.751,   90.66,
    732018,  5.001,   92.429,
    732018,  5.251,   94.58,
    732018,  5.501,   97.043,
    732018,  6.001,   102.64,
    732018,  6.501,   108.798,
    732079,  2.543,   94.153,
    732079,  3.043,   90.666,
    732079,  3.543,   88.118,
    732079,  3.793,   87.399,
    732079,  4.043,   87.152,
    732079,  4.293,   87.425,
    732079,  4.543,   88.643,
    732079,  4.793,   89.551,
    732079,  5.043,   91.326,
    732079,  5.293,   93.489,
    732079,  5.543,   95.964,
    732079,  6.043,   101.587,
    732079,  6.543,   107.766,
    732170,  2.597,   95.394,
    732170,  3.097,   91.987,
    732170,  3.597,   89.515,
    732170,  3.847,   88.83,
    732170,  4.097,   88.61,
    732170,  4.347,   88.902,
    732170,  4.597,   90.131,
    732170,  4.847,   91.035,
    732170,  5.097,   92.803,
    732170,  5.347,   94.953,
    732170,  5.597,   97.414,
    732170,  6.097,   103.008,
    732170,  6.597,   109.164,
    732353,  4.685,   91.422,
]
dimension = 3
array = np.array(data)
nrows = len(data) // dimension
ncols = len(data) // nrows
data = array.reshape((nrows, ncols))

Затем я создал Sample с данными, масштабируя a, чтобы упростить вычисления.

x = ot.Sample(data[:, [0, 1]])
x[:, 0] /= 1.e5
y = ot.Sample(data[:, [2]])

Создать метамодель кригинга очень просто с помощью тренда ConstantBasisFactory и модели ковариации SquaredExponential.

inputDimension = 2
basis = ot.ConstantBasisFactory(inputDimension).build()
covarianceModel = ot.SquaredExponential([0.1]*inputDimension, [1.0])
algo = ot.KrigingAlgorithm(x, y, covarianceModel, basis)
algo.run()
result = algo.getResult()
metamodel = result.getMetaModel()

Затем метамодель кригинга можно использовать для прогнозирования:

a = 732107 / 1.e5
b = 4.92
inputPrediction = [a, b]
outputPrediction = metamodel([inputPrediction])[0, 0]
print(outputPrediction)

Это печатает:

95.3261715192566

Это не соответствует вашему прогнозу и имеет меньшую амплитуду, чем прогноз RBF.

Чтобы увидеть это более четко, я создал график данных, метамодель и точку для прогнозирования.

graph = metamodel.draw([7.320, 2.0], [7.325,6.597], [50]*2)
cloud = ot.Cloud(x)
graph.add(cloud)
point = ot.Cloud(ot.Sample([inputPrediction]))
point.setColor("red")
graph.add(point)
graph.setXTitle("a")
graph.setYTitle("b")

Получается следующая графика:

Кригинг

Вы видите, что справа есть выброс: это последняя точка в таблице. Точка, которую необходимо предсказать, выделена красным цветом в левом верхнем углу графика. В окрестности этой точки слева направо мы видим, что кригинг увеличивается с 92 до 95, затем снова уменьшается. Это вызвано очень высокими значениями (близкими к 100) в верхней части домена.

Затем я вычисляю доверительный интервал прогноза кригинга.

conditionalVariance = result.getConditionalMarginalVariance(
    inputPrediction)
sigma = np.sqrt(conditionalVariance)
[outputPrediction - 2 * sigma, outputPrediction + 2 * sigma]

Это производит:

[84.26731758315441, 106.3850254553588]

Следовательно, ваш прогноз 90,79 содержится в 95% доверительном интервале, но с достаточно высокой неопределенностью.

Исходя из этого, я бы сказал, что кубический RBF преувеличивает изменения в данных, приводя к довольно высокому значению.

person Michael Baudin    schedule 01.05.2020
comment
Большое спасибо за очень подробный ответ. griddata в линейном режиме дает мне почти то, что я хочу. a - это ось даты, поэтому в идеале я хотел бы выполнять линейную интерпретацию для каждой даты, и если эта дата не существует, используйте только две ближайшие даты для выполнения интерференции. По ощущениям griddata ближе по методологии к тому, что я только что описал. Это правильно ? - person Chapo; 01.05.2020
comment
также openturns очень приятно. поздравляю - person Chapo; 01.05.2020
comment
Я согласен, что griddata выполняет свою работу. Однако кригинг — это метод интерполяции (как и данные сетки), который дает ошибку прогноза (в отличие от данных сетки). Таким образом, у кригинга есть огромное преимущество, не так ли? - person Michael Baudin; 01.05.2020
comment
В принципе, это здорово, я на 100% с вами в этом. В моем конкретном случае использования (временной ряд) я как бы хочу игнорировать все, что не близко к точке интерполяции (т.е. если моя дата не существует во входных данных, я хочу, чтобы метод заботился только о непосредственном предыдущем и на следующую дату, а не на следующую неделю, когда, например, значение могло бы резко возрасти). Я подтвержу ваш ответ, потому что он прекрасно отвечает на мой вопрос, и я не упомянул дату. - person Chapo; 01.05.2020