рекомендации

среда, 9 января 2019 г.

Анализ временных рядов в Python: введение

Аддитивные модели для моделирования временных рядов

Временные ряды являются одним из наиболее распространенных типов данных, встречающихся в повседневной жизни. Финансовые показатели, погода, потребление энергии дома и даже вес - все это примеры данных, которые можно собирать через регулярные промежутки времени. Практически каждый научный сотрудник будет сталкиваться с временными рядами в своей повседневной работе и научиться их моделировать - важный навык в наборе умений для научных исследований. Одним из мощных, но простых методов анализа и прогнозирования периодических данных является аддитивная модель. Идея проста: представляют временные ряды как комбинацию паттернов в разных масштабах, таких как ежедневные, еженедельные, сезонные и ежегодные, а также общая тенденция. Ваше потребление энергии может расти летом и уменьшаться зимой, но при этом наблюдается общая тенденция к увеличению энергоэффективности вашего дома. Аддитивная модель может как показать нам как паттерны/тенденции, так и сделать прогнозы на основе этих наблюдений.

На следующем рисунке показана декомпозиция аддитивной модели временного ряда в общую, годовую и недельную тенденции.




Этот пост проведет вас через вводный пример создания аддитивной модели для финансовых временных рядов с использованием Python и пакета прогнозирования Prophet, разработанного Facebook. По пути мы рассмотрим некоторые манипуляции с данными с использованием pandas, доступ к финансовым данным с использованием библиотеки Quandl и, а также построение графика с помощью matplotlib. Я включил код, где это нужно, и я призываю всех проверить Jupyter Notebook на GitHub для полного анализа статьи. Это введение покажет вам все шаги, необходимые для самостоятельного моделирования временных рядов!

Отказ от ответственности: теперь скучная часть, когда я должен упомянуть, что когда дело доходит до финансовых данных, прошлая эффективность не является индикатором будущей эффективности, и вы вряд ли сможете разбогатеть, используя приведенные здесь методы. Я решил использовать данные котировок, потому что они легко доступны. Если вы действительно хотите стать богатым, изучение науки о данных - лучший выбор по сравнению с игрой на фондовом рынке!

Получение финансовых данных

Обычно около 80% времени, потраченного на проект в области данных, - это получение и чистка данных. Благодаря финансовой библиотеке quandl для этого проекта это время сокращено примерно до 5% . Quandl может быть установлена с помощью pip из командной строки, она позволяет вам получать доступ к тысячам финансовых индикаторов с помощью одной строки Python и допускает до 50 запросов в день без регистрации. Если вы зарегистрируете бесплатную учетную запись, вы получите ключ api, который разрешает неограниченные запросы.

Сначала мы импортируем необходимые библиотеки и получаем некоторые данные. Quandl автоматически помещает наши данные в dataframe pandas, структуру данных для науки о данных. (Для других компаний просто замените «TSLA» или «GM» биржевым тикером. Вы также можете указать диапазон дат).

  • # quandl для получения финансовых данных
  • import quandl
  •  
  • # pandas работы с данными
  • import pandas as pd
  • quandl.ApiConfig.api_key = 'getyourownkey!'
  •  
  • # Получение данных для TSLA с Quandl
  • tesla = quandl.get('WIKI/TSLA')
  •  
  • # Получение данных для GM с Quandl
  • gm = quandl.get('WIKI/GM')
  • gm.head(5)


Объем данных на quandl почти неограниченный , но я хотел сосредоточиться на сравнении двух компаний в одной и той же отрасли, а именно Tesla и General Motors. Tesla - увлекательная компания не только потому, что это первый успешный американский автомобильный стартап за 111 лет, но и потому, что время от времени в 2017 году она была самой дорогой автомобильной компанией в Америке, несмотря на то, что она продавала только 4 марки автомобиля. Другим претендентом на титул самой дорогой автомобильной компании является General Motors, которая недавно продемонстрировала признаки охвата автомобильного рынка будущего, создав несколько довольно крутых электромобилей.


Мы могли бы легко потратить часы на поиск этих данных и загрузить их в виде файлов электронной таблицы csv, но вместо этого, благодаря quandl, у нас есть все необходимые данные за несколько секунд!

Исследование данных

