Как насчет чего-то подобного следующему, которое занимает у меня 0,34 секунды для запуска серии данных с нерегулярно разнесенными данными и 900k строк?Я предполагаю, что окно 5 подразумевает 5-дневный промежуток.
Во-первых, давайте создадим несколько примеров данных.
# Create sample data for a price stream of 2.6m price observations sampled 1 second apart.
seconds_per_day = 60 * 60 * 24 # 60 seconds / minute * 60 minutes / hour * 24 hours / day
starting_value = 100
annualized_vol = .3
sampling_percentage = .35 # 35%
start_date = '2018-12-01'
end_date = '2018-12-31'
np.random.seed(0)
idx = pd.date_range(start=start_date, end=end_date, freq='s') # One second intervals.
periodic_vol = annualized_vol * (1/ 252 / seconds_per_day) ** 0.5
daily_returns = np.random.randn(len(idx)) * periodic_vol
cumulative_indexed_return = (1 + daily_returns).cumprod() * starting_value
index_level = pd.Series(cumulative_indexed_return, index=idx)
# Sample 35% of the simulated prices to create a time series of 907k rows with irregular time intervals.
s = index_level.sample(frac=sampling_percentage).sort_index()
Теперь давайте создадим функцию генератора для хранения последнего значения экспоненциальновзвешенный временной ряд.Это может запустить с.В 4 раза быстрее, установив numba, импортировав его, а затем добавив одну строку декоратора над определением функции @jit(nopython=True)
.
from numba import jit # Optional, see below.
@jit(nopython=True) # Optional, see below.
def ewma(vals, decay_vals):
result = vals[0]
yield result
for val, decay in zip(vals[1:], decay_vals[1:]):
result = result * (1 - decay) + val * decay
yield result
Теперь давайте запустим этот генератор на нерегулярно расположенных рядах s
.Для этого примера с 900k строк у меня уходит 1,2 секунды на запуск следующего кода.Я могу дополнительно сократить время выполнения до 0,34 секунды, опционально используя компилятор Just in Time с numba .Сначала вам нужно установить этот пакет, например conda install numba
.Обратите внимание, что я использовал список со списком для заполнения значений ewma
из генератора, а затем присваиваю эти значения исходному ряду после первого преобразования его в массив данных.
# Assumes time series data is now named `s`.
window = 5 # Span of 5 days?
dt = pd.Series(s.index).diff().dt.total_seconds().div(seconds_per_day) # Measured in days.
decay = (1 - (dt / -window).apply(np.exp))
g = ewma_generator(s.values, decay.values)
result = s.to_frame('midpoint').assign(
ewma=pd.Series([next(g) for _ in range(len(s))], index=s.index))
>>> result.tail()
midpoint ewma
2018-12-30 23:59:45 103.894471 105.546004
2018-12-30 23:59:49 103.914077 105.545929
2018-12-30 23:59:50 103.901910 105.545910
2018-12-30 23:59:53 103.913476 105.545853
2018-12-31 00:00:00 103.910422 105.545720
>>> result.shape
(907200, 2)
Чтобы убедиться, чточисла следуют нашей интуиции, давайте визуализируем результат, выбирая почасовые выборки.Это выглядит хорошо для меня.
obs_per_day = 24 # 24 hourly observations per day.
step = int(seconds_per_day / obs_per_day)
>>> result.iloc[::step, :].plot()