Скажем, что у меня есть данные от метеостанций на 3 (известных) высотах на горе. В частности, каждая станция регистрирует измерение температуры на своем месте каждую минуту. У меня есть два типа интерполяции, которые я хотел бы выполнить. И я хотел бы иметь возможность выполнять каждый быстро.
Итак, давайте настроим некоторые данные:
import numpy as np
from scipy.interpolate import interp1d
import pandas as pd
import seaborn as sns
np.random.seed(0)
N, sigma = 1000., 5
basetemps = 70 + (np.random.randn(N) * sigma)
midtemps = 50 + (np.random.randn(N) * sigma)
toptemps = 40 + (np.random.randn(N) * sigma)
alltemps = np.array([basetemps, midtemps, toptemps]).T # note transpose!
trend = np.sin(4 / N * np.arange(N)) * 30
trend = trend[:, np.newaxis]
altitudes = np.array([500, 1500, 4000]).astype(float)
finaltemps = pd.DataFrame(alltemps + trend, columns=altitudes)
finaltemps.index.names, finaltemps.columns.names = ['Time'], ['Altitude']
finaltemps.plot()
Отлично, поэтому наши температуры выглядят так:
Интерполируйте все времена на одну и ту же высоту:
Я думаю, что это довольно просто. Скажем, я хочу получить температуру на высоте 1000 для каждого раза. Я могу просто использовать встроенные методы интерполяции scipy
:
interping_function = interp1d(altitudes, finaltemps.values)
interped_to_1000 = interping_function(1000)
fig, ax = plt.subplots(1, 1, figsize=(8, 5))
finaltemps.plot(ax=ax, alpha=0.15)
ax.plot(interped_to_1000, label='Interped')
ax.legend(loc='best', title=finaltemps.columns.name)
Это хорошо работает. И посмотрим на скорость:
%%timeit
res = interp1d(altitudes, finaltemps.values)(1000)
#-> 1000 loops, best of 3: 207 µs per loop
Интерполировать "вдоль пути":
Итак, теперь у меня есть вторая, связанная с этим проблема. Скажем, я знаю высоту пешеходной вечеринки как функцию времени, и я хочу вычислить температуру в своем (движущемся) месте, линейно интерполируя мои данные во времени. В частности, времена, когда я знаю, что местонахождение походной вечеринки - это те же самые, когда я знаю температуру на своих метеостанциях. Я могу сделать это без особых усилий:
location = np.linspace(altitudes[0], altitudes[-1], N)
interped_along_path = np.array([interp1d(altitudes, finaltemps.values[i, :])(loc)
for i, loc in enumerate(location)])
fig, ax = plt.subplots(1, 1, figsize=(8, 5))
finaltemps.plot(ax=ax, alpha=0.15)
ax.plot(interped_along_path, label='Interped')
ax.legend(loc='best', title=finaltemps.columns.name)
Итак, это работает очень хорошо, но важно отметить, что ключевая строка выше использует понимание списка, чтобы скрыть огромный объем работы. В предыдущем случае scipy
создает для нас одну интерполяционную функцию и оценивает ее один раз на большом количестве данных. В этом случае scipy
фактически создает N
отдельные интерполяционные функции и каждый раз оценивает небольшой объем данных. Это по своей сути неэффективно. Здесь существует замкнутая петля (в понимании списка), и, кроме того, это просто кажется дряблым.
Неудивительно, что это намного медленнее, чем предыдущий случай:
%%timeit
res = np.array([interp1d(altitudes, finaltemps.values[i, :])(loc)
for i, loc in enumerate(location)])
#-> 10 loops, best of 3: 145 ms per loop
Итак, второй пример работает на 1000 медленнее первого. То есть в соответствии с идеей о том, что тяжелый подъем является шагом "сделать линейную интерполяционную функцию"... который происходит во втором примере в 1000 раз, но только один раз в первом.
Итак, вопрос: есть ли лучший способ приблизиться ко второй проблеме? Например, есть ли хороший способ настроить его с помощью 2-мерной интерполяции (которая, возможно, могла бы справиться с этим случаем где время, в котором известны местоположения пешеходной зоны, - это не время, в которое были выбраны температуры)? Или есть особенно гладкий способ справиться с ситуациями здесь, где время выстраивается в линию? Или другое?