Прежде чем мы сможем перейти к моделированию, лучше всего получить представление о структуре и диапазонах данных, сделав несколько графиков. Это также позволит нам увидеть выбросы или недостающие значения, которые необходимо исправить.

dataframe Pandas можно легко визуализировать с помощью matplotlib. Если что-то из графического кода выглядит устрашающе, не беспокойтесь. Я также считаю matplotlib неинтуитивной и часто копирую и вставляю примеры из Stack Overflow или документации, чтобы получить график, который я хочу. Одно из правил программирования - не изобретать уже существующее решение!

  • # Скорректированные значения для цен закрытия, это то, что мы должны нарисовать
  • plt.plot(gm.index, gm['Adj. Close'])
  • plt.title('GM Stock Price')
  • plt.ylabel('Price ($)');
  • plt.show()
  •  
  • plt.plot(tesla.index, tesla['Adj. Close'], 'r')
  • plt.title('Tesla Stock Price')
  • plt.ylabel('Price ($)');
  • plt.show();


Сравнение двух компаний только по ценам на акции не покажет, какая из них является более ценной, поскольку общая стоимость компании (рыночная капитализация) также зависит от количества акций (рыночная капитализация = цена акции * количество акций). Quandl не имеет данных о количестве акций, но мне удалось найти средние годовые количества акции для обеих компаний с помощью поиска в Google. Это не точное значение, но оно будет достаточно точным для нашего анализа. Иногда нам приходится делать с несовершенными данными!

Чтобы создать столбец рыночной капитализации в нашем dataframe, мы используем несколько трюков с pandas, таких как перемещение индекса в столбец (reset_index) и одновременное индексирование и изменение значений в dataframe с помощью ix.

  • # Среднее годовое количество акций  для Tesla и GM
  • tesla_shares = {2018168e62017162e62016144e62015128e62014125e6,
  •  2013119e62012107e62011100e6201051e6}
  •  
  • gm_shares = {20181.42e920171.50e920161.54e920151.59e920141.61e9,
  •  20131.39e920121.57e920111.54e92010:1.50e9}
  •  
  • # Создаем столбец Year
  • tesla['Year'= tesla.index.year
  • # Берем Dates из индекса и перемещаем в столбец Date
  • tesla.reset_index(level=0, inplace = True)
  • tesla['cap'= 0
  •  
  • # Рассчитываем рыночную капитализацию для каждого года
  • for i, year in enumerate(tesla['Year']):
  •     # Получаем количество акций для текущего года
  •     shares = tesla_shares.get(year)
  •  
  •     # Сохраняем в столбце cap произведения количества акций на цену
  •     tesla.ix[i, 'cap'= shares * tesla.ix[i, 'Adj. Close']

Эта команда создает столбец «cap» для Tesla. Мы выполняем тот же процесс с данными GM, а затем объединяем два столбца. Слияние является важной частью рабочего процесса, поскольку позволяет нам объединять наборы данных. В этом случае у нас есть цены акций для двух разных компаний на одни и те же даты, и поэтому мы хотим присоединить к данным в столбце даты. Мы выполняем «внутреннее» слияние для сохранения только записей Date, которые присутствуют в обоих dataframe. После слияния мы переименовываем столбцы, чтобы мы знали, к какой компании он относится.

  • # Объединяем два набора данных и переименовываем столбцы
  • cars = gm.merge(tesla, how='inner', on='Date')
  • cars.rename(columns={'cap_x''gm_cap''cap_y''tesla_cap'}, inplace=True)
  •  
  • # Выбираем только релевантные столбцы
  • cars = cars.ix[:, ['Date''gm_cap''tesla_cap']]
  •  
  • # Делим, чтобы получить рыночную капитализацию в миллиардах долларов
  • cars['gm_cap'= cars['gm_cap'/ 1e9
  • cars['tesla_cap'= cars['tesla_cap'/ 1e9
  • cars.head()


Рыночная капитализация теперь задана миллиардах долларов. Мы можем видеть, что General Motors начали с рыночной капитализации примерно в 30 раз больше, чем у Tesla! Сохранится ли такое положение на протяжении всей временной шкалы?

  • plt.figure(figsize=(108))
  • plt.plot(cars['Date'], cars['gm_cap'], 'b-', label = 'GM')
  • plt.plot(cars['Date'], cars['tesla_cap'], 'r-', label = 'TESLA')
  • plt.xlabel('Date'); plt.ylabel('Market Cap (Billions $)'); plt.title('Market Cap 
  • of GM and Tesla')
  • plt.legend();


Мы наблюдаем стремительный подъем для Tesla и незначительное увеличение для General Motors. Tesla даже превосходит GM по стоимости в 2017 году!

  • import numpy as np
  •  
  • # Находим первый и последний раз, когда Тесла была оценена выше, чем GM
  • first_date = cars.ix[np.min(list(np.where(cars['tesla_cap'] > cars['gm_cap'])[0])),
  •  'Date']
  • last_date = cars.ix[np.max(list(np.where(cars['tesla_cap'] > cars['gm_cap'])[0])),
  •  'Date']
  • print("Tesla was valued higher than GM from {} to {}.".format(first_date.date(),
  •  last_date.date()))

Tesla was valued higher than GM from 2017-04-10 to 2017-09-21.

За это время Tesla продала около 48 000 автомобилей, а GM продала 1 500 000. GM была оценена ниже, чем Tesla в то время, когда она продала в 30 раз больше автомобилей! Это определенно отображает силу ее руководителя и высококачественного продукта. Хотя стоимость Tesla сейчас ниже GM, хороший вопрос, можем ли мы ожидать, что Tesla снова превзойдет GM? Когда это произойдет? Для этого мы переходим к аддитивным моделям для прогнозирования или, другими словами, к прогнозированию будущего.

Моделирование с помощью Prophet

Пакет Prophet от Facebook был выпущен в 2017 году для Python и R, и ученые-исследователи по всему миру были этому рады. Prophet предназначен для анализа временных рядов с ежедневными наблюдениями, отображающими паттерны в разных временных масштабах. Он также обладает расширенными возможностями для моделирования эффектов праздничных дней по временным рядам и реализации пользовательских контрольных точек, но мы будем придерживаться основных функций для построения и запуска модели. Prophet, как и quandl, может быть установлен с помощью pip из командной строки.

Сначала мы импортируем prophet и переименовываем столбцы в наших данных в правильный формат. Столбец «Date» должен называться «ds», а столбец "value", значения которого мы хотим предсказать, «y». Затем мы создаем модели prophet и привязываем их к данным, подобно модели машинного обучения Scikit-Learn:

  • import fbprophet
  •  
  • # Prophet требует столбцы ds (Date) и y (value)
  • gm = gm.rename(columns={'Date''ds''cap''y'})
  •  
  • # Переводим капитализацию в миллиарды долларов
  • gm['y'= gm['y'/ 1e9
  •  
  • # Создаем модель prophet  и применяем к данным
  • gm_prophet = fbprophet.Prophet(changepoint_prior_scale=0.15)
  • gm_prophet.fit(gm)

При создании моделей prophet я устанавливаю значение changepoint 0,15, а не значение по умолчанию 0,05. Этот гиперпараметр используется для контроля того, насколько чувствительна тенденция к изменениям, причем более высокое значение соответствует большей чувствительности. 

Если мы слишком близки к нашим обучающим данным, это называется переобучениями, у нас слишком большая дисперсия, и в общем случае наша модель может не работать на новых данных С другой стороны, если наша модель не выявляет тенденцию в обучающих данных, это недообучение и это системная предвзятость. Когда модель недообучена, увеличение значения changepoint делает ее более гибкой, а при переобучении уменьшение changepoint ограничивает ее гибкость. Эффект changepoint может быть проиллюстрирован визуализацией предсказаний, выполненных с различными  значениями:


Чем выше changepoint, тем более гибкая модель и чем точнее она соответствует обучающим данным. Это может показаться именно тем, чего мы хотим, но может привести к переобучению и невозможности точно предсказать новые данные. Поэтому нам необходимо найти правильный баланс для подгонки обучающих данных и возможности обобщения новых данных. Поскольку котировки меняются изо дня в день, и мы хотим, чтобы наша модель зафиксировала это, я увеличил гибкость после экспериментов с различными значениями.

В вызове для создания модели prophet мы также можем контрольные точки (changepoints), которые появляются, если временные ряды переходят от возрастания к уменьшению или от медленного роста к быстрому (они расположены там, где изменение скорости наибольшее). Контрольные точки могут соответствовать значительным событиям, таким как запуск нового продукта или макроэкономические колебания на рынке. Если мы не укажем контрольные точки, prophet рассчитает их для нас.

Чтобы делать прогнозы, нам нужно создать так называемый future dataframe. Мы указываем количество будущих периодов для прогнозирования (два года) и частоту предсказаний (ежедневно). Затем мы делаем прогноз с созданной нами моделью prophet и future dataframe:

  • # Делаем future dataframe для 2 лет
  • gm_forecast = gm_prophet.make_future_dataframe(periods=365 * 2, freq='D')
  •  
  • # Делаем предсказания
  • gm_forecast = gm_prophet.predict(gm_forecast)

Наши future dataframe содержат предполагаемую рыночную капитализацию Tesla и GM на следующие два года. Мы можем визуализировать предсказания с помощью функции prophet plot.

  • gm_prophet.plot(gm_forecast, xlabel = 'Date', ylabel = 'Market Cap (billions $)')
  • plt.title('Market Cap of GM');


Черные точки представляют фактические значения (обратите внимание, как они заканчиваются в начале 2018 года), синяя линия указывает прогнозируемые значения, а светло-голубая затененная область - неопределенность (всегда критическая часть любого прогноза). Область неопределенности увеличивается с течением времени, потому что исходная неопределенность растет с течением времени. Это можно видеть в прогнозах погоды, которые становятся все менее точными, чем дальше во времени они сделаны.

Мы также можем проверить контрольные точки, идентифицированные моделью. Опять же, контрольные точки показывают, когда скорость роста временных рядов значительно изменяется.

  • tesla_prophet.changepoints[:10]
  •  
  • 61    2010-09-24
  • 122   2010-12-21
  • 182   2011-03-18
  • 243   2011-06-15
  • 304   2011-09-12
  • 365   2011-12-07
  • 425   2012-03-06
  • 486   2012-06-01
  • 547   2012-08-28
  • 608   2012-11-27

Для сравнения, мы можем посмотреть Google Search Trends для Tesla в течение этого временного диапазона. Мы строим контрольные точки (вертикальные линии) и найденные тенденции на одном и том же графике:

  • # Загрузка данных
  • tesla_search = pd.read_csv('data/tesla_search_terms.csv')
  •  
  • # конвертируем месяц в datetime
  • tesla_search['Month'= pd.to_datetime(tesla_search['Month'])
  • tesla_changepoints = [str(date) for date in tesla_prophet.changepoints]
  •  
  • # Строим частоту поиска
  • plt.plot(tesla_search['Month'], tesla_search['Search'], label = 'Searches')
  •  
  • # Строим контрольные точки
  • plt.vlines(tesla_changepoints, ymin = 0, ymax= 100, colors = 'r', linewidth=0.6
  • linestyles = 'dashed', label = 'Changepoints')
  •  
  • # Форматируем график
  • plt.grid('off'); plt.ylabel('Relative Search Freq'); plt.legend()
  • plt.title('Tesla Search Terms and Changepoints');


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

Нам еще нужно выяснить, когда рыночная капитализация Теслы будет превосходить рыночную капитализацию General Motors. Поскольку у нас есть оба прогноза на следующие два года, мы можем построить линии для обеих компаний на одном графике после слияния данных. Перед слиянием мы переименовываем столбцы, чтобы отслеживать данные.

  • gm_names = ['gm_%s' % column for column in gm_forecast.columns]
  • tesla_names = ['tesla_%s' % column for column in tesla_forecast.columns]
  •  
  • # Объединение Dataframe
  • merge_gm_forecast = gm_forecast.copy()
  • merge_tesla_forecast = tesla_forecast.copy()
  •  
  • # Переименовываем столбцы
  • merge_gm_forecast.columns = gm_names
  • merge_tesla_forecast.columns = tesla_names
  •  
  • # Объединение двух наборов данных
  • forecast = pd.merge(merge_gm_forecast, merge_tesla_forecast, how = 'inner'
  • left_on = 'gm_ds', right_on = 'tesla_ds')
  •  
  • # Переименовываем столбец date
  • forecast = forecast.rename(columns={'gm_ds''Date'}).drop('tesla_ds', axis=1)

Сначала мы строим только оценку. Оценка (называемая «yhat» в пакете prophet) сглаживает шум в данных, поэтому она немного отличается от графиков необработанных данных. Уровень гладкости будет зависеть от значения changepoint  - более высокие значения означают более гибкую модель и больше подъемов и падений.


Наша модель считает, что кратковременное превышение GM компанией Tesla в 2017 году было всего лишь шумом, и только в начале 2018 года Тесла действительно выиграет у GM в прогнозе. Точная дата - 27 января 2018 года, поэтому, если это произойдет, я с радостью получу награду за предсказание будущего!

При составлении приведенного выше графика мы опустили основную часть прогноза: неопределенность! Мы можем использовать matplotlib, чтобы показать области сомнений:


Это лучшее представление прогноза. Оно показывает, что стоимость обеих компаний, как ожидается, возрастет, но Tesla будет расти быстрее, чем General Motors. Опять же, неопределенность возрастает со временем, как и ожидалось для прогноза, а нижняя граница Tesla ниже верхней границы GM в 2020 году, что означает, что GM может сохранить лидерство.

Тенденции и паттерны

На последнем этапе анализа рыночной капитализации рассматривается общая тенденция и модели. Prophet позволяет нам легко визуализировать общую тенденцию и паттерны компонентов:

  • # Строим паттерны и тенденции
  • gm_prophet.plot_components(gm_forecast)


Тенденция довольно ясная: акции GM растут и продолжают расти. Годовой паттерн интересен тем, что, по-видимому, GM увеличивает стоимость в конце года с медленным снижением к лету. Мы можем попытаться определить, существует ли корреляция между годовой рыночной капитализацией и среднемесячными объемами продаж GM за период времени. Сначала я собирал ежемесячные продажи автомобилей в Google, а затем усреднял их, используя groupby. Это еще одна важная операция в области анализа данных, поскольку часто мы хотим сравнить статистику между категориями, например, пользователей определенной возрастной группы или транспортных средств от одного производителя. В этом случае мы хотим рассчитать средние продажи в каждом месяце, поэтому мы объединяем месяцы вместе, а затем усредняем продажи.

  • gm_sales_grouped = gm_sales.groupby('Month').mean()


Не похоже, что ежемесячные продажи коррелируют с рыночной капитализацией. Ежемесячные продажи в августе на втором месте, что соответствует самой низкой точке для рыночной капитализации!

Если посмотреть на недельную тенденцию, то нет никакого значимого сигнала (в выходные дни цены на акции не регистрируются, поэтому мы смотрим на изменение в течение недели). Этого следует ожидать, поскольку теория случайных блужданий в экономике утверждает, что паттерны в ценах акций на ежедневной основе не предсказуемы. Как показывает наш анализ, в конечном итоге котировки имеют тенденцию к росту, но на дневном масштабе практически нет модели, которую мы можем использовать.

Простой взгляд на индекс Dow Jones Industrial Average (индекс рынка 30 крупнейших компаний на фондовой бирже) хорошо иллюстрирует этот момент:


Понятно, что лучше всего вернуться в 1900 год и вложить все свои деньги! Или на самом деле, когда рынок падает, не уходите с него, потому что в соответствии с историей он отыграет все потери . В общем масштабе текущие колебания слишком малы, чтобы их даже можно было увидеть, и если мы думаем, как ученые, мы понимаем, что играть на ежедневных котировках акций глупо по сравнению с инвестированием во весь рынок и удерживанием позиции в течение длительных периодов времени ,

Prophet может также применяться к более крупным данным, таким как ВВП, мера общего размера экономики страны. Я сделал следующий прогноз, создав модели prophet на основе исторических данных по ВВП США и Китая.


Точная дата, когда Китай превысит США в ВВП, составляет 2036! Эта модель ограничена в точности из-за низкой частоты наблюдений (ВВП измеряется один раз в квартал, но prophet лучше всего работает с ежедневными данными), но она обеспечивает базовое предсказание без необходимых макроэкономических знаний.

Существует много способов моделирования временных рядов - от простой линейной регрессии до рекуррентных нейронных сетей с ячейками LSTM. Аддитивные модели полезны, потому что они быстро создаются, быстро обучаются, предоставляют интерпретируемые шаблоны и делают прогнозы с неопределенностями. Возможности Prophet впечатляют, и мы только здесь только прошлись по верхам. Я рекомендую вам использовать эту статью, чтобы изучить некоторые данные, предлагаемые Quandl или ваши собственные временные ряды.

Комментариев нет:

Отправить комментарий