<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10russianfull.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;DkAMR3g5fCp7ImA9WhVUFkQ.&quot;"><id>tag:blogger.com,1999:blog-3994427606477253566</id><updated>2012-05-22T17:53:06.624+04:00</updated><category term="Алгоритмы" /><category term="Topcoder" /><category term="вопросы с собеседований" /><category term="Собеседования Google" /><category term="SVD" /><category term="Сингулярное разложение" /><category term="Многопоточность" /><category term="новости" /><category term="Causality test" /><category term="графы" /><category term="rusquant" /><category term="Динамическое программирование" /><category term="1 Апреля" /><category term="Java" /><category term="Google" /><category term="Information retrieval" /><category term="Цифровая обработка сигналов" /><category term="Haskell" /><category term="Time series" /><category term="C#" /><category term="JDK7" /><category term="Инвестирование" /><category term="Математика" /><category term="работа" /><category term="Text mining" /><category term="Задачи" /><category term="invokedynamic" /><category term="Data mining" /><category term="Статистика" /><category term="Генетические алгоритмы" /><category term="R" /><title>О программировании, алгоритмах и не только</title><subtitle type="html">Жена посылает мужа-программиста в магазин и говорит, купи батон колбасы, а если будут яйца - возьми десяток. Он в магазине: У Вас яйца есть? -Есть -Тогда дайте десять батонов колбасы..</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://www.algorithmist.ru/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://www.algorithmist.ru/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>SergE</name><uri>http://www.blogger.com/profile/01485674727390704780</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://4.bp.blogspot.com/_rst1xzlxZSU/S_t-TztXAKI/AAAAAAAABRs/LZpr_ILRYOU/S220/me.jpg" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>57</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/algorithmist/ru" /><feedburner:info uri="algorithmist/ru" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:feedFlare href="http://add.my.yahoo.com/rss?url=http%3A%2F%2Ffeeds.feedburner.com%2Falgorithmist%2Fru" src="http://us.i1.yimg.com/us.yimg.com/i/us/my/addtomyyahoo4.gif">Subscribe with My Yahoo!</feedburner:feedFlare><feedburner:feedFlare href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2Falgorithmist%2Fru" src="http://www.newsgator.com/images/ngsub1.gif">Subscribe with NewsGator</feedburner:feedFlare><feedburner:feedFlare href="http://feeds.my.aol.com/add.jsp?url=http%3A%2F%2Ffeeds.feedburner.com%2Falgorithmist%2Fru" src="http://o.aolcdn.com/favorites.my.aol.com/webmaster/ffclient/webroot/locale/en-US/images/myAOLButtonSmall.gif">Subscribe with My AOL</feedburner:feedFlare><feedburner:feedFlare href="http://www.bloglines.com/sub/http://feeds.feedburner.com/algorithmist/ru" src="http://www.bloglines.com/images/sub_modern11.gif">Subscribe with Bloglines</feedburner:feedFlare><feedburner:feedFlare href="http://www.netvibes.com/subscribe.php?url=http%3A%2F%2Ffeeds.feedburner.com%2Falgorithmist%2Fru" src="http://www.netvibes.com/img/add2netvibes.gif">Subscribe with Netvibes</feedburner:feedFlare><feedburner:feedFlare href="http://fusion.google.com/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2Falgorithmist%2Fru" src="http://buttons.googlesyndication.com/fusion/add.gif">Subscribe with Google</feedburner:feedFlare><feedburner:feedFlare href="http://www.pageflakes.com/subscribe.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2Falgorithmist%2Fru" src="http://www.pageflakes.com/ImageFile.ashx?instanceId=Static_4&amp;fileName=ATP_blu_91x17.gif">Subscribe with Pageflakes</feedburner:feedFlare><feedburner:feedFlare href="http://lenta.yandex.ru/settings.xml?name=feed&amp;url=http%3A%2F%2Ffeeds.feedburner.com%2Falgorithmist%2Fru" src="http://lenta.yandex.ru/i/addfeed.gif">?????? ? ??????.?????</feedburner:feedFlare><feedburner:feedFlare href="http://www.plusmo.com/add?url=http%3A%2F%2Ffeeds.feedburner.com%2Falgorithmist%2Fru" src="http://plusmo.com/res/graphics/fbplusmo.gif">Subscribe with Plusmo</feedburner:feedFlare><feedburner:feedFlare href="http://www.thefreedictionary.com/_/hp/AddRSS.aspx?http%3A%2F%2Ffeeds.feedburner.com%2Falgorithmist%2Fru" src="http://img.tfd.com/hp/addToTheFreeDictionary.gif">Subscribe with The Free Dictionary</feedburner:feedFlare><feedburner:feedFlare href="http://www.bitty.com/manual/?contenttype=rssfeed&amp;contentvalue=http%3A%2F%2Ffeeds.feedburner.com%2Falgorithmist%2Fru" src="http://www.bitty.com/img/bittychicklet_91x17.gif">Subscribe with Bitty Browser</feedburner:feedFlare><feedburner:feedFlare href="http://www.live.com/?add=http%3A%2F%2Ffeeds.feedburner.com%2Falgorithmist%2Fru" src="http://tkfiles.storage.msn.com/x1piYkpqHC_35nIp1gLE68-wvzLZO8iXl_JMledmJQXP-XTBOLfmQv4zhj4MhcWEJh_GtoBIiAl1Mjh-ndp9k47If7hTaFno0mxW9_i3p_5qQw">Subscribe with Live.com</feedburner:feedFlare><feedburner:feedFlare href="http://mix.excite.eu/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2Falgorithmist%2Fru" src="http://image.excite.co.uk/mix/addtomix.gif">Subscribe with Excite MIX</feedburner:feedFlare><feedburner:feedFlare href="http://www.webwag.com/wwgthis.php?url=http%3A%2F%2Ffeeds.feedburner.com%2Falgorithmist%2Fru" src="http://www.webwag.com/images/wwgthis.gif">Subscribe with Webwag</feedburner:feedFlare><feedburner:feedFlare href="http://www.podcastready.com/oneclick_bookmark.php?url=http%3A%2F%2Ffeeds.feedburner.com%2Falgorithmist%2Fru" src="http://www.podcastready.com/images/podcastready_button.gif">Subscribe with Podcast Ready</feedburner:feedFlare><feedburner:feedFlare href="http://www.wikio.com/subscribe?url=http%3A%2F%2Ffeeds.feedburner.com%2Falgorithmist%2Fru" src="http://www.wikio.com/shared/img/add2wikio.gif">Subscribe with Wikio</feedburner:feedFlare><feedburner:feedFlare href="http://www.dailyrotation.com/index.php?feed=http%3A%2F%2Ffeeds.feedburner.com%2Falgorithmist%2Fru" src="http://www.dailyrotation.com/rss-dr2.gif">Subscribe with Daily Rotation</feedburner:feedFlare><entry gd:etag="W/&quot;Ck4DR3wycSp7ImA9WhVUEUU.&quot;"><id>tag:blogger.com,1999:blog-3994427606477253566.post-10759683536249729</id><published>2012-05-16T19:09:00.001+04:00</published><updated>2012-05-16T19:09:36.299+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-16T19:09:36.299+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Data mining" /><category scheme="http://www.blogger.com/atom/ns#" term="R" /><title>Random Forest с примерами на R</title><content type="html">Random Forest - один из моих любимых алгоритмов data mining. Во-первых он невероятно универсален, с его помощью можно решать как задачи регрессии так и классификации. Проводить поиск аномалий и отбор предикторов. Во-вторых это тот алгоритм, который действительно сложно применить неправильно. Просто потому, что в отличии от других алгоритмов у него мало настраиваемых параметров. И еще он удивительно прост по своей сути. И в то же время он отличается удивительной точностью. &lt;br /&gt;
&lt;br /&gt;
В чем же идея такого замечательного алгоритма? Идея проста: допустим у нас есть какой-то очень слабый алгоритм, скажем, &lt;a href="http://www.algorithmist.ru/2012/05/decision-trees-in-r.html"&gt;дерево принятия решений&lt;/a&gt;. Если мы сделаем очень много разных моделей с использованием этого слабого алгоритма и усредним результат их предсказаний, то итоговый результат будет существенно лучше. Это, так называемое, &lt;a href="http://en.wikipedia.org/wiki/Ensemble_learning"&gt;обучение ансамбля&lt;/a&gt; в действии. Алгоритм Random Forest потому и называется "Случайный Лес", для полученных данных он создает множество деревьев приятия решений и потом усредняет результат их предсказаний. Важным моментом тут является элемент случайности в создании каждого дерева. Ведь понятно, что если мы создадим много одинаковых деревьев, то результат их усреднения будет обладать точностью одного дерева.&lt;br /&gt;
&lt;br /&gt;
Как он работает? Предположим, у нас есть некие данные на входе. Каждая колонка соответствует некоторому параметру, каждая строка соответствует некоторому элементу данных. &lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-pJmWokzLCOA/T4RRstQ21aI/AAAAAAAABy4/3G-kBgXiXSY/s1600/RF1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://3.bp.blogspot.com/-pJmWokzLCOA/T4RRstQ21aI/AAAAAAAABy4/3G-kBgXiXSY/s320/RF1.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Мы можем выбрать, случайным образом, из всего набора данных некоторое количество столбцов и строк и построить по ним дерево принятия решений. &lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-B4JrdL70teo/T4RRxwQj6sI/AAAAAAAABzE/qBqKb3ykUtY/s1600/RF2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://1.bp.blogspot.com/-B4JrdL70teo/T4RRxwQj6sI/AAAAAAAABzE/qBqKb3ykUtY/s320/RF2.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Дальше мы можем повторить эту процедуру много-много раз и получить множество различных деревьев. Алгоритм построения дерева очень быстр. И поэтому нам не составит большого труда сделать столько деревьев, сколько будет нужно. При этом, все эти деревья в некотором смысле случайны, ведь для создания каждого из них мы выбирали случайное подмножество данных. &lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-7PQw0YCbdw8/T4RR2NvmoPI/AAAAAAAABzQ/mH1U6M05GNY/s1600/RF3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://1.bp.blogspot.com/-7PQw0YCbdw8/T4RR2NvmoPI/AAAAAAAABzQ/mH1U6M05GNY/s320/RF3.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
В оригинальной версии алгоритма, выделение случайного подмножество происходит на каждом шаге построения дерева. Но это не меняет сути и результаты получаются сравнимыми.&lt;br /&gt;
&lt;br /&gt;
Этот удивительно простой алгоритм и самым сложным шагом в его реализации является построение дерева decision tree. И не смотря на свою простоту он дает очень хорошие результаты в реальных задачах. С практической точки зрения у него есть одно огромное преимущество: он почти не требует конфигурации. Если мы возьмем любой другой алгоритм машинного обучения, будь то регрессия или нейронная сеть, они все имеют кучу параметров и их надо уметь подбирать под конкретную задачу. Алгоритм random forest имеет по-сути лишь один параметр: размер случайного подмножества выбираемого на каждом шаге построения дерева. Этот параметр важен, но даже значения по-умолчанию позволяют получить весьма приемлемые результаты. &lt;br /&gt;
&lt;br /&gt;
Перейдем теперь к примеру на R&lt;br /&gt;
В R random forest реализован в пакете под названием randomForest (удивительно). Для сегодняшнего примера я воспользуюсь теми же данными что и в &lt;a href="http://www.algorithmist.ru/2012/05/decision-trees-in-r.html"&gt;статье про деревья принятия решений&lt;/a&gt;. Попробуем предсказать изменения в объемах торгов акциями Газпрома на следующий день.  &lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;#Где взять пакет rusquant и как его установить можно посмотреть тут http://www.algorithmist.ru/p/rusquant.html 
library(rusquant)
getSymbols('GAZP', from='2003-05-01', to='2012-05-09', src='Finam')

#для повторяемости результатов
set.seed(1)
label &lt;- (Vo(GAZP) - lag(Vo(GAZP)))/lag(Vo(GAZP))
names(label) &lt;- c('label')
data &lt;- lag(GAZP)

data &lt;-cbind(lag(label), lag(label, k=2), lag(label, k=3), lag(label, k=4), lag(label, k=5), RSI(Cl(data)), MACD(Cl(data)), volatility(data), factor(weekdays(index(label))))
names(data) &lt;- c('Volume1', 'Volume2', 'Volume3', 'Volume4', 'Volume5', 'RSI', 'macd', 'signal', 'volatility', 'weekdays')
data &lt;- cbind(label, data)
data &lt;- data[complete.cases(data),]

# 80% данных используем для тренировки модели, 20% для тестирования
split &lt;- runif(dim(data)[1]) &gt; 0.2
train &lt;- data[split,]
test &lt;- data[!split,]

&lt;/pre&gt;
Код для тренировки random forest в R очень прост:

&lt;pre class="java" name="code"&gt;#random forest
library(randomForest)
rf &lt;- randomForest(label ~ ., train)
predictions &lt;- predict(rf, test)
print(sqrt(sum((as.vector(predictions - test$label))^2))/length(predictions))
&lt;/pre&gt;

В результате получается точность 0.02157 что полностью согласуется с результатами полученными с &lt;a href="http://www.algorithmist.ru/2012/05/decision-trees-in-r.html"&gt;помощью ансамбля деревьев&lt;/a&gt;. Можно ли как-то подкрутить этот алгоритм? Если не считать вопросы скорости работы, есть только один параметр который имеет смысл менять mtry - он определяет, сколько столбцов тестируется при поиске следующего разбиения, для каждого дерева. Но его изменения не сказываются на результате принципиальным образом. Лучшее значение, которое мне удалось выжать, при mtry=3 было 0.02149. Подобрать оптимальное значение mtry поможет функция tuneRF

Кроме того, random forest позволяет оценить качество используемых предикторов. 

&lt;pre class="java" name="code"&gt;importance(rf)
&lt;/pre&gt;
В нашем примере получилось:
&lt;pre&gt;           IncNodePurity
Volume1        136.11990
Volume2         24.80163
Volume3         15.96727
Volume4         19.43955
Volume5         32.80988
RSI             18.90843
macd            18.68504
signal          18.35107
volatility      21.04426
weekdays        13.54460
&lt;/pre&gt;
Т.е. самым значимым предиктором оказалось изменение объема в предыдущий день, все остальные несли гораздо меньше информации. Это функция алгоритма random forest может помочь при выборе предикторов и провести простейший &lt;a href="http://www.algorithmist.ru/2011/08/feature-selection-r.html"&gt;feature selection&lt;/a&gt;. 

Помимо перечисленных возможностей в пакете randomForest есть еще много интересных функций, часть из них позволяют лучше распределять работу при тренировке случайного леса, часть визуализировать данные, вставлять пропущенные значения итп. В следующей статье я расскажу о том, как мы боролись за скорость при использовании случайного леса в &lt;a href="http://www.algorithmist.ru/2012/05/kagglecom.html"&gt;последнем соревновании&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3994427606477253566-10759683536249729?l=www.algorithmist.ru' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/algorithmist/ru/~4/3P6tXdhskc0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.algorithmist.ru/feeds/10759683536249729/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.algorithmist.ru/2012/05/random-forest-r.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/10759683536249729?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/10759683536249729?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/algorithmist/ru/~3/3P6tXdhskc0/random-forest-r.html" title="Random Forest с примерами на R" /><author><name>SergE</name><uri>http://www.blogger.com/profile/01485674727390704780</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://4.bp.blogspot.com/_rst1xzlxZSU/S_t-TztXAKI/AAAAAAAABRs/LZpr_ILRYOU/S220/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-pJmWokzLCOA/T4RRstQ21aI/AAAAAAAABy4/3G-kBgXiXSY/s72-c/RF1.png" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://www.algorithmist.ru/2012/05/random-forest-r.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUUFQ3kzfCp7ImA9WhVVFk8.&quot;"><id>tag:blogger.com,1999:blog-3994427606477253566.post-2310522853554067374</id><published>2012-05-10T09:19:00.001+04:00</published><updated>2012-05-10T09:20:12.784+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-10T09:20:12.784+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Data mining" /><category scheme="http://www.blogger.com/atom/ns#" term="R" /><title>Деревья принятия решений с примерами на R</title><content type="html">Деревья принятия решения это алгоритм из серии "для домохозяек", обычно в том или ином виде с ним встречались все. Мое начальство очень любит такого рода картинки, наверняка и вам доводилось видеть подобное.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-4j7x_NVnz-Q/T4biLaoYyKI/AAAAAAAABzs/DXjefJZlWWU/s1600/advancedtree465-2.jpg" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="320" width="274" src="http://2.bp.blogspot.com/-4j7x_NVnz-Q/T4biLaoYyKI/AAAAAAAABzs/DXjefJZlWWU/s320/advancedtree465-2.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Это и есть дерево принятия решений. В силу своей наглядности этот алгоритм часто используется для визуализации данных. И тем не менее, несмотря на свою простоту, алгоритм является чрезвычайно важным в data mining и лежит в основе многих продвинутых методов, таких как random forest, boosted trees итп.  &lt;br /&gt;
&lt;br /&gt;
Итак, как же работает дерево принятия решений? Каждая вершина представляет собой точку разбиения данных. Вершина определяет некоторый простой критерий по которому мы делим данные на части. Далее я буду рассматривать только бинарные деревья, где каждая вершина делит данные на две части, именно они используются на практике. Кроме того, я буду рассматривать только простые критерии разбиения данных на части, а именно разбиение по единственному параметру. Таким образом, в каждой вершине дерева мы разбиваем данные на две части согласно значению одного из параметров:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-aoN-6eVVQcQ/T5Ajw0l0t6I/AAAAAAAAB0k/Qfeezpgu1Yk/s1600/DT1.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="240" width="320" src="http://2.bp.blogspot.com/-aoN-6eVVQcQ/T5Ajw0l0t6I/AAAAAAAAB0k/Qfeezpgu1Yk/s320/DT1.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Дальше для каждой части мы повторяем операцию и в итоге получаем дерево. Для простоты реализации часто используют рекурсию. Здесь неочевидными остаются только три вещи:&lt;br /&gt;
1. Как выбирается параметр по которому происходит разбиение&lt;br /&gt;
2. Как выбирается пороговое значения параметра &lt;br /&gt;
3. Когда алгоритм должен остановиться&lt;br /&gt;
&lt;br /&gt;
Ответы на эти вопросы зависят от реализации. Существует несколько популярных реализаций со странными названиями типа ID3, C4.5, CARD. Возьмем, к примеру, CARD, как он отвечает на эти вопросы? Параметр по которому происходит разбиение и пороговое значение этого параметра находятся методом полного перебора. Т.е. мы просто пробуем все возможные способы разбиения данных по каждому из параметров и оцениваем, насколько разбиение было удачным. А как оценить удачность разбиения? Очень просто, выполняя каждое разбиение мы получаем некоторое промежуточное дерево и его возможности классификации или регрессии нужно оценить. Вот тут то на помощь приходят понятия вроде &lt;a href="http://ru.wikipedia.org/wiki/%D0%98%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D0%B0%D1%8F_%D1%8D%D0%BD%D1%82%D1%80%D0%BE%D0%BF%D0%B8%D1%8F"&gt;информационной энтропии&lt;/a&gt; и &lt;a href="http://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D1%8D%D1%84%D1%84%D0%B8%D1%86%D0%B8%D0%B5%D0%BD%D1%82_%D0%94%D0%B6%D0%B8%D0%BD%D0%B8"&gt;коэффициентов Джини&lt;/a&gt;. В моей практике, степень удачности разбиения часто определялась условиями задачи, а именно, критерием, который мы пытаемся оптимизировать.&lt;br /&gt;
&lt;br /&gt;
Когда алгоритму остановиться? Есть два варианта: когда дальнейшие разбиения не способны улучшить дерево, например если в текущей вершине все элементы имеют одинаковое значение. Либо когда мы достигли определенного уровня, например в вершине 5 или меньше элементов (этот критерий используется по-умолчанию в реализации random forest в R)&lt;br /&gt;
&lt;br /&gt;
Существует несколько модулей в R которые реализуют деревья принятия решений, мне доводилось работать с tree и rpart. Они чрезвычайно похожи, в основе обоих лежит один и тот же алгоритм, но rpart предоставляет больше возможностей и быстрее работает. tree это реализация CART из языка программирования S, этот модуль был реализован для поддержки других пакетов и если вы не являетесь фанатом S, то стоит остановиться на rpart. &lt;br /&gt;
&lt;br /&gt;
Мы плавно пришли к примеру. Для примера попробуем предсказывать изменения в объемах торгов акциями Газпрома на рынке ММВБ на следующий день (цены предсказывать дело не благодарное, поэтому объемы). В качестве предикторов будем использовать изменения в объемах торгов за предыдущие 5 дней, несколько стандартных технических индикаторов и день недели. Итак, скачаем и сформируем данные: &lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;#Где взять пакет rusquant и как его установить можно посмотреть тут http://www.algorithmist.ru/p/rusquant.html 
library(rusquant)
getSymbols('GAZP', from='2003-05-01', to='2012-05-09', src='Finam')

#для повторяемости результатов
set.seed(1)
label &lt;- (Vo(GAZP) - lag(Vo(GAZP)))/lag(Vo(GAZP))
names(label) &lt;- c('label')
data &lt;- lag(GAZP)

data &lt;-cbind(lag(label), lag(label, k=2), lag(label, k=3), lag(label, k=4), lag(label, k=5), RSI(Cl(data)), MACD(Cl(data)), volatility(data), factor(weekdays(index(label))))
names(data) &lt;- c('Volume1', 'Volume2', 'Volume3', 'Volume4', 'Volume5', 'RSI', 'macd', 'signal', 'volatility', 'weekdays')
data &lt;- cbind(label, data)
data &lt;- data[complete.cases(data),]

# 80% данных используем для тренировки модели, 20% для тестирования
split &lt;- runif(dim(data)[1]) &gt; 0.2
train &lt;- data[split,]
test &lt;- data[!split,]

&lt;/pre&gt;
Для начала проведем небольшую проверку на адекватность (sanity check) чтобы понимать, будет ли наш алгоритм вообще предсказывать что-то или нет. Предположим, что объемы торгов вообще не меняются и посмотрим, какую ошибку даст это предположение:

&lt;pre class="java" name="code"&gt;#бенчмарк
print(sqrt(sum((as.vector(test$label))^2))/length(test$label))
&lt;/pre&gt;В результате получаем: 0.02397

Теперь посмотрим что способно предсказать одно единственное дерево:

&lt;pre class="java" name="code"&gt;library(rpart)

#тренируем дерево принятия решений
tree &lt;- rpart(label ~ ., train)
predictions &lt;- predict(tree, test)
print(sqrt(sum((as.vector(predictions - test$label))^2))/length(predictions))

&lt;/pre&gt;
Результат: 0.02442 - чуть-чуть хуже чем бенчмарк. Не очень то оптимистично.

Как можно улучшить этот результат? Как я уже писал, тенденция последнего времени, если одна модель работает плохо, надо объединить много моделей. Поступим следующим образом, будем из наших данных выбирать подмножества: примерно 70% столбцов и примерно 70% рядов, и потом для этих подмножеств генерить деревья. Дальше, получив много таких деревьев для разных подмножеств мы просто посчитаем арифметическое среднее для результатов их предсказаний. Смысл генерации подмножеств в том, чтобы как-то рандомизировать деревья, ведь если просто сгенерить много одинаковых деревьев и усреднить их предсказания то результаты никак не улучшатся.

&lt;i&gt;
Здесь есть очень важный момент в вызове функции rpart  следует передавать параметр control=rpart.control(cp=.0), он необходим для того чтобы алгоритм строил полные деревья не удаляя вершины, которые не приводят к существенному улучшению результата. Без этой опции деревья будут получаться слишком похожими и усреднение ничего не даст. &lt;/i&gt;


&lt;pre class="java" name="code"&gt;ntrees &lt;- 300
res &lt;- numeric(ntrees)
prd &lt;- numeric(nrow(test))
for(i in 1:ntrees){
    #выберем 70% рядов и колонок из множества данных
    x &lt;- runif(nrow(train))&gt;0.3;
    y &lt;- runif(ncol(train))&gt;0.3;
    #обязательно включим label
    y[1] &lt;- TRUE
    traindata &lt;- train[x,y]
    #генерим полное дерево не оптимизируя процесс
    tree &lt;- rpart(label ~ ., traindata, control=rpart.control(cp=.0))
    #усредняем предсказание со всеми предыдущими деревьями
    prd &lt;- prd + predict(tree, test)
    predictions &lt;- prd / i
    #оцениваем ошибку
    res[i] &lt;- sqrt(sum((as.vector(predictions - test$label))^2))/length(predictions)
    print(res[i])
}

plot(res, type='l')

&lt;/pre&gt;
В результате для 300 деревьев мы получаем ошибку: 0.02170 что намного лучше чем одно дерево и бенчмарк. Теперь результаты стали гораздо интересней. Посмотрим как изменяется ошибка в зависимости от количества деревьев:

&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-Bcw2Bpz5yOY/T6sKmWx4nFI/AAAAAAAAB14/WE2go6Jj8J8/s1600/Screen%2Bshot%2B2012-05-09%2Bat%2B5.23.21%2BPM.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="272" width="320" src="http://3.bp.blogspot.com/-Bcw2Bpz5yOY/T6sKmWx4nFI/AAAAAAAAB14/WE2go6Jj8J8/s320/Screen%2Bshot%2B2012-05-09%2Bat%2B5.23.21%2BPM.png" /&gt;&lt;/a&gt;&lt;/div&gt;На графике видно типичное поведение ошибки при тренировке ансамбля. Видно что где-то к 30 деревьям результат стабилизируется и дальнейшее добавление деревьев слабо влияет на ошибку. По-сути мы только что построили подобие алгоритма random forest для регрессии. Аналогичным образом можно построить алгоритм для классификации.  

Когда я запускал эти же алгоритмы на предварительно обработанных данных для &lt;a href="http://www.kaggle.com/c/benchmark-bond-trade-price-challenge"&gt;kaggle.com&lt;/a&gt;, первый дал результат 0.85076, что принесло бы примерно 100-ое место, а второй всего лишь с десятью деревьями дал бы 0.73623 очков и 10-ое место. Что не сильно хуже полноценного random forest, но об этом в следующий раз. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3994427606477253566-2310522853554067374?l=www.algorithmist.ru' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/algorithmist/ru/~4/jHgsfvVemdY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.algorithmist.ru/feeds/2310522853554067374/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.algorithmist.ru/2012/05/decision-trees-in-r.html#comment-form" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/2310522853554067374?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/2310522853554067374?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/algorithmist/ru/~3/jHgsfvVemdY/decision-trees-in-r.html" title="Деревья принятия решений с примерами на R" /><author><name>SergE</name><uri>http://www.blogger.com/profile/01485674727390704780</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://4.bp.blogspot.com/_rst1xzlxZSU/S_t-TztXAKI/AAAAAAAABRs/LZpr_ILRYOU/S220/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-4j7x_NVnz-Q/T4biLaoYyKI/AAAAAAAABzs/DXjefJZlWWU/s72-c/advancedtree465-2.jpg" height="72" width="72" /><thr:total>3</thr:total><feedburner:origLink>http://www.algorithmist.ru/2012/05/decision-trees-in-r.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUUGSH48fSp7ImA9WhVVEEg.&quot;"><id>tag:blogger.com,1999:blog-3994427606477253566.post-5258815502219458237</id><published>2012-05-03T17:53:00.003+04:00</published><updated>2012-05-03T17:53:49.075+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-05-03T17:53:49.075+04:00</app:edited><title>Kaggle.com мы третьи!</title><content type="html">И вот, наконец, закончилось мое первое соревнование в &lt;a href="http://www.kaggle.com/c/benchmark-bond-trade-price-challenge/leaderboard"&gt;kaggle.com с неожиданным для меня результатом: третье место&lt;/a&gt;. Это, пожалуй, были самые лучшие два месяца с точки зрения изучения data mining-а и я постараюсь в ближайшее время поделиться своими находками. &lt;br /&gt;
&lt;br /&gt;
Цель соревнования заключалась в том, чтобы предсказать цену бондов (или облигаций, если совсем по-русски) на основе некоторых данных, среди которых: цены последних 10 сделок, объемы сделок, типы сделок, тип бонда, и некая загадочная curve_based_price посчитанная компанией организатором и учитывающая множество фундаментальных факторов, но при этом абсолютно неопределенная для участников соревнования. Вот пример поведения цен и этой загадочной curve_based_price взятый из презентации организаторов.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-uo6NSFgSVY8/T5_q-WfjXRI/AAAAAAAAB1Q/yrHFcEax4TI/s1600/Screen%2Bshot%2B2012-05-01%2Bat%2B6.53.19%2BAM.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="125" width="320" src="http://2.bp.blogspot.com/-uo6NSFgSVY8/T5_q-WfjXRI/AAAAAAAAB1Q/yrHFcEax4TI/s320/Screen%2Bshot%2B2012-05-01%2Bat%2B6.53.19%2BAM.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Сложности добавляла низкая ликвидность на рынке бондов. Некоторые контракты не торговались неделями и, понятно, что ни о каком High Frequency Trading тут и речи идти не может. &lt;br /&gt;
&lt;br /&gt;
Моя первая идея была использовать random forest, благо что и пример организаторов использовал его и в целом было понятно что при такой неопределенности данных эта модель должна как-минимум работать неплохо. Поэтому, первым, что я сделал, это нормализовал цены относительно curve_based_price так, чтобы избавиться насовсем от абсолютных значений в долларах США. Это простое изменение в тот момент подняло меня на 5-ую строчку в рейтинге и мотивировало на дальнейшие исследования. Следующим существенным изменением было разбить тренировочные данные на несколько множеств (по типу сделки и по типу бонда). Основной эффект этого разбиения заключался в том, что эффективное время обучения random forest на большом множестве данных упало многократно. Это изменение подняло меня на первое место где я и находился около месяца. За это время успев получить приглашение от организаторов и посетить Stanford Conference of Quantitative Finance. &lt;br /&gt;
&lt;br /&gt;
А дальше был творческий кризис, я пробовал добавлять различные предикторы, пробовал оптимизировать модель, удалять outliers и даже переписал реализацию random forest на Java надеясь что оптимизировав функцию оценки смогу получить лучший результат. Но тщетно, некоторые изменения давали мне небольшое улучшение, но большинство не влияли на результат либо влияли негативно. Постепенно участники стали формировать команды и я потерял свое первое место и рисковал оказаться в районе 5-го места в финале. Что было бы весьма обидно ведь денежный приз дается только за первые три места. Но в последние три дня нам удалось объединить усилия с участником VikP, посчитать арифметическое среднее наших моделей и выйти на третье место. &lt;br /&gt;
&lt;br /&gt;
Немного разочаровало то, что модели победителей это просто смесь большого количества не самых лучших индивидуальных моделей. Т.е. рецепт победы в общем случае прост: натренируйте кучу разных моделей а потом с помощью линейной регресси найдите оптимальную комбинацию. Такой подход мне кажется немного ущербным. Ведь в основе часто лежат модели типа random forest и GBM которые уже сами по себе являются ансамблями большого числа еще более слабых моделей. Но, видимо, в настоящее время, этот подход является лучшим из известного, достаточно например посмотреть на описания решений победителей промежуточных туров &lt;a href="http://www.heritagehealthprize.com/c/hhp"&gt;Heritage Health Prize&lt;/a&gt;. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3994427606477253566-5258815502219458237?l=www.algorithmist.ru' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/algorithmist/ru/~4/M4GTlUwy0Dk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.algorithmist.ru/feeds/5258815502219458237/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.algorithmist.ru/2012/05/kagglecom.html#comment-form" title="9 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/5258815502219458237?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/5258815502219458237?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/algorithmist/ru/~3/M4GTlUwy0Dk/kagglecom.html" title="Kaggle.com мы третьи!" /><author><name>SergE</name><uri>http://www.blogger.com/profile/01485674727390704780</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://4.bp.blogspot.com/_rst1xzlxZSU/S_t-TztXAKI/AAAAAAAABRs/LZpr_ILRYOU/S220/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-uo6NSFgSVY8/T5_q-WfjXRI/AAAAAAAAB1Q/yrHFcEax4TI/s72-c/Screen%2Bshot%2B2012-05-01%2Bat%2B6.53.19%2BAM.png" height="72" width="72" /><thr:total>9</thr:total><feedburner:origLink>http://www.algorithmist.ru/2012/05/kagglecom.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0ANQHs6cCp7ImA9WhRVE0o.&quot;"><id>tag:blogger.com,1999:blog-3994427606477253566.post-1898499910609980815</id><published>2012-01-12T17:43:00.000+04:00</published><updated>2012-01-12T17:43:11.518+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-12T17:43:11.518+04:00</app:edited><title>Моя новая жизнь</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-tkt04cm5yzM/Tw5Z_hi4ujI/AAAAAAAABww/zPecHdXNPm8/s1600/y_af8cdae6.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="214" src="http://3.bp.blogspot.com/-tkt04cm5yzM/Tw5Z_hi4ujI/AAAAAAAABww/zPecHdXNPm8/s320/y_af8cdae6.jpg" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Вот собственно и всё. 17-ти часовой перелет позади, Россия осталась за океаном. А в окно уютной 2-ух спальной квартиры на нас смотрит Сан-Франциско, знаменитая Кремниевая долина, Калифорния, США. Да, это и есть та самая причина, по которой я практически не писал последнее время. Мы переехали. &lt;br /&gt;
&lt;br /&gt;
Всё это началось еще в апреле 2011 года, когда я проходил телефонное интервью в компании Zynga. Тогда это все казалось какой-то игрой не имеющей отношения к реальности и я и представить себе не мог, во что это выльется. В июне 2011 года Zynga приехали в Москву и провели серию собеседований, рассматривалось около 60 кандидатов прошедших телефонное интервью и из них было отобрано около 15 человек (точное число не знаю, кто-то потом передумал, кто-то сразу отказался). Интервью оказалось неожиданно простым. Ни тебе задачек на программирование, ни заковыристых вопросов про форму люков, в основном проверялись способности болтать. А знания, на мой взгляд, оценивались лишь поверхностно.  &lt;br /&gt;
&lt;br /&gt;
А дальше началась канитель. Сначала мы ждали результатов, потом офера, потом одобрение LCA, потом одобрения петиции на визу, потом документы из США, потом очередь в посольстве, потом дополнительную проверку, потом визу. Временами мне казалось, что я готов все бросить и забить. Временами я сомневался, а нужна ли нам эта Америка ведь и в России не плохо. Весь процесс занял где-то около полугода, в итоге, в середине декабря мы получили визы и начали готовиться к отъезду. &lt;br /&gt;
&lt;br /&gt;
В понедельник был мой первый рабочий день на новом месте. В офисе созданы все условия для того чтобы не только  работать, но и жить. Завтраки, обеды и ужины от собственных поваров, куча разнообразнейшей еды распиханной по всем уголкам, спортзал, массаж и даже парикмахер. Все это совершенно бесплатно для сотрудников. Многие добираются на работу на велосипеде и для хранения транспорта оборудовано несколько комнат. В общем, ничего подобного в России мне встречать не доводилось. Всему, однако, есть своя цена, нас сразу предупредили, что работать придется много. Что такое 'много', по их меркам, мне не очень понятно. &lt;br /&gt;
&lt;br /&gt;
Надеюсь, однако, что несмотря на количество работы, в обозримом будущем смогу возобновить ведение блога и, может быть, расскажу что-нибудь о американской жизни и работе программистом в Америке. Поживем - увидим. А пока, поздравляю всех с наступившим новым годом и рождеством и до новых встреч!&lt;br /&gt;
&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3994427606477253566-1898499910609980815?l=www.algorithmist.ru' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/algorithmist/ru/~4/07_vHSwUrcM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.algorithmist.ru/feeds/1898499910609980815/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.algorithmist.ru/2012/01/blog-post.html#comment-form" title="7 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/1898499910609980815?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/1898499910609980815?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/algorithmist/ru/~3/07_vHSwUrcM/blog-post.html" title="Моя новая жизнь" /><author><name>SergE</name><uri>http://www.blogger.com/profile/01485674727390704780</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://4.bp.blogspot.com/_rst1xzlxZSU/S_t-TztXAKI/AAAAAAAABRs/LZpr_ILRYOU/S220/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-tkt04cm5yzM/Tw5Z_hi4ujI/AAAAAAAABww/zPecHdXNPm8/s72-c/y_af8cdae6.jpg" height="72" width="72" /><thr:total>7</thr:total><feedburner:origLink>http://www.algorithmist.ru/2012/01/blog-post.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0MASHw9fyp7ImA9WhRRFU4.&quot;"><id>tag:blogger.com,1999:blog-3994427606477253566.post-8260099743225425044</id><published>2011-11-29T06:57:00.000+04:00</published><updated>2011-11-29T06:57:29.267+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-11-29T06:57:29.267+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="rusquant" /><category scheme="http://www.blogger.com/atom/ns#" term="R" /><title>Дивиденды rusquant 0.3.3</title><content type="html">Выкатил очередное обновление &lt;a href="http://www.algorithmist.ru/p/rusquant.html"&gt;rusquant&lt;/a&gt;, помимо мелких фиксов, теперь стали доступны дивиденды по множеству отечественных бумагам. Значения выкачиваются с сайта компании &lt;a href="http://gmi.troika.ru/rus/Capital_Markets/Custody/dividends.wbp"&gt;Тройка Диалог&lt;/a&gt;.  &lt;br /&gt;
Дивиденды за последний год можно получить командой:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;library(rusquant)
divs &amp;lt;-  getAllDividends()
&lt;/pre&gt;Все дивиденды, можно получить командой getAllDividends(allYears = TRUE)&lt;br /&gt;
Возвращаемый объект содержит следующие данные:&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;Symbol&lt;/i&gt; - id акции на бирже&lt;br /&gt;
&lt;i&gt;Name&lt;/i&gt; - полное название эмитента&lt;br /&gt;
&lt;i&gt;BoardDate&lt;/i&gt; - дата собрания совета директоров&lt;br /&gt;
&lt;i&gt;RegistryDate&lt;/i&gt; - дата закрытия реестра&lt;br /&gt;
&lt;i&gt;RecommendedDivs&lt;/i&gt; - рекомендуемые дивиденды&lt;br /&gt;
&lt;i&gt;Divs&lt;/i&gt; - выплаченные дивиденды&lt;br /&gt;
&lt;br /&gt;
Для примера использования, распечатаем дивидендную доходность российских компаний. В качестве базовой цены, берем цену закрытия акции в день закрытия реестра. Почему-то на сайте тройки этой информации нет, а она ведь гораздо интересней чем абсолютные величины дивидендов. &lt;br /&gt;
&lt;i&gt;Внимание! Код выполняется довольно долго, т.к. для каждой акции требуется сделать запрос на сервера finam и получить её стоимость. &lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;result &amp;lt;- NULL
    for(i in (1:length(divs[,1]))){
       d &amp;lt;- divs[i,]
       if (d$Divs&gt;0){
          try({
              quotes &amp;lt;- getSymbols(d$Symbol, src="Finam", from="2010-01-01", auto.assign=FALSE)
             if (!is.nan(quotes)){ 
                 price &amp;lt;- Cl(quotes[d$RegistryDate])
                 if (length(price)&amp;gt;0){
                      dd &amp;lt;- d$Divs
                      result &amp;lt;- rbind(result, data.frame(d$Symbol, d$Name, d$RegistryDate, as.numeric(dd)/as.numeric(price), stringsAsFactors=FALSE))
                 }
             }
          }, silent=TRUE)
       }
    }
colnames(result) &amp;lt;- c('Symbol', 'Name', 'RegistryDate', 'Divs')
result[with(result, order(Divs, decreasing=TRUE)),c('Symbol','RegistryDate','Divs')]

&lt;/pre&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-2Czuw3IKRIc/Ts9X15zAhpI/AAAAAAAABvU/mADVnkmuWWc/s1600/divs.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="320" width="221" src="http://1.bp.blogspot.com/-2Czuw3IKRIc/Ts9X15zAhpI/AAAAAAAABvU/mADVnkmuWWc/s320/divs.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Аналогично можно построить статистику для прошлых лет.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3994427606477253566-8260099743225425044?l=www.algorithmist.ru' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/algorithmist/ru/~4/9-E_ILeHaAQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.algorithmist.ru/feeds/8260099743225425044/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.algorithmist.ru/2011/11/rusquant-033.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/8260099743225425044?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/8260099743225425044?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/algorithmist/ru/~3/9-E_ILeHaAQ/rusquant-033.html" title="Дивиденды rusquant 0.3.3" /><author><name>SergE</name><uri>http://www.blogger.com/profile/01485674727390704780</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://4.bp.blogspot.com/_rst1xzlxZSU/S_t-TztXAKI/AAAAAAAABRs/LZpr_ILRYOU/S220/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-2Czuw3IKRIc/Ts9X15zAhpI/AAAAAAAABvU/mADVnkmuWWc/s72-c/divs.png" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://www.algorithmist.ru/2011/11/rusquant-033.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkADRn06fCp7ImA9WhRQFk0.&quot;"><id>tag:blogger.com,1999:blog-3994427606477253566.post-5312633606878976765</id><published>2011-11-28T06:00:00.000+04:00</published><updated>2011-12-11T14:52:57.314+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-12-11T14:52:57.314+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Data mining" /><category scheme="http://www.blogger.com/atom/ns#" term="R" /><title>Регуляризация линейной регрессии с примерами на R</title><content type="html">Последнее время немного выпал из реальности, погрузившись в изучение &lt;a href="https://www.ai-class.com/"&gt;AI &lt;/a&gt;и &lt;a href="http://www.ml-class.org/"&gt;Machine Learning&lt;/a&gt; на стенфордских курсах. Тем не менее, все новое и хорошо забытое старое, что там рассказали, натолкнуло на новые размышления. Вот к примеру, о &lt;a href="http://www.algorithmist.ru/2011/04/linear-regression-with-examples-in-r.html"&gt;линейной регрессии&lt;/a&gt; я уже писал. Тема эта важная, уже хотя бы потому, что алгоритмов регрессии вообще не так уж много, и линейная регрессия неизбежно остается одним из самых популярных. И не беда, что данные не всегда описываются линейными зависимостями, мы всегда можем вручную добавить в них нелинейные члены.&lt;br /&gt;
&lt;br /&gt;
Дабы сильно не усложнять задачу, сегодня я воспользуюсь синтетическими данными. Как обычно, пример будет на языке программирования R. Итак, данные:     &lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;"gen" &amp;lt;- function(x){
    return(3*x^2 + 2*x + 2 + rnorm(length(x))*0.5)
}

#Генерируем тренировочные данные
X &amp;lt;- runif(6)
Y &amp;lt;- gen(X)
#Данные для кросс-валидации
Xcv &amp;lt;- runif(50)
Ycv &amp;lt;- gen(Xcv)
&lt;/pre&gt;Тут все просто, данные распределены согласно некой выдуманной формуле 3*x&lt;sup&gt;2&lt;/sup&gt;+2*x+2 плюс некоторая погрешность, случайным образом распределенная. Тренировочных данных было умышленно взято очень мало, для наглядности (в реальных задачах их все равно, как правило, не хватает, так что это ничуть не мешает обобщению результатов). &lt;br /&gt;
&lt;br /&gt;
Допустим, мы не знаем как на самом деле распределены наши данные, но мы хотим построить какую-нибудь модель. Простейшая линейная регрессия по переменной x нам не подойдет, т.к. линейная регрессия предполагает только линейные зависимости. Поэтому, мы попробуем построить полиномиальную модель. И для этого добавим в наши данные все возможные степени x от 1 до 5. &lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;#Линейная регрессия по полиномиальному набору данных
train &amp;lt;- data.frame(Y, X, X^2, X^3, X^4, X^5)
colnames(train) &amp;lt;- c('Y', 'X', 'X2', 'X3', 'X4', 'X5')
simple &amp;lt;- lm(Y ~ X+X2+X3+X4+X5, train)
error &amp;lt;- sum((predict(simple, train)-Y)^2)/length(Y)
cat("Train error: ",error,"\n")

cv &amp;lt;- data.frame(Xcv, Xcv^2, Xcv^3, Xcv^4, Xcv^5)
colnames(cv) &amp;lt;- c('X', 'X2', 'X3', 'X4', 'X5')
error &amp;lt;- sum((predict(simple, cv)-Ycv)^2)/length(Ycv)
cat("Cross-validation error: ",error,"\n")

#Построим кривую, описывающую наше решение
x &amp;lt;- (1:100)/100
test = data.frame(x, x^2, x^3, x^4, x^5)
names(test) &amp;lt;- c('X', 'X2', 'X3', 'X4', 'X5')
y0 &amp;lt;- predict(simple, test)

plot(X, Y, ylim=range(y0), xlim=c(0,1))
lines(x,y0, col='red')
&lt;/pre&gt;В результате, ошибка на тренировочных данных близка к нулю, а на данных для кросс-валидации очень велика. &lt;br /&gt;
&lt;pre&gt;Train error:  4.951098e-13 
Cross-validation error:  6.751967 
&lt;/pre&gt;Это, так называемый, overfitting. И он абсолютно ожидаемый, ведь через 6 точек на плоскости всегда можно провести полином 5-ой степени. &lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-q6w6m9eMho8/Ts9KZG1YG7I/AAAAAAAABuY/Q77gXDVMy8U/s1600/overfit.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="313" width="320" src="http://2.bp.blogspot.com/-q6w6m9eMho8/Ts9KZG1YG7I/AAAAAAAABuY/Q77gXDVMy8U/s320/overfit.png" /&gt;&lt;/a&gt;&lt;/div&gt;Тут, читатель может заметить, а к чему, собственно, нам полином 5-ой степени, при таком количестве точек разумней было бы использовать меньше степеней. Согласен, случай выдуманный, но аналогичная проблема возникает в случае сильно-многомерных данных. Например, при обработке текстов. Где переменная X запросто может быть вектором с 10000 элементов. &lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Что делать?&lt;/h4&gt;Для начала, посмотрим на формулу, которой мы пытаемся описать наши данные:&lt;br /&gt;
y&lt;sub&gt;t&lt;/sub&gt; = a&lt;sub&gt;0&lt;/sub&gt; + a&lt;sub&gt;1&lt;/sub&gt;*x  + a&lt;sub&gt;2&lt;/sub&gt;*x  + a&lt;sub&gt;3&lt;/sub&gt;*x  + a&lt;sub&gt;4&lt;/sub&gt;*x  + a&lt;sub&gt;5&lt;/sub&gt;*x &lt;br /&gt;
Мы пытаемся подобрать эти коэффициенты a&lt;sub&gt;i&lt;/sub&gt; таким образом, чтобы минимизировать ошибку т.е. минимизировать выражение:&lt;br /&gt;
L = &amp;Sigma;(y&lt;sub&gt;t&lt;/sub&gt;-y)&lt;sup&gt;2&lt;/sup&gt;&lt;br /&gt;
А что если в это выражение добавить еще какой-нибудь член, чтобы как-то уменьшить величины коэффициентов a&lt;sub&gt;i&lt;/sub&gt;? Например, вместо L можно было бы минимизировать выражение:&lt;br /&gt;
&lt;br /&gt;
L1 = &amp;Sigma;(y&lt;sub&gt;t&lt;/sub&gt;-y)&lt;sup&gt;2&lt;/sup&gt; + &amp;lambda;*&amp;Sigma;|a&lt;sub&gt;i&lt;/sub&gt;|&lt;br /&gt;
&lt;br /&gt;
или другой вариант&lt;br /&gt;
&lt;br /&gt;
L2 = &amp;Sigma;(y&lt;sub&gt;t&lt;/sub&gt;-y)&lt;sup&gt;2&lt;/sup&gt; + &amp;lambda;*&amp;Sigma;(a&lt;sub&gt;i&lt;/sub&gt;)&lt;sup&gt;2&lt;/sup&gt;&lt;br /&gt;
&lt;br /&gt;
Что мы здесь сделали? Мы добавили в наше выражение штраф за большие значения a&lt;sub&gt;i&lt;/sub&gt;. И величина этого штрафа пропорциональна величине параметра &amp;lambda;, с помощью которого мы теперь можем настраивать наш алгоритм. &lt;br /&gt;
&lt;br /&gt;
Первый вариант носит название L1-регуляризация (в английской литературе LASSO regression), второй вариант L2-регуляризация или регуляризация Тихонова (в английской литературе ridge regression)&lt;br /&gt;
&lt;br /&gt;
Оба варианта реализованы в R. К сожалению, реализация немного не последовательна в используемых входных параметрах и возвращаемых значениях. Тем не менее, мы можем посмотреть её использование чтобы сравнить результат работы.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;L1-регуляризация или Lasso regression&lt;/h4&gt;L1 регуляризация реализована в пакете lars. &lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;#Lasso regression
#Требуется установить пакет lars если его нет
#install.packages('lars')
library(lars)

train &amp;lt;- cbind(X, X^2, X^3, X^4, X^5)
colnames(train) &amp;lt;- c('X', 'X2', 'X3', 'X4', 'X5')
lasso &amp;lt;- lars(train, Y, type='lasso')

cv &amp;lt;- cbind(Xcv, Xcv^2, Xcv^3, Xcv^4, Xcv^5)
colnames(cv) &amp;lt;- c('X', 'X2', 'X3', 'X4', 'X5')

x &amp;lt;- (1:100)*(max(X)*1.1)/100
test &amp;lt;- cbind(x, x^2, x^3, x^4, x^5)
colnames(test) &amp;lt;- c('X', 'X2', 'X3', 'X4', 'X5')


stats &amp;lt;- NULL
lambda &amp;lt;- 0
plot(X, Y, ylim=c(-5, 10), pch=20, col='red')
points(Xcv, Ycv, pch=20, col='blue')
ls &amp;lt;- NULL
cs &amp;lt;- NULL
for(i in 1:15){
    Yp &amp;lt;- predict(lasso, train, s=lambda, type='fit', mode='lambda')
    trainError &amp;lt;- sum((Yp$fit-Y)^2)/length(Y)

    Yp &amp;lt;- predict(lasso, cv, s=lambda, type='fit', mode='lambda')
    cvError &amp;lt;- sum((Yp$fit-Ycv)^2)/length(Ycv)

    stats &amp;lt;- rbind(stats, c(lambda, trainError, cvError))
    if (i%%5==1){
        #Построим кривую, описывающую наше решение
        y0 &amp;lt;-  predict(lasso, test, lambda, type='fit', mode='lambda')
        lines(x,y0$fit, col=i)
        ls &amp;lt;- c(ls, paste("lambda=",lambda,sep=''))
        cs &amp;lt;- c(cs, i)
    }

    lambda &amp;lt;- ifelse(lambda==0,0.00000001, lambda*10)
}
legend("topleft", c("Train", "Cross-validation"), pch=20, col=c('red', 'blue'))
legend("bottomright",inset=0.05, legend=ls, pch=20, col=cs, text.col=cs, bty="n")



plot(stats[,2], ylim=range(stats[,2:3]), type='l', col='red', ylab="error", xlab="log(lambda)")
lines(stats[,3], col='blue')

#Оптимальное значение lamda=1
coef(lasso, s=1, mode='lambda')

&lt;/pre&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-iKtcvL-X6Z0/Ts9NFpJUYjI/AAAAAAAABuk/wUyxA9sguWI/s1600/lasso.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="302" width="320" src="http://4.bp.blogspot.com/-iKtcvL-X6Z0/Ts9NFpJUYjI/AAAAAAAABuk/wUyxA9sguWI/s320/lasso.png" /&gt;&lt;/a&gt;&lt;/div&gt;На графике показано несколько решений, для разных значений &amp;lambda;. При &amp;lambda;=0 получается обычная линейная регрессия, которую я описал выше. При очень больших значениях &amp;lambda; получается горизонтальная линия, т.е. решение при котором коэффициенты a&lt;sub&gt;i&lt;/sub&gt;=0, для всех i&gt;0. Оба крайних случая очевидно не являются оптимальными решениями. Как же найти оптимальное? Для этого построим график ошибки.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-KScnZPNI3iQ/Ts9OAWC3ysI/AAAAAAAABuw/GFhB3EH8-14/s1600/lasso_error.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="302" width="320" src="http://2.bp.blogspot.com/-KScnZPNI3iQ/Ts9OAWC3ysI/AAAAAAAABuw/GFhB3EH8-14/s320/lasso_error.png" /&gt;&lt;/a&gt;&lt;/div&gt;Здесь, на графике, красным цветом отмечена ошибка для тренировочного множества, а синим цветом ошибка для cross-validation множества. Видно, что ошибка тренировочного множества сначала очень мала (overfitting), но постепенно возрастает (underfitting).  В то же время ошибка cross-validation имеет выраженный минимум, который для нас и будет оптимальным значением &amp;lambda; В нашем случае, это оптимальное значение оказалось равным 10.  &lt;br /&gt;
&lt;br /&gt;
У L1 регуляризации есть одна полезная особенность. Многие коэффициенты оказываются в точности равны 0. Например, в нашем случае:&lt;br /&gt;
&lt;pre&gt;X        X2        X3        X4        X5 
0.2989565 0.0000000 0.0000000 0.0000000 5.2679886 
&lt;/pre&gt;Т.е. L1 регуляризация может быть использована еще и как алгоритм &lt;a href="http://www.algorithmist.ru/2011/08/feature-selection-r.html"&gt;feature selection&lt;/a&gt;. &lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Регуляризация Тихонова или Ridge regression&lt;/h4&gt;Этот алгоритм реализован в пакете MASS. К сожалению, реализация не совсем стандартная, например, пакет MASS не предоставляет нам функции predict которой так привычно пользоваться тем, кто знаком с R. Тем не менее, не представляет никакой сложности самостоятельная реализация этой функции через умножение матриц.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;#Ridge regression
#Требуется установить пакет MASS если его нет
#install.packages('MASS')
library(MASS)

train &amp;lt;- data.frame(Y, X, X^2, X^3, X^4, X^5)
colnames(train) &amp;lt;- c('Y', 'X', 'X2', 'X3', 'X4', 'X5')

stats &amp;lt;- NULL
lambda &amp;lt;- 0
x &amp;lt;- (1:100)/100
test &amp;lt;- as.matrix(cbind(1, x, x^2, x^3, x^4, x^5))

plot(X, Y, ylim=c(-5,15), pch=20, col='red')
points(Xcv, Ycv, pch=20, col='blue')
ls &amp;lt;- NULL
cs &amp;lt;- NULL
for(i in (1:15)) {

    ridge &amp;lt;- lm.ridge(Y ~ X+X2+X3+X4+X5, train, lambda=lambda)
    Yp &amp;lt;- as.matrix(cbind(1, X, X^2, X^3, X^4, X^5)) %*% as.matrix(coef(ridge))
    trainError &amp;lt;- sum((Yp - Y)^2)/length(Y)

    cv &amp;lt;- as.matrix(cbind(1, Xcv, Xcv^2, Xcv^3, Xcv^4, Xcv^5))
    Yp &amp;lt;- cv %*% as.matrix(coef(ridge))
    cvError &amp;lt;- sum((Yp-Ycv)^2)/lentgh(Ycv)

    stats &amp;lt;- rbind(stats, c(lambda, trainError, cvError))
    if (i%%5==1){
        #Построим кривую, описывающую наше решение
        y0 &amp;lt;- test %*% as.matrix(coef(ridge))
        lines(x,y0, col=i)
        ls &amp;lt;- c(ls, paste("lambda=",lambda,sep=''))
        cs &amp;lt;- c(cs, i)
    }
    lambda &amp;lt;- ifelse(lambda==0,0.00000001, lambda*10)

}
legend("topleft", c("Train", "Cross-validation"), pch=20, col=c('red', 'blue'))
legend("bottomright",inset=0.05, legend=ls, pch=20, col=cs, text.col=cs, bty="n")


#Рисуем изменение ошибки CV
plot(stats[,2], ylim=range(stats[,2:3]), type='l', col='red', ylab="error", xlab="log(lambda)")
lines(stats[,3], col='blue')

#Оптимальное значение lamda=10
ridge &amp;lt;- lm.ridge(Y ~ X+X2+X3+X4+X5, train, lambda=10)
coef(ridge)

&lt;/pre&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-T3MD_PnkgnI/Ts9QlbcR5mI/AAAAAAAABu8/z6vyVd6DvWs/s1600/ridge.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="305" width="320" src="http://4.bp.blogspot.com/-T3MD_PnkgnI/Ts9QlbcR5mI/AAAAAAAABu8/z6vyVd6DvWs/s320/ridge.png" /&gt;&lt;/a&gt;&lt;/div&gt;Аналогично случаю L1 регрессии, здесь мы тоже можем определить оптимальные значения параметра &amp;lambda;&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-9_Rj5iJz9Fk/Ts9Q1Jk6QGI/AAAAAAAABvI/Zycv0_5XqEY/s1600/ridge_error.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="305" width="320" src="http://4.bp.blogspot.com/-9_Rj5iJz9Fk/Ts9Q1Jk6QGI/AAAAAAAABvI/Zycv0_5XqEY/s320/ridge_error.png" /&gt;&lt;/a&gt;&lt;/div&gt;Оптимальное значение &amp;lambda; в данном случае оказалось равным 1. И соответствующие коэффициенты, как видим, совсем не равны 0. &lt;br /&gt;
&lt;pre&gt;X        X2        X3        X4        X5 
3.0257674 0.7951164 0.7637522 0.8631815 1.0352908 1.2643954 
&lt;/pre&gt;&lt;br /&gt;
Естественно методы регуляризации не ограничиваются L1 и L2-регуляризациями. Существует еще несколько популярных подходов, например алгоритм elastic net, включающий в себя как L1 так и L2.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3994427606477253566-5312633606878976765?l=www.algorithmist.ru' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/algorithmist/ru/~4/s6DrU08d2qw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.algorithmist.ru/feeds/5312633606878976765/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.algorithmist.ru/2011/11/regularization-with-examples-in-r.html#comment-form" title="7 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/5312633606878976765?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/5312633606878976765?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/algorithmist/ru/~3/s6DrU08d2qw/regularization-with-examples-in-r.html" title="Регуляризация линейной регрессии с примерами на R" /><author><name>SergE</name><uri>http://www.blogger.com/profile/01485674727390704780</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://4.bp.blogspot.com/_rst1xzlxZSU/S_t-TztXAKI/AAAAAAAABRs/LZpr_ILRYOU/S220/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-q6w6m9eMho8/Ts9KZG1YG7I/AAAAAAAABuY/Q77gXDVMy8U/s72-c/overfit.png" height="72" width="72" /><thr:total>7</thr:total><feedburner:origLink>http://www.algorithmist.ru/2011/11/regularization-with-examples-in-r.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEIFQ30ycSp7ImA9WhdUE0g.&quot;"><id>tag:blogger.com,1999:blog-3994427606477253566.post-7237927797451658862</id><published>2011-09-30T07:14:00.000+04:00</published><updated>2011-09-30T07:15:12.399+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-30T07:15:12.399+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Text mining" /><title>[Text mining] Индекс настроений фондовых рынков</title><content type="html">В прошлом году я  уже &lt;a href="http://www.algorithmist.ru/2010/11/twitter.html"&gt;писал о нашумевшей работе американцев&lt;/a&gt;, которые умудрились предсказывать индекс S&amp;P500 по сообщениям в twitter. Это было лишь начало, сейчас в интернете можно найти множество статей посвященных анализу текстовых данных для предсказания котировок на бирже:&lt;br /&gt;
&lt;br /&gt;
"&lt;a href="http://www.aaai.org/ocs/index.php/ICWSM/ICWSM10/paper/download/1529/1904"&gt;Trading Strategies to Exploit Blog and News Sentiment&lt;/a&gt;" (Zhang, Skiena 2010)&lt;br /&gt;
"&lt;a href="http://thesis.haverford.edu/dspace/handle/10066/4888"&gt;The Predictive Power of Financial Blogs&lt;/a&gt;" (Frisbee 2010)&lt;br /&gt;
"&lt;a href="http://portal.acm.org/citation.cfm?id=1860669"&gt;An analysis of verbs in financial news articles and their impact on stock price&lt;/a&gt;" (Schumaker 2010)&lt;br /&gt;
&lt;br /&gt;
Но это все там, за океаном. А что же у нас, в России?  &lt;br /&gt;
&lt;br /&gt;
Искать правду в твиттере для российского рынка, мне показалось, бесполезным. Во-первых твиттером у нас пользуются по-прежнему единицы, а во-вторых у американцев, так или иначе, в фондовый рынок вовлечено почти все население, у нас же это развлечение для избранных. Зато в России есть раскрученная социальная сеть трейдеров comon.ru. На этом ресурсе публикуется несколько десятков статей ежедневно и, что самое главное, они по теме. Что может быть лучше для задачи анализа текста?&lt;br /&gt;
&lt;br /&gt;
Итак, сказано - сделано. Я решил проверить связано ли как-то настроение блогеров comon.ru с движениями на рынке и, самое интересное, могут ли эти данные быть использованы для предсказания движений рынка. Первым делом, было выкачано около 10 тыс. статей от 100 авторов с comon.ru, вместе с датой публикации и именем автора. Потом текст статей был обработан: удалены все элементы HTML-разметки и &lt;a href="http://www.algorithmist.ru/2010/12/stop-symbols-in-russian.html"&gt;стоп-слова&lt;/a&gt; а у всех оставшихся слов была &lt;a href="http://www.algorithmist.ru/2010/12/porter-stemmer-russian.html"&gt;выделена основа&lt;/a&gt;. &lt;br /&gt;
&lt;br /&gt;
Дальше надо было научиться как-то характеризовать текст. Для начала я выбрал всего две категории: негативную и позитивную. Под позитивной понимаются тексты в которых идет речь о росте, восходящем тренде и повышении. Негативные тексты, напротив, изобилуют словами типа дефолт, кризис, обвал и понижение. Интересно, что негативных слов получилось вдвое больше чем позитивных. &lt;br /&gt;
&lt;br /&gt;
Существует несколько разных подходов для составления списка позитивных и негативных слов. Одним из них может быть&lt;a href="http://habrahabr.ru/blogs/algorithm/110078/"&gt; латентно-семантический анализ&lt;/a&gt;. В простейшем случае, можно собрать списки слов вручную. &lt;br /&gt;
&lt;br /&gt;
Предположим, что некоторым образом, мы составили списки слов обладающих позитивной окраской и негативной окраской. Далее, самой простой метрикой настроения текста будет разность количества позитивных и количества негативных слов в нем. Очевидно, что подобная метрика не идеальна и существует масса различных улучшений, позволяющих добиться еще более значимых результатов. Но это тема отдельной статьи. В данном случае мы можем использовать даже самую простую метрику.&lt;br /&gt;
&lt;br /&gt;
Когда я прогнал полученный алгоритм по набору текстов, стало понятно, что результаты крайне нестабильны. И понятно почему. Одни авторы используют много негативных слов, другие мало, одни оптимисты, другие пессимисты, одни пишут часто и регулярно, другие редко, у одних тексты состоят из нескольких тысяч слов, у других они больше похоже на посты в твиттере. Чтобы сгладить влияние отдельных авторов, было проведено нормирование индекса настроений, по каждому автору, предположив, что среднее настроение автора за весь период жизни на комоне нейтрально. Попутно, кстати, получилась интересная метрика, показывающая насколько одни авторы более пессимистичны или оптимистичны чем другие.&lt;br /&gt;
&lt;br /&gt;
Далее, после усреднения данных по дням, получился некоторый индекс, назовем его индексом настроений comon.ru. Это число, которое характеризует среднее настроение блогеров comon.ru в этот день. Интересным оказался график изменения индекса настроения:&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-7pRiv-ijpt8/ToRT9o93z1I/AAAAAAAABtE/srnkTWxwsQQ/s1600/Sentiment.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="210" width="320" src="http://4.bp.blogspot.com/-7pRiv-ijpt8/ToRT9o93z1I/AAAAAAAABtE/srnkTWxwsQQ/s320/Sentiment.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Из графика видно, что вначале года, несмотря на ряд провалов, среднее значение индекса настроений большую часть времени больше нуля. Во второй половине года настроения изменились и стали все больше негативными. Что в это время происходило на бирже, комментировать не надо. Кроме того, индекс настроений успешно предсказал два самых крупных обвала года. &lt;br /&gt;
&lt;br /&gt;
Следующим шагом, я проверил а сколько можно было бы заработать если покупать вечером того дня когда индекс настроений больше нуля и закрывать позицию на следующий день. И аналогично для отрицательных значений индекса настроений. Оказалось, что среднее дневное изменение индекса ММВБ в день, следующий за днем с позитивным настроением составляет: &lt;b&gt;0.13%&lt;/b&gt;. А в дни следующие за днями с негативным настроением &lt;b&gt;-0.34%&lt;/b&gt;. Результат мне показался вдохновляющим. И было решено проверить математически гипотезу о том, что индекс настроения комона предсказывает значения ММВБ. Для этого, я воспользовался методом описанным в &lt;a href="http://arxiv.org/PS_cache/arxiv/pdf/1010/1010.3003v1.pdf"&gt;оригинальной работе&lt;/a&gt;, а именно, причинным тестом Грейнджера:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;Model 1: MICEX ~ Lags(MICEX, 1:1) + Lags(SIGNAL, 1:1)
Model 2: MICEX ~ Lags(MICEX, 1:1)
  Res.Df Df      F  Pr(&gt;F)  
1    178                    
2    179 -1 2.9539 &lt;b&gt;0.08741&lt;/b&gt; .
&lt;/pre&gt;&lt;br /&gt;
P-value теста составило 0.087, что меньше 0.1. Значит наша гипотеза о том что индекс настроений комона предсказывает изменения индекса ММВБ на следующий день верна с вероятностью 90%. Что является неплохим результатом, вполне сравнимым с результатом в оригинальной работе с твиттером.&lt;br /&gt;
&lt;br /&gt;
Большим недостатком полученного индекса является его высокая волатильность. По-идее, добавление других источников данных и улучшение алгоритма анализа настроений должны её сгладить. С другой стороны, увеличение количества источников данных может снизить точность результатов. &lt;br /&gt;
&lt;br /&gt;
Что использовалось для данной работы?&lt;br /&gt;
1. Граббер текстов с comon.ru и предварительная обработка были написан на Java&lt;br /&gt;
2. Вся математика, анализ и построение графиков выполнялись на R с использованием библиотек &lt;a href="http://cran.r-project.org/web/packages/lmtest/index.html"&gt;lmtest &lt;/a&gt;и &lt;a href="http://www.algorithmist.ru/p/rusquant.html"&gt;rusquant&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Какие варианты развития?&lt;br /&gt;
1. В отличии от твиттера, количество авторов комона которые пишут регулярно не велико. Можно ввести что-то вроде предсказательного рейтинга автора, тем самым улучшив общий прогноз.&lt;br /&gt;
2. Учитывать оценки других пользователей и комментарии к каждой статье.&lt;br /&gt;
3. Добавить анализ морфологии русского языка, учитывать время в котором написаны отдельные предложения статьи. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3994427606477253566-7237927797451658862?l=www.algorithmist.ru' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/algorithmist/ru/~4/fgzPlSfGeaI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.algorithmist.ru/feeds/7237927797451658862/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.algorithmist.ru/2011/09/text-mining-sentiment-analysis.html#comment-form" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/7237927797451658862?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/7237927797451658862?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/algorithmist/ru/~3/fgzPlSfGeaI/text-mining-sentiment-analysis.html" title="[Text mining] Индекс настроений фондовых рынков" /><author><name>SergE</name><uri>http://www.blogger.com/profile/01485674727390704780</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://4.bp.blogspot.com/_rst1xzlxZSU/S_t-TztXAKI/AAAAAAAABRs/LZpr_ILRYOU/S220/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-7pRiv-ijpt8/ToRT9o93z1I/AAAAAAAABtE/srnkTWxwsQQ/s72-c/Sentiment.png" height="72" width="72" /><thr:total>3</thr:total><feedburner:origLink>http://www.algorithmist.ru/2011/09/text-mining-sentiment-analysis.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C08NQH0_fyp7ImA9WhdVFEQ.&quot;"><id>tag:blogger.com,1999:blog-3994427606477253566.post-2895952867805233927</id><published>2011-09-20T06:55:00.000+04:00</published><updated>2011-09-20T08:11:31.347+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-20T08:11:31.347+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="R" /><title>Этот безумный R</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-hcyHjfmIfYw/TngAgEdTcQI/AAAAAAAABs4/BXKzRwgItXM/s1600/Rlogo.png" imageanchor="1" style="clear:left; float:left;margin-right:1em; margin-bottom:1em"&gt;&lt;img border="0" height="243" width="320" src="http://2.bp.blogspot.com/-hcyHjfmIfYw/TngAgEdTcQI/AAAAAAAABs4/BXKzRwgItXM/s320/Rlogo.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Сначала мне казалось, что это простой и примитивный язык, для небольших скриптов, с уклоном в статистику. Потом мне стало казаться, что это самый обычный язык программирования, с уклоном в статистику. Сейчас мне кажется, что это гремучая смесь классических императивных языков вроде C++ или Java и матерой функциональщины вроде Haskell или Lisp. И всё это R.&lt;br /&gt;
&lt;br /&gt;
На самом деле, действительно, R многое заимствовал из Lisp. Функции в R являются полноценными сущностями. Их можно не только вызывать и определять но и передавать в качестве параметров или составлять из них сложные выражения. В R доступны &lt;a href="http://ru.wikipedia.org/wiki/%D0%97%D0%B0%D0%BC%D1%8B%D0%BA%D0%B0%D0%BD%D0%B8%D0%B5_%28%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%29"&gt;замыкания &lt;/a&gt; и &lt;a href="http://ru.wikipedia.org/wiki/%D0%9E%D1%82%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5_%D0%B2%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D1%8F"&gt;отложенные вычисления&lt;/a&gt;. При этом, в отличии, например, от Haskell, R не страдает от функциональной чистоты и допускает функции с побочными эффектами. Я уже не говорю о том, что в R еще и объектно-ориентированный язык. Правда, признаюсь честно, реализация объектно-ориентированного программирования в R больше похожа на помойку. В R есть несколько ООП-систем и каждая живет своей жизнью. Все это многообразие, делает работу сложней и интересней. Нередко, разобравшись в концепции, начинаешь понимать, как тот или иной код можно написать короче и эффективней. Некоторыми такими концепциями я бы и хотел сегодня поделиться.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
&lt;h3&gt;Векторизация&lt;/h3&gt;Векторизация это первое с чем сталкиваются все R разработчики не всегда, правда, осознанно. Векторы, в R повсюду. Даже когда мы просто объявляем в консоли число 1, мы создаем вектор с единственным элементом.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;&amp;gt; 1
[1] 1
&lt;/pre&gt;&lt;br /&gt;
Как следствие этого, большинство функций в R работают с векторами. И обычно об этом даже не нужно думать. Например:&lt;br /&gt;
&lt;pre class="java" name="code"&gt;&amp;gt; c(1,2,3)+c(4,5,6)
[1] 5 7 9
&amp;gt; c(1,2,3)*c(4,5,6)
[1]  4 10 18
&lt;/pre&gt;&lt;br /&gt;
Чертовски удобно, когда задача сводится к обработке больших массивов однотипных данных. Что, в общем-то, является типичной задачей в статистике. &lt;br /&gt;
&lt;br /&gt;
Собственные функции тоже часто получаются векторизованными сами собой. Но не всегда. Например конструкция if не векторизована, но зато есть векторизованная конструкция ifelse которую и рекомендуется использовать в большинстве случаев:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;&gt; a &lt;- c(1,-2,-3,4)
&gt; ifelse(a&gt;0, 1, 0)
[1] 1 0 0 1
&lt;/pre&gt;&lt;br /&gt;
&lt;h3&gt;Recycling rule&lt;/h3&gt;Эта концепция важна в сочетании с векторизацией. Что будет, если в примере выше два вектора будут разной длины? Оказывается, что если размер более длинного вектора кратен размеру короткого, то короткий вектор будет автоматически повторен нужное количество раз. Если же не кратен, то произойдет то же самое, но будет показано предупреждение (которое, правда, можно подавить) &lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;&amp;gt; c(1,2)+c(1,2,3,4)
[1] 2 4 4 6
&amp;gt; c(1,2,3)+c(1,2,3,4)
[1] 2 4 6 5
Предупреждение
In c(1, 2, 3) + c(1, 2, 3, 4) :
  длина большего объекта не является произведением длины меньшего объекта
&lt;/pre&gt;&lt;br /&gt;
Это очень удобное свойство, но главное его удобство проявляется неявно. Например, когда мы хотим все значения в векторе увеличить на единицу:&lt;br /&gt;
&lt;pre class="java" name="code"&gt;&amp;gt; c(1,2,3)+1
[1] 2 3 4
&lt;/pre&gt;Мы ведь помним, что единица это тоже вектор, с длиной 1.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Всё является функцией&lt;/h3&gt;Да, R действительно функциональный язык программирования. Функцией является всё, даже скобочки {, [, операторы присвоения &amp;lt;- и условные операторы if, while и т.п. Даже ключевое слово, объявляющее функции function - тоже является функцией. И главное, их можно переопределить!&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;&amp;gt; `if`
.Primitive("if")
&amp;gt; `if` &amp;lt;- function(...) {"Hello"}
&amp;gt; if (TRUE) 1 else 0
[1] "Hello"
&amp;gt; `if` &amp;lt;- .Primitive("if")
&amp;gt; if (TRUE) 1 else 0
[1] 1
&lt;/pre&gt;&lt;br /&gt;
Для чего это может понадобиться не берусь даже представить. &lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Отложенные вычисления&lt;/h3&gt;&lt;br /&gt;
R, как и Haskell, использует концепцию отложенных (lazy) вычислений. Это значит, что аргументы функций не вычисляются до тех пор пока их значения не потребуются. На самом деле, незаметно для программиста, R передает в функции специальные объекты "обещания" (promise), эти объекты содержат в себе всё, что необходимо, чтобы по требованию вычислить значение. Если значение было вычислено, то оно запоминается в объекте promise и используется в дальнейшем по мере необходимости. &lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;&gt; fun &lt;- function(x,a,b) {if (x) a else b}
&gt; fun(TRUE, 1, factorial(10000))
[1] 1
&gt; fun(FALSE, 1, factorial(10000))
[1] Inf
Предупреждение
In factorial(10000) : значение в 'gammafn' -- за пределами
&lt;/pre&gt;&lt;br /&gt;
Как видно из примера, в первом вызове функции fun значение факториала даже не вычислялось.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Окружение&lt;/h3&gt;&lt;br /&gt;
Окружение (environment) это часть механизма замыканий (closures), реализованного в R. Любая переменная доступная в окружении на момент вызова функции сохраняется и передается вместе с окружением в вызываемую функцию. Но самое неожиданное, это то, что с помощью функции assign можно определить некую переменную не только в текущем окружении, но и в родительском, например так:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;&gt; foo &lt;- function(){assign("a", 123, envir=parent.frame())}
&gt; bar &lt;- function(){foo(); message(a)}
&gt; bar()
123
&lt;/pre&gt;&lt;br /&gt;
Зачем это может быть нужно, мне сложно представить. В любом случае, полагаю, что использовать эту возможность следует с большой осторожностью. В целом, язык программирования R оказался намного больше, разнообразней и сложнее чем казалось в начале. Здесь я перечислил далеко не все его возможности. Более полное описание языка можно найти в его &lt;a href="http://cran.r-project.org/doc/manuals/R-lang.html"&gt;спецификации&lt;/a&gt;.&lt;br /&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3994427606477253566-2895952867805233927?l=www.algorithmist.ru' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/algorithmist/ru/~4/NLCh8FKFCFw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.algorithmist.ru/feeds/2895952867805233927/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.algorithmist.ru/2011/09/crazy-r.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/2895952867805233927?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/2895952867805233927?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/algorithmist/ru/~3/NLCh8FKFCFw/crazy-r.html" title="Этот безумный R" /><author><name>SergE</name><uri>http://www.blogger.com/profile/01485674727390704780</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://4.bp.blogspot.com/_rst1xzlxZSU/S_t-TztXAKI/AAAAAAAABRs/LZpr_ILRYOU/S220/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-hcyHjfmIfYw/TngAgEdTcQI/AAAAAAAABs4/BXKzRwgItXM/s72-c/Rlogo.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.algorithmist.ru/2011/09/crazy-r.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0QDRnY5eyp7ImA9WhdVE0Q.&quot;"><id>tag:blogger.com,1999:blog-3994427606477253566.post-7804951651922483005</id><published>2011-09-19T06:26:00.000+04:00</published><updated>2011-09-19T06:29:37.823+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-19T06:29:37.823+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="rusquant" /><category scheme="http://www.blogger.com/atom/ns#" term="R" /><title>Тиковые данные rusquant v0.3</title><content type="html">Очередное обновление &lt;a href="http://www.algorithmist.ru/p/rusquant.html"&gt;rusquant&lt;/a&gt;. Теперь стали доступны тиковые данные. &lt;br /&gt;
&lt;br /&gt;
Как обычно, обновиться, тем у кого уже есть, или установить тем, у кого еще нет, можно командой &lt;a href="http://www.algorithmist.ru/2011/03/r-language-for-traders.html"&gt;в консоли R&lt;/a&gt;:&lt;br /&gt;
&lt;pre class="java" name="code"&gt;install.packages("rusquant", repos="http://R-Forge.R-project.org")
&lt;/pre&gt;&lt;br /&gt;
Тиковые данные, к сожалению, доступны лишь за два последних дня. Это ограничение Finam, вообще там есть и другие ограничения, так что не удивлюсь, если через некоторое время этот код перестанет работать, пишите, если что. &lt;br /&gt;
&lt;br /&gt;
Для получения тиковых данных можно воспользоваться знакомой функцией getSymbols()&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;&gt; library(rusquant)
&gt;  getSymbols("SBER", from=Sys.Date()-1, src="Finam", period="tick")
[1] "SBER"
&gt;  SBER[1:5]
                    SBER.Close SBER.Volume
2011-09-14 09:59:59      79.24          30
2011-09-14 09:59:59      79.24          30
2011-09-14 09:59:59      79.24          10
2011-09-14 09:59:59      79.24          20
2011-09-14 09:59:59      79.24         500
&lt;/pre&gt;&lt;br /&gt;
Сразу отмечу, что вызов выполняется достаточно долго, т.к. объем получаемых данных очень большой. Для удобства, сохранена система именования OHLC данных. Т.е. цены можно получить всё той же функций Cl, а объем Vo. &lt;br /&gt;
&lt;br /&gt;
Если же есть необходимость преобразовать тиковые данные в OHLC, то нет ничего проще, в модуле xts для этого есть ряд удобных функций типа to.minutes, to.hourly, to.minutes10 и более общая функция to.period&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;&gt; to.period(SBER, "mins", k=5)
                    SBER.Open SBER.High SBER.Low SBER.Close SBER.Volume
2011-09-14 09:59:59     79.24     79.24    79.24      79.24      287520
2011-09-14 10:04:58     79.47     79.68    79.11      79.52     4042460
2011-09-14 10:09:59     79.52     79.66    79.34      79.35     3095280
&lt;/pre&gt;&lt;br /&gt;
Доступные значения периодов: “secs”, “seconds”, “mins”, “minutes”, “hours”, “days”, “weeks”, “months”, “quarters”, и “years”. Хотя, я сомневаюсь, что последними кто-то будет пользоваться. k=5 - показывает какое количество периодов объединять в группу. В данном примере мы получили пятиминутные данные. &lt;br /&gt;
&lt;br /&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3994427606477253566-7804951651922483005?l=www.algorithmist.ru' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/algorithmist/ru/~4/kc0foGzquOA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.algorithmist.ru/feeds/7804951651922483005/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.algorithmist.ru/2011/09/rusquant-v03.html#comment-form" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/7804951651922483005?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/7804951651922483005?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/algorithmist/ru/~3/kc0foGzquOA/rusquant-v03.html" title="Тиковые данные rusquant v0.3" /><author><name>SergE</name><uri>http://www.blogger.com/profile/01485674727390704780</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://4.bp.blogspot.com/_rst1xzlxZSU/S_t-TztXAKI/AAAAAAAABRs/LZpr_ILRYOU/S220/me.jpg" /></author><thr:total>4</thr:total><feedburner:origLink>http://www.algorithmist.ru/2011/09/rusquant-v03.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEUBSH89eSp7ImA9WhdWGEU.&quot;"><id>tag:blogger.com,1999:blog-3994427606477253566.post-271908684258121448</id><published>2011-09-13T06:50:00.000+04:00</published><updated>2011-09-13T06:50:59.161+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-13T06:50:59.161+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="R" /><title>Геомагнитные бури на фондовом рынке</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-kaTEb7OzbLs/Tm7Ej2QsSXI/AAAAAAAABsw/A_8EItxSmeI/s1600/maja_7.jpg" imageanchor="1" style="clear:left; float:left;margin-right:1em; margin-bottom:1em"&gt;&lt;img border="0" height="240" width="320" src="http://4.bp.blogspot.com/-kaTEb7OzbLs/Tm7Ej2QsSXI/AAAAAAAABsw/A_8EItxSmeI/s320/maja_7.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Сегодняшняя тема немного несерьезная. А может быть наоборот очень серьезная. Так сложилось, что во времена моей прыщавой юности, когда рухнул совок и было не во что больше верить, люди начали слушать всяких Чумаков, Кашпировских и прочих телепузиков.  Кажется именно тогда особой популярностью пользовались геомагнитные бури и бабушки у подъезда с радостью списывали очередной приступ остеохондроза на повышенную геомагнитную активность. Наверное, именно такое отношение и привело в последствии к предвзятому взгляду на прогнозы геомагнитной активности (я то её влияние никогда не чувствовал). Тем не менее, явление то существует и объективно регистрируется.   &lt;br /&gt;
&lt;br /&gt;
Есть масса исследований, утверждающих, что геомагнитные бури влияют на настроение людей. Люди склонны пессимистичней оценивать происходящее вокруг них во время и после бури, нежели в спокойные дни. Теперь подумаем, если люди глядят на вещи с пессимизом, причем это люди по всей земле. Значит, это должно непременно сказываться на фондовом рынке. Логично предположить, что магнитные бури должны приводить к сокращению вложений в рискованные активы, такие как акции. А раз так, то этим можно было бы воспользоваться для построения собственной торговой стратегии.  И вот, в статье "&lt;a href="http://www.frbatlanta.org/filelegacydocs/wp0305b.pdf"&gt;Playing the Field: Geomagnetic Storms and the Stock Market&lt;/a&gt;" такое исследование уже проводилось и результаты мне показались достаточно интересными чтобы проверить их на практике.&lt;br /&gt;
&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Итак, сила магнитной бури измеряется двумя величинами Kp и Ap. Какой за ними тайный смысл я не вникал, создалось ощущение, что в любом случае они очень сильно взаимосвязаны. И не важно, какую из них взять за основу.  Данные по геомагнитным бурям аж с 1932 года доступны всем желающим на публичном ftp сервере: ftp://ftp.ngdc.noaa.gov/STP/GEOMAGNETIC_DATA/INDICES/KP_AP/  Но мы, не для того столько времени посвятили языку программирования R, чтобы качать вручную и разбирать их экселем. Напишем маленькую программку, которая эти данные соберет и представит нам в удобоваримом формате:  &lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;"getGeomagnetic" &amp;lt;-
function(year){
    require(xts)
    url &amp;lt;- paste("ftp://ftp.ngdc.noaa.gov/STP/GEOMAGNETIC_DATA/INDICES/KP_AP/", year, sep='')
    tmp &amp;lt;- tempfile()
    download.file(url, destfile=tmp, quiet=TRUE)
    fr &amp;lt;- readLines(con = tmp, warn=FALSE)
    unlink(tmp)
    date &amp;lt;- as.Date(strptime(substr(fr,1,6), "%y%m%d"))
    apIndex &amp;lt;- as.numeric(substr(fr, 56, 58))
    kpIndex &amp;lt;- as.numeric(substr(fr, 29, 31))
    result &amp;lt;- xts(as.matrix(cbind(apIndex, kpIndex)), date)
    result &amp;lt;- result[complete.cases(result)]
    return(result)
}
&lt;/pre&gt;Совместим, для начала, графики геомагнитной обстановки и индекса RTS (&lt;a href="http://www.algorithmist.ru/2011/08/quantmod-for-russian-stock-market.html"&gt;нам понадобится модуль rusquant о котором я уже писал в одной из прошлых статей&lt;/a&gt;):  &lt;br /&gt;
&lt;pre class="java" name="code"&gt;gm &amp;lt;- c(getGeomagnetic(2010),getGeomagnetic(2011))

library(rusquant)
getSymbols("RTSI", from="2010-01-01", src="Finam")
data &amp;lt;- cbind(RTSI, gm)

chartSeries(data, TA=NULL, name="RTSI")
addTA(data$kpIndex, legend="Геомагнитная обстановка")

&lt;/pre&gt;Из графиков видно, что два самых больших пика находятся аккурат перед обвалами рынка. &lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-pPBXSr5aTgI/TmbcqyhjoSI/AAAAAAAABsc/eqzk9qo89Wc/s1600/geo.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="319" src="http://3.bp.blogspot.com/-pPBXSr5aTgI/TmbcqyhjoSI/AAAAAAAABsc/eqzk9qo89Wc/s320/geo.PNG" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;Значит ли это, что обвалы можно спрогнозировать по геомагнитным бурям или нет?   Для начала повторим эксперимент, проведенный в статье, в применении к Российскому рынку. А именно, посмотрим на среднее изменение величины индекса РТС в день геомагнитной бури и в течении 6 дней после нее. Будем считать магнитной бурей такие дни, когда значение величины Ap больше 29 (именно такое значение использовалось в оригинальной работе).   &lt;br /&gt;
&lt;pre class="java" name="code"&gt;library(rusquant)
getSymbols("RTSI", from="2002-01-01", src="Finam")
gm &amp;lt;- getGeomagnetic(2002)
for(i in (2003:2011)){
	gm &amp;lt;- c(gm, getGeomagnetic(i))
}

GMS &amp;lt;- ifelse(gm$apIndex&amp;gt;29, 1, 0)
GMS &amp;lt;- apply(Lag(GMS, k=(0:6))&amp;gt;0, 1, any)
data &amp;lt;- cbind(GMS, ROC(Cl(RTSI)))
names(data) &amp;lt;- c("GMS", "RTSI")
mean(data[data$GMS==1]$RTSI, na.rm=TRUE)
mean(data[data$GMS==0]$RTSI, na.rm=TRUE)
&lt;/pre&gt;Получается, что в случае спокойной геомагнитной обстановки среднее увеличение индекса РТС составляет 0.092% в день, в случае же магнитной бури 0.01% в день, т.е. в 9 раз меньше!&lt;br /&gt;
&lt;br /&gt;
Попробуем теперь подтвердить или опровергнуть гипотезу о влиянии геомагнитных бурь математически.&lt;br /&gt;
&lt;br /&gt;
Когда-то давно я рассказывал о &lt;a href="http://www.blogger.com/%D0%9A%D1%83%D1%80%D0%B8%D1%86%D1%8B,%20%D1%8F%D0%B9%D1%86%D0%B0,%20%D0%BF%D1%80%D0%B8%D1%87%D0%B8%D0%BD%D0%BD%D1%8B%D0%B9%20%D1%82%D0%B5%D1%81%D1%82%20%D0%93%D1%80%D0%B5%D0%B9%D0%BD%D0%B4%D0%B6%D0%B5%D1%80%D0%B0%20%D0%B8%D0%BB%D0%B8%20%D0%BA%D1%82%D0%BE%20%D0%B1%D1%8B%D0%BB%20%D0%BF%D0%B5%D1%80%D0%B2%D1%8B%D0%BC?"&gt;причинном тесте Грейнджера&lt;/a&gt; и показал тогда, как его можно реализовать на Java. В этот раз мы проведем аналогичный тест, но на R и увидим, насколько это проще.&lt;br /&gt;
&lt;br /&gt;
Причинный тест Грейнджера это, по сути, авторегрессия. Т.е. линейная регрессия временного ряда на его собственных значениях в прошлом. С точки зрения математической формулы, это выглядит так:&lt;br /&gt;
Y&lt;sub&gt;i&lt;/sub&gt; = μ&lt;sub&gt;i&lt;/sub&gt; + ∑α&lt;sub&gt;k&lt;/sub&gt;Y&lt;sub&gt;i-k&lt;/sub&gt;  + ∑β&lt;sub&gt;k&lt;/sub&gt;X&lt;sub&gt;i-k&lt;/sub&gt; + ε&lt;sub&gt;i&lt;/sub&gt;&lt;br /&gt;
где, Y&lt;sub&gt;i&lt;/sub&gt; - значение исследуемой переменной Y в момент времени i, в нашем случае, это индекс РТС&lt;br /&gt;
X&lt;sub&gt;i&lt;/sub&gt; - значение переменной X, влияние которой мы хотим проверить, в момент времени i. В нашем случае это сила геомагнитной бури.&lt;br /&gt;
k - временная задержка.&amp;nbsp;Она определяет, влияние какой давности мы хотим проверить.&lt;br /&gt;
&lt;br /&gt;
Мы будем проверять влияние "на следующий день" после магнитной бури.   Реализация теста Грейнджера доступна в пакете lmtest.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;#install.packages("lmtest")
library(lmtest)

GMS &amp;lt;- ifelse(gm$apIndex&amp;gt;29, 1, 0)
data &amp;lt;- cbind(GMS, ROC(Cl(RTSI)))
names(data) &amp;lt;- c("GMS", "RTSI")
grangertest(RTSI~GMS, data=data, order=1)

&lt;/pre&gt;Получаем &lt;br /&gt;
&lt;pre&gt;Granger causality test

Model 1: RTSI ~ Lags(RTSI, 1:1) + Lags(GMS, 1:1)
Model 2: RTSI ~ Lags(RTSI, 1:1)
  Res.Df Df      F Pr(&amp;gt;F)
1   2374                 
2   2375 -1 1.3522  &lt;b&gt;0.245&lt;/b&gt;
&lt;/pre&gt;Значение p-value=0.245 не позволяет нам утверждать о наличии зависимости между индексом RTS и геомагнитными бурями. Однако, если немного увеличить порог критерия магнитной бури, то получим:  &lt;br /&gt;
&lt;pre class="java" name="code"&gt;GMS &amp;lt;- ifelse(gm$apIndex&amp;gt;33, 1, 0)
data &amp;lt;- cbind(GMS, ROC(Cl(RTSI)))
names(data) &amp;lt;- c("GMS", "RTSI")
grangertest(RTSI~GMS, data=data, order=1)
&lt;/pre&gt;результат: &lt;br /&gt;
&lt;pre&gt;Granger causality test

Model 1: RTSI ~ Lags(RTSI, 1:1) + Lags(GMS, 1:1)
Model 2: RTSI ~ Lags(RTSI, 1:1)
  Res.Df Df      F  Pr(&amp;gt;F)  
1   2374                    
2   2375 -1 2.9949 &lt;b&gt;0.08366&lt;/b&gt;
&lt;/pre&gt;&lt;br /&gt;
В данном случае, значение p-value=0.08366 меньше 0.1 и значит, что с вероятностью 90% мы не можем отвергать гипотезу о влиянии геомагнитных бурь на фондовый рынок. Выходит, что все-таки влияет. Но влияют только очень сильные магнитные бури, которых  с 2002 года было всего 137. Можно ли это как-то использовать? Мне тоже интересно. Буду ждать ближайшей бури, чтобы проверить.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3994427606477253566-271908684258121448?l=www.algorithmist.ru' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/algorithmist/ru/~4/znYMbZCCjQc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.algorithmist.ru/feeds/271908684258121448/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.algorithmist.ru/2011/09/geomagnetic-storms-and-stock-market.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/271908684258121448?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/271908684258121448?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/algorithmist/ru/~3/znYMbZCCjQc/geomagnetic-storms-and-stock-market.html" title="Геомагнитные бури на фондовом рынке" /><author><name>SergE</name><uri>http://www.blogger.com/profile/01485674727390704780</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://4.bp.blogspot.com/_rst1xzlxZSU/S_t-TztXAKI/AAAAAAAABRs/LZpr_ILRYOU/S220/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/-kaTEb7OzbLs/Tm7Ej2QsSXI/AAAAAAAABsw/A_8EItxSmeI/s72-c/maja_7.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.algorithmist.ru/2011/09/geomagnetic-storms-and-stock-market.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DU8AQn4_eyp7ImA9WhdWEko.&quot;"><id>tag:blogger.com,1999:blog-3994427606477253566.post-7232517205597867533</id><published>2011-09-06T06:57:00.000+04:00</published><updated>2011-09-06T06:57:23.043+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-06T06:57:23.043+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="rusquant" /><category scheme="http://www.blogger.com/atom/ns#" term="R" /><title>Обновление rusquant 0.2</title><content type="html">Небольшая &lt;a href="http://www.algorithmist.ru/2011/08/quantmod-for-russian-stock-market.html"&gt;библиотека для работы с Российскими фондовыми рынками в R&lt;/a&gt; пополнилась новыми возможностями. Теперь, помимо доступа к данным с Финама, прямо из &lt;a href="http://www.r-project.org/"&gt;R&lt;/a&gt; вы можете получить доступ к котировкам фьючерсов и опционов Forts, доступным на сайте rts.ru &lt;br /&gt;
&lt;br /&gt;
Итак, для тех что еще не установил себе rusquant - ставим, для тех, кто уже установил - обновляемся командой:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;install.packages("rusquant", repos="http://R-Forge.R-project.org")
&lt;/pre&gt;&lt;br /&gt;
Что новенького?&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;1.&lt;/b&gt; Стала доступной доска опционов:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;library(rusquant)
rts &lt;- getOptionChain("RTS-9.11", "2011-09-15", src="Forts", session = "MAIN")
&lt;/pre&gt;

В качестве параметров передается название базового инструмента, дата экспирации и источник данных src="Forts", кроме того, можно указать сессию session = "MAIN" - основную или session = "EVENING" - вечернюю. 

Для примера, построим улыбку волатильности. Замечу, что ожидаемая волатильность в данном примере считается биржей и точной методики расчета мне найти не удалось. 

&lt;pre class="java" name="code"&gt;plot(cbind(rts$calls[,'Strike'], rts$calls[,'IV']), type='l', main="Implied Volatility", ylab="Volatility", xlab="Strike")
&lt;/pre&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-WH8wsbbejIo/TmWJu94wQoI/AAAAAAAABsE/xjNjoHOv378/s1600/iv.PNG" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="313" width="320" src="http://3.bp.blogspot.com/-WH8wsbbejIo/TmWJu94wQoI/AAAAAAAABsE/xjNjoHOv378/s320/iv.PNG" /&gt;&lt;/a&gt;&lt;/div&gt;

Графики открытого интереса:

&lt;pre class="java" name="code"&gt;plot(cbind(rts$calls[,'Strike'], rts$calls[,'OI']), type='l', col='blue', main="Open Interest", ylab="R", xlab="Strike")
lines(cbind(rts$puts[,'Strike'], rts$puts[,'OI']), type='l', col='red')
&lt;/pre&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-oRuPkfInR8g/TmWKb-0GwLI/AAAAAAAABsM/pBCU98V9ItM/s1600/oi.PNG" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="214" width="320" src="http://1.bp.blogspot.com/-oRuPkfInR8g/TmWKb-0GwLI/AAAAAAAABsM/pBCU98V9ItM/s320/oi.PNG" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;b&gt;2.&lt;/b&gt; Стали доступны дневные исторические данные по опционам:

&lt;pre class="java" name="code"&gt;getSymbols("RTS-9.11M150911CA 185000", from="2011-03-21", src="Forts")
chartSeries(RTS911M150911CA185000)
&lt;/pre&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-FUEP9KVoORU/TmWKwp4OctI/AAAAAAAABsU/kL0fPSTngBI/s1600/opt_hist.PNG" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="299" width="320" src="http://4.bp.blogspot.com/-FUEP9KVoORU/TmWKwp4OctI/AAAAAAAABsU/kL0fPSTngBI/s320/opt_hist.PNG" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
Планы на будущее: добавить дивиденды, статистику и тиковые данные. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3994427606477253566-7232517205597867533?l=www.algorithmist.ru' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/algorithmist/ru/~4/TsD7h8IyZ34" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.algorithmist.ru/feeds/7232517205597867533/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.algorithmist.ru/2011/09/rusquant-02.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/7232517205597867533?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/7232517205597867533?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/algorithmist/ru/~3/TsD7h8IyZ34/rusquant-02.html" title="Обновление rusquant 0.2" /><author><name>SergE</name><uri>http://www.blogger.com/profile/01485674727390704780</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://4.bp.blogspot.com/_rst1xzlxZSU/S_t-TztXAKI/AAAAAAAABRs/LZpr_ILRYOU/S220/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-WH8wsbbejIo/TmWJu94wQoI/AAAAAAAABsE/xjNjoHOv378/s72-c/iv.PNG" height="72" width="72" /><thr:total>1</thr:total><feedburner:origLink>http://www.algorithmist.ru/2011/09/rusquant-02.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkQNQX09cSp7ImA9WhdXGEs.&quot;"><id>tag:blogger.com,1999:blog-3994427606477253566.post-4510691340087737548</id><published>2011-09-01T10:57:00.000+04:00</published><updated>2011-09-01T10:59:50.369+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-01T10:59:50.369+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Time series" /><category scheme="http://www.blogger.com/atom/ns#" term="R" /><title>И снова о коинтеграции временных рядов</title><content type="html">В &lt;a href="http://www.algorithmist.ru/2011/08/time-series-similarity-measures.html"&gt;недавнем посте про сравнение временных рядов&lt;/a&gt; я начал рассказ о таком явлении, как коинтеграция. Настал день продолжить этот рассказ. Прежде всего, стоит все-таки определить, что такое коинтеграция. Существует точное математическое определение коинтеграции:&lt;br /&gt;
Если некоторая линейная комбинация двух временных рядов имеет порядок интегрирования меньший чем порядок интегрирования каждого из рядов, то говорят, что временные ряды коинтегрированы. &lt;br /&gt;
&lt;br /&gt;
На мой взгляд, определение в таком виде тяжеловато для осмысления, а кроме того, оно является слишком общим для большинства практических задач. Разберем определение по частям, но для начала, немного теории.&lt;br /&gt;
&lt;br /&gt;
Что такое стационарный временной ряд? Если совсем просто, то это временной ряд свойства которого не меняются во времени. Т.е. если мы возьмем некий его отрезок, посчитаем стандартные статистические характеристики, такие как математическое ожидание или дисперсию, мы должны получить одинаковые величины, в пределах погрешности, естественно.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим пример на &lt;a href="http://www.algorithmist.ru/2011/03/r-language-for-traders.html"&gt;языке программирования R&lt;/a&gt;:&lt;br /&gt;
&lt;pre class="java" name="code"&gt;data &lt;- rnorm(50)
plot(data, type='l')
&lt;/pre&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-YxtvxBQGCMM/Tl2gkc_0tyI/AAAAAAAABrg/hXc5qVaKiXk/s1600/stationary.PNG" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="97" width="320" src="http://1.bp.blogspot.com/-YxtvxBQGCMM/Tl2gkc_0tyI/AAAAAAAABrg/hXc5qVaKiXk/s320/stationary.PNG" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;a name='more'&gt;&lt;/a&gt;

Стационарный временной ряд имеет порядок интегрирования 0. Обратное не верно, но нам не очень то и нужно. Возьмем теперь стационарный ряд X&lt;sub&gt;1&lt;/sub&gt;, X&lt;sub&gt;2&lt;/sub&gt;... X&lt;sub&gt;n&lt;/sub&gt;, и преобразуем его в следующий ряд: Y&lt;sub&gt;1&lt;/sub&gt; = X&lt;sub&gt;1&lt;/sub&gt;, Y&lt;sub&gt;2&lt;/sub&gt; = Y&lt;sub&gt;1&lt;/sub&gt; + X&lt;sub&gt;2&lt;/sub&gt;, ... Y&lt;sub&gt;n&lt;/sub&gt; = Y&lt;sub&gt;n-1&lt;/sub&gt; + X&lt;sub&gt;n&lt;/sub&gt;  получившийся ряд имеет порядок интегрирования 1. Если вернуться к определению, то временной ряд имеет порядок интегрирования 1 если ряд его приращений имеет порядок интегрирования 0. Аналогично можно определить более высокие порядки интегрирования. 

Рассмотрим пример ряда, интегрированого с порядком 1:
&lt;pre class="java" name="code"&gt;data &lt;- cumsum(rnorm(100))
plot(data, type='l')
&lt;/pre&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-7Lkf6e1snUs/Tl3RAUCzHwI/AAAAAAAABro/36IwRNkkkYw/s1600/rw.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="123" width="320" src="http://4.bp.blogspot.com/-7Lkf6e1snUs/Tl3RAUCzHwI/AAAAAAAABro/36IwRNkkkYw/s320/rw.png" /&gt;&lt;/a&gt;&lt;/div&gt;
Классическое случайное блуждание как раз является примером такого ряда. В некотором приближении, цены на акции тоже можно считать примером временного ряда с порядком интегрирования 1. 

Теперь мы можем переформулировать определение коинтеграции в применении для цен. Два временных ряда цен коинтегрированы если мы можем найти такую их линейную комбинацию, которая является стационарным временным рядом. Классическим примером коинтеграции является соотношение цен на золото GLD и индекс акций золотодобывающих компаний GDX. Оба доступны на yahoo.finance.com для скачивания, но мы воспользуемся R чтобы упростить себе жизнь

&lt;pre class="java" name="code"&gt;library(tseries)
library(quantmod)

#сначала скачаем данные
from &lt;- "2006-05-23"
to &lt;- "2008-07-23"
getSymbols("GLD", from=from, to=to)
getSymbols("GDX", from=from, to=to)

#Очень важный момент в исследованиях на коинтеграцию, выплаты дивидендов, ребалансировки индекса все должно быть учетно. В случае GLD все просто, золото оно и есть золото. В случае GDX следует взять adjusted данные.
data &lt;- cbind(Cl(GLD), GDX$GDX.Adjusted)
data &lt;- data[complete.cases(data)]

names(data) &lt;- c("GLD", "GDX")

#проводим линейную регрессию для определения правильного соотношения
model &lt;- lm(GLD~GDX+0, data)

#вычисляем разницу цен (спред)
spread &lt;- data$GLD - coef(model)[1]*data$GDX

#проводим тест Дики-Фуллера на стационарность
adf.test(as.vector(spread), k=0)

&lt;/pre&gt;
&lt;a href="http://ru.wikipedia.org/wiki/%D0%A2%D0%B5%D1%81%D1%82_%D0%94%D0%B8%D0%BA%D0%B8-%D0%A4%D1%83%D0%BB%D0%BB%D0%B5%D1%80%D0%B0"&gt;Тест Дики-Фуллера&lt;/a&gt; это далеко не единственный тест на стационарность, но в данном примере мы используем именного его.

Результат:
&lt;pre&gt;Augmented Dickey-Fuller Test

data:  as.vector(spread) 
Dickey-Fuller = -3.8556, Lag order = 0, p-value = 0.01622
alternative hypothesis: stationary 
&lt;/pre&gt;
Видим, что. p-value мал, и отсюда можем утверждать, что действительно GLD и GDX коинтегрированы. Что подтверждается визуально на графике:

&lt;pre class="java" name="code"&gt;plot(as.vector(Cl(GLD)), col='blue', type='l', main='Коинтеграция GLD и GDX', ylab='price')
lines(as.vector(GDX$GDX.Adjusted*coef(model)[1]), col='red', type='l')
&lt;/pre&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-odup1sGYM2s/Tl73XMuTjpI/AAAAAAAABrw/VTs5gejsk98/s1600/gld_gdx.PNG" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="319" width="320" src="http://4.bp.blogspot.com/-odup1sGYM2s/Tl73XMuTjpI/AAAAAAAABrw/VTs5gejsk98/s320/gld_gdx.PNG" /&gt;&lt;/a&gt;&lt;/div&gt;
Красиво? Безусловно, если не знать о том, что в 2008, когда цены на нефть взлетели, на мир обрушился финансовый кризис, этот спред развалился и более не является стационарным. Изменим в нашей программе значение переменной to на "2011-09-01" и получим:
&lt;pre&gt;Augmented Dickey-Fuller Test

data:  as.vector(spread) 
Dickey-Fuller = -2.9022, Lag order = 0, p-value = 0.1964
alternative hypothesis: stationary 
&lt;/pre&gt;
Большое значение p-value уже не позволяет нам говорить о стационарности спреда, а значит и о коинтеграции двух цен, и график только подтверждает это:

&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-UTAsCXbmbug/Tl745LqygdI/AAAAAAAABr4/7rtKmgBSFFc/s1600/gld_gdx_1.PNG" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="319" width="320" src="http://3.bp.blogspot.com/-UTAsCXbmbug/Tl745LqygdI/AAAAAAAABr4/7rtKmgBSFFc/s320/gld_gdx_1.PNG" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Тем не менее, никто не отменял возможности восстановления стационарности спреда по мере стабилизации на рынке в будущем. И никто не отменял более сложных комбинаций инструментов, например золото, золотодобывающие компании и нефть. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3994427606477253566-4510691340087737548?l=www.algorithmist.ru' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/algorithmist/ru/~4/sliN4pmQUiI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.algorithmist.ru/feeds/4510691340087737548/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.algorithmist.ru/2011/09/time-series-test-for-cointegration.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/4510691340087737548?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/4510691340087737548?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/algorithmist/ru/~3/sliN4pmQUiI/time-series-test-for-cointegration.html" title="И снова о коинтеграции временных рядов" /><author><name>SergE</name><uri>http://www.blogger.com/profile/01485674727390704780</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://4.bp.blogspot.com/_rst1xzlxZSU/S_t-TztXAKI/AAAAAAAABRs/LZpr_ILRYOU/S220/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-YxtvxBQGCMM/Tl2gkc_0tyI/AAAAAAAABrg/hXc5qVaKiXk/s72-c/stationary.PNG" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.algorithmist.ru/2011/09/time-series-test-for-cointegration.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkcARHgzcCp7ImA9WhdXFUU.&quot;"><id>tag:blogger.com,1999:blog-3994427606477253566.post-3429216863787799126</id><published>2011-08-29T06:13:00.001+04:00</published><updated>2011-08-29T06:14:05.688+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-08-29T06:14:05.688+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Time series" /><category scheme="http://www.blogger.com/atom/ns#" term="R" /><title>Сравнение временных рядов</title><content type="html">Огромное количество данных в data mining вообще и в финансах в частности приходит к нам в виде временных рядов. Это не удивительно, ведь очень часто нас интересуют какие-то события или показатели изменяющиеся во времени. При этом, огромный пласт классической математики веками создавался для работы с множествами чисел. В результате, одним из самых популярных подходов к работе с такого рода данными был отказ от оси  времени как полноценной оси координат и переход к простым множествам данных. Действительно, все алгоритмы, что я до сих пор рассматривал в серии &lt;a href="http://www.algorithmist.ru/search/label/Data%20mining"&gt;data mining с примерами на R&lt;/a&gt; работают с наборами данных и игнорируют время. &lt;br /&gt;
&lt;br /&gt;
Даже когда мы говорим о такой простой и знакомой вещи как корреляция, следует помнить, что она тоже определяется без учета времени. Меж тем, корреляцию цен очень часто используют как аргумент в пользу совершения сделки в трейдинге. &lt;br /&gt;
&lt;br /&gt;
С математической точки зрения, фраза "корреляция цен" не имеет особого смысла. Честно говоря, я  вообще не понимаю что это такое может быть. Т.е. чисто формально мы можем посчитать эту корреляцию для определенного набора данных, которые являются ценами. Но полученное значение не будет сходиться ни к какой определенной величине с увеличением размера набора данных - свойство, которое ожидается от любой измеряемой величины.  &lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
Поэтому, обычно, когда говорят о корреляции цен, имеют в виду корреляцию изменений цен за какой-то промежуток времени, например за день. Т.е. в каждой точке мы рассматриваем не само значение временного ряда, а его приращение. Тем самым мы преобразуем нестационарный временной ряд в стационарный (на самом деле, существует термин &lt;a href="http://en.wikipedia.org/wiki/Order_of_integration"&gt;order of integration&lt;/a&gt;, и, строго говоря стационарность нам не требуется). Но значит ли высокая корреляция в таком случае, что цены будут двигаться вместе? Это значит лишь то, что большую часть временных интервалов цены будут двигаться в одном направлении. Но при этом они вполне могут расходиться дальше и дальше друг от друга.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим пример:&lt;br /&gt;
&lt;br /&gt;
По бесконечно широкой улице идет два абсолютно пьяных человека. Каждый шаг пьяного человека случаен. Он может шагнуть как вправо, так и влево. Корреляция шагов алкоголиков нулевая и, в таком случае, никому не надо доказывать, что они вскоре разойдутся очень далеко.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;mean &lt;- 0
std &lt;- 0.01 

res1 &lt;- cumsum(rnorm(1000, mean=mean, sd=std))     
res2 &lt;- cumsum(rnorm(1000, mean=mean, sd=std))     

plot(res1, type="l", col="red", ylim=range(res1, res2), main="Два случайных блуждания", ylab='Y')      
lines(res2, col="blue") 
&lt;/pre&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-Chvq4eIp4SY/Tlkw5PZjYsI/AAAAAAAABrI/Gnqo1oXRM_E/s1600/rw.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="320" width="320" src="http://3.bp.blogspot.com/-Chvq4eIp4SY/Tlkw5PZjYsI/AAAAAAAABrI/Gnqo1oXRM_E/s320/rw.png" /&gt;&lt;/a&gt;&lt;/div&gt;Усложним наш пример. На каждой стороне улицы стоят вино-водочные магазины и заманивают наших пьянчуг к себе. На каждый шаг, пьянчуги слышат рекламу одного из магазинов и с некоторой вероятностью шагают в его сторону. Следующий шаг они могут услышать рекламу другого магазина и пойти в другую сторону. Теперь их движения, хоть все еще и являются случайными, но уже сильно коррелируют друг с другом. 

&lt;pre class="java" name="code"&gt;library(MASS)   

mean &lt;- 0
std &lt;- 0.01 
cor &lt;- 0.8  

covmat &lt;- (std^2) * matrix(c(1, cor, cor, 1), nrow=2)
res &lt;- mvrnorm(1000, c(mean, mean), covmat)  

res1 &lt;- cumsum(res[,1])
res2 &lt;- cumsum(res[,2])   

plot(res1, type="l", col="red", ylim=range(res1, res2), main="Два случайных блуждания с корреляцией", ylab='Y')      
lines(res2, col="blue")  
&lt;/pre&gt;
Если запустить этот код несколько раз, то становится очевидно, что несмотря на то, что их отдельные шаги очень сильно коррелируют, и иногда действительно кажется, что они идут вместе. Их интегральная сумма шагов может со временем расходиться.

&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-aSOlaqw0wwc/TlkyCUucwLI/AAAAAAAABrQ/W5Ic8dSFiV8/s1600/cor.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="320" width="320" src="http://1.bp.blogspot.com/-aSOlaqw0wwc/TlkyCUucwLI/AAAAAAAABrQ/W5Ic8dSFiV8/s320/cor.png" /&gt;&lt;/a&gt;&lt;/div&gt;Какая здесь аналогия с финансами? Непосредственная, предположим что мы нашли две акции, изменения цен которых сильно коррелируют. Значит ли это, что если вдруг, в какой-то момент времени одна стала стоить существенно дешевле чем другая, то мы можем купить дешевую, продать дорогую и ждать когда цена сойдется? Нет, не значит. Цена может не сойтись никогда. 

Вернемся к нашим алкоголикам, предположим теперь, что они знают друг друга и на каждый шаг кричат: эй, дружище, ты где? Услышав такой крик, они оценивают направление и расстояние друга до друга и стараются шагнуть в нужном направлении. 

&lt;pre  class="java" name="code"&gt;library(xts)
mean &lt;- 0
std &lt;- 0.01
coint &lt;- 0.02

x &lt;- 0 
y &lt;- 0
for (i in 1:1000) {
  px &lt;- last(x)
  py &lt;- last(y)  
  x &lt;- c(x,  px + (coint * (py - px)) + rnorm(1, mean=mean, sd=std))  
  y &lt;- c(y,  py + (coint * (px - py)) + rnorm(1, mean=mean, sd=std))  
}  

ylim &lt;- range(x, y)  
plot(x, ylim=ylim, type="l", col="red", main="Два коинтегрированных случайных блуждания")  
lines(y, col="blue")  
 
&lt;/pre&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-nNML8mQ4y2Q/TlkyucPJdAI/AAAAAAAABrY/7jy9Xibc5bo/s1600/coint.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="320" width="320" src="http://2.bp.blogspot.com/-nNML8mQ4y2Q/TlkyucPJdAI/AAAAAAAABrY/7jy9Xibc5bo/s320/coint.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Теперь, несмотря на то, что кривая каждого отдельного алконавта все еще очень похожа на случайное блуждание, они никогда не расходятся "далеко". И всякий раз, после разбредания в разные стороны, они снова стремятся друг другу. О таких временных последовательностях говорят, что они коинтегрированы. Коинтеграция является гораздо более важным понятием в теории временных рядов, чем корреляция.&lt;br /&gt;
&lt;br /&gt;
Но вернемся к финансам, если нам удалось найти две ценных бумаги, которые коинтегрированы, и в какой-то момент их цены разошлись, то мы можем купить более дешевую и продать более дорогую и получить прибыль когда их цены снова сойдутся. Вопрос лишь в том, как узнать, что две последовательности коинтегрированы? Для этого существует несколько различных тестов, о которых мы поговорим в следующий раз. &lt;br /&gt;
&lt;br /&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3994427606477253566-3429216863787799126?l=www.algorithmist.ru' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/algorithmist/ru/~4/XqPEII_hhXQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.algorithmist.ru/feeds/3429216863787799126/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.algorithmist.ru/2011/08/time-series-similarity-measures.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/3429216863787799126?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/3429216863787799126?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/algorithmist/ru/~3/XqPEII_hhXQ/time-series-similarity-measures.html" title="Сравнение временных рядов" /><author><name>SergE</name><uri>http://www.blogger.com/profile/01485674727390704780</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://4.bp.blogspot.com/_rst1xzlxZSU/S_t-TztXAKI/AAAAAAAABRs/LZpr_ILRYOU/S220/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-Chvq4eIp4SY/Tlkw5PZjYsI/AAAAAAAABrI/Gnqo1oXRM_E/s72-c/rw.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.algorithmist.ru/2011/08/time-series-similarity-measures.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkcNSX48eip7ImA9WhdXEkk.&quot;"><id>tag:blogger.com,1999:blog-3994427606477253566.post-8076428233065245335</id><published>2011-08-25T06:41:00.000+04:00</published><updated>2011-08-25T06:41:38.072+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-08-25T06:41:38.072+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Инвестирование" /><category scheme="http://www.blogger.com/atom/ns#" term="R" /><title>Куплю будущее, недорого</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-i_5g7WVk9aE/TlSc5sLZVrI/AAAAAAAABq4/ys27zPa5Nok/s1600/TimeIsMoney.jpg" imageanchor="1" style="clear:left; float:left;margin-right:1em; margin-bottom:1em"&gt;&lt;img border="0" height="320" width="257" src="http://2.bp.blogspot.com/-i_5g7WVk9aE/TlSc5sLZVrI/AAAAAAAABq4/ys27zPa5Nok/s320/TimeIsMoney.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Лирическое отступление в финансы. Сегодняшний пост не содержит сложных алгоритмов. Тем не менее, он может показаться интересным новичкам в финансах и будет сопровождаться традиционным примером на R.&lt;br /&gt;
&lt;br /&gt;
Мы все так или иначе, хотим мы того или нет сталкиваемся с финансами. Очень часто по телевизору или радио приходится слышать термины вроде репо, фьючерс или спот. При этом, для большинства из нас они так и остаются загадкой. В лучшем случае, мы имеем какое-то представление о том, что такаое акция. Что это, своего рода, доля в компании, которая может дать мне право голоса на собрании акционеров и может принести дивиденды. &lt;br /&gt;
&lt;br /&gt;
В принципе, такое интуитивное представление об акциях не так уж далеко от реальности. Конечно, мало кто задумывается над тем, что цена акции падает на величину дивидендов сразу после закрытия реестра, когда эти самые дивиденды объявляются. Что акции бывают простые и привилегированные. Что акции можно не только сначала купить, а потом продать, но и наоборот, и это называется короткая позиция. И много чего еще, но сейчас это не важно. &lt;br /&gt;
&lt;br /&gt;
А важны нам сейчас фьючерсы. О них и поговорим. &lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Фьючерс, от английского future-будущее, уже своим названием как-бы намекает, что это некий контракт касательно будущего. А именно, фьючерс это право и обязательство купить или продать некий базовый актив по заранее определенной цене в фиксированный момент времени в будущем. Фьючерс может быть выпущен на акции, индексы, нефть, валютные пары и много чего еще. Сегодня мы будем рассматривать только фьючерсы на акции.&lt;br /&gt;
&lt;br /&gt;
Например, я хочу купить акции сбербанка на новый год, но я не знаю, сколько они будут стоить к новому году поэтому я хочу договориться о цене сейчас. Иду на биржу и говорю, за сколько вы мне продадите акции сбербанка на новый год? Именно такую возможность мне  предоставляет фьючерс РТС на акции сбербанка. Когда-то, для меня стало неожиданным открытием, что согласно современной теории, цена на сбербанковский фьючерс не зависит от перспектив сбербанка. Цена на фьючерс зависит лишь от текущей стоимости акций и процентной ставки. Это объясняется довольно простой теорией арбитража. &lt;br /&gt;
&lt;br /&gt;
Суть такая: предположим, что мы можем брать неограниченное количество денег в долг, а также отдавать деньги на временное хранение по одной и той же процентной ставке 10% годовых. Предположим, что акция сбербанка стоит 100 рублей. Сколько должен стоит фьючерс на акцию сбербанка с датой окончания (экспирации) ровно через год?&lt;br /&gt;
&lt;br /&gt;
Предположим, что есть человек, желающий купить фьючерс за 115 рублей, тогда мы можем взять в долг 100 рублей, купить на них акцию сбербанка и, одновременно, продать желающему фьючерс за 115. Через год мы отдадим акцию сбербанка покупателю фьючерса, получим за нее 115 рублей, отдадим 100+10% = 110 рублей в банк и останемся с чистой и гарантированной прибылью 5 рублей. Заметьте, нам абсолютно не важно сколько будет стоить акция сбербанка через год. И для получения прибыли нам не понадобилось ни копейки собственных денег.   &lt;br /&gt;
&lt;br /&gt;
Предположим теперь, что есть человек, желающий продать фьючерс за 105 рублей и у нас есть акция сбербанка. Тогда мы можем продать эту акцию, положить деньги в банк и одновременно купить фьючерс. Через год мы заберем из банка 100+10% = 110 рублей и выполним обязательства по фьючерсу купив обратно акцию сбербанка за 105 рублей. Снова в итоге 5 рублей гарантированной прибыли. И заметьте, нам опять абсолютно не важно сколько будет стоит акция сбербанка через год.&lt;br /&gt;
&lt;br /&gt;
На рынке много игроков как первого типа так и второго. Они создают противоположные силы. И эти  силы толкают цену фьючерса к некоторому значению, в нашем случае 110 рублей, что равно цене базового актива + процентная ставка. Конечно, формула на самом деле несколько сложнее, ведь проценты могут капитализироваться, акция может приносить дивиденды а брокер может брать комиссию за проведение операции. Все это можно было бы учесть, но для дальнейшего рассмотрения, оно не очень интересно. &lt;br /&gt;
&lt;br /&gt;
Ключевой момент, который тут надо запомнить, это то, что цена на фьючерс определяется лишь стоимостью акции сейчас и процентной ставкой. Что будет если, например, все знают, что компания через год разорится? Будет ли это влиять на стоимость её фьючерсов? Ответ: будет, но через цену на акции. Если все знают, что компания через год разорится, её акции упадут до нуля и вместе с ними упадут фьючерсы. &lt;br /&gt;
&lt;br /&gt;
Перейдем от теории к практике. Я тут несколько раз упомянул некую гипотетическую процентную ставку. Исходя из вывода формулы можно предположить, что это процентная ставка, по которой крупные игроки, банки, могут занимать и давать в долг деньги. Попробуем, для начала, подтвердить теорию а потом и оценить чему равна эта процентная ставка на практике. Для запуска примера понадобится &lt;a href="http://www.algorithmist.ru/2011/08/quantmod-for-russian-stock-market.html"&gt;расширение к модулю quantmod для работы на Российском рынке&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;library(rusquant)
from &amp;lt;- "2010-09-01"
#июньский фьючерс на акции сбербанка
future &amp;lt;- getSymbols("SRM1", src="Finam", period="hour", from=from, auto.assign=FALSE)
stock &amp;lt;- getSymbols("SBER", src="Finam", period="hour", from=from, auto.assign=FALSE)
#стоимость фьючерса равна стоимости 100 акций
data &amp;lt;- cbind(100*Cl(stock), Cl(future))
data &amp;lt;- data[complete.cases(data)]
names(data) &amp;lt;- c("Stock", "Future")
chartSeries(future)
addTA(data$Future-data$Stock, legend="Spread", col='blue', yrange=c(-50, 300))
&lt;/pre&gt;На картинке, в верхней части цена на фьючерс, в средней объемы торгов, а в нижней части разница цен фьючерса и акций (спред). &lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-UGi6XbulEOg/TlSbllmtg5I/AAAAAAAABqw/NooyD086gnY/s1600/F_spread_sber.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="320" width="262" src="http://2.bp.blogspot.com/-UGi6XbulEOg/TlSbllmtg5I/AAAAAAAABqw/NooyD086gnY/s320/F_spread_sber.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
Обратимся к нижнему графику, он явно разделен на три части. Первая часть - неактивные торги, малые объемы, хаотичное состояние фьючерса. Третья часть графика, напротив показывает высокие объемы торгов и линейное сокращение разности между ценой фьючерса и акции (в теории конечно тут должна быть экспонента, но у нас нет торгуемых фьючерсов с большим временем до экспирации чтобы проверить это). А посередине, между ними, странный провал который неожиданно заканчивается 15-го апреля. Это дата закрытия реестра, когда происходит выплата дивидендов. &lt;br /&gt;
&lt;br /&gt;
Попробуем найти угол наклона прямой в правой части графика с помощью уже известной нам &lt;a href="http://www.algorithmist.ru/2011/04/linear-regression-with-examples-in-r.html"&gt;линейной регрессии&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;#отбираем линейную часть данных
data &amp;lt;- data['2011-05-03/']
#вычисляем спред
spread &amp;lt;- data$Future-data$Stock
#дней до экспирации
exptime &amp;lt;- as.double(difftime(last(index(spread)), index(spread), units="days"))

model &amp;lt;- cbind(exptime, spread)
names(model) &amp;lt;- c("ExpTime", "Spread")
lm(Spread~ExpTime-1, model)
&lt;/pre&gt;&lt;br /&gt;
Результат:&lt;br /&gt;
ExpTime  &lt;br /&gt;
1.468 &lt;br /&gt;
&lt;br /&gt;
Т.е. за каждый день держания сбербанковского фьючерса вы неявно платите 1 рубль, 47 копеек. Что без рекапитализации, в годовом эквиваленте дает приблизительно 5.5%. Это и есть та самая процентная ставка. &lt;br /&gt;
&lt;br /&gt;
На последок замечу, что если бы мы рассматривали фьючерс на нефть то значение спреда получилось бы с противоположным знаком. Т.к. в отличии от акций, для хранения нефти требуются деньги. Но нефть это совсем другая история...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3994427606477253566-8076428233065245335?l=www.algorithmist.ru' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/algorithmist/ru/~4/nOieDfN-zgU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.algorithmist.ru/feeds/8076428233065245335/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.algorithmist.ru/2011/08/blog-post.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/8076428233065245335?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/8076428233065245335?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/algorithmist/ru/~3/nOieDfN-zgU/blog-post.html" title="Куплю будущее, недорого" /><author><name>SergE</name><uri>http://www.blogger.com/profile/01485674727390704780</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://4.bp.blogspot.com/_rst1xzlxZSU/S_t-TztXAKI/AAAAAAAABRs/LZpr_ILRYOU/S220/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-i_5g7WVk9aE/TlSc5sLZVrI/AAAAAAAABq4/ys27zPa5Nok/s72-c/TimeIsMoney.jpg" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.algorithmist.ru/2011/08/blog-post.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0UFQno8eyp7ImA9WhdQFk4.&quot;"><id>tag:blogger.com,1999:blog-3994427606477253566.post-7375413832985854945</id><published>2011-08-18T06:40:00.000+04:00</published><updated>2011-08-18T06:40:13.473+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-08-18T06:40:13.473+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Data mining" /><category scheme="http://www.blogger.com/atom/ns#" term="R" /><title>Feature selection с примерами на R</title><content type="html">В &lt;a href="http://www.algorithmist.ru/search/label/Data%20mining"&gt;серии постов о data mining&lt;/a&gt; я уже описал несколько различных методов и алгоритмов. Они все разные, но у них есть одна неизбежная общая черта: на вход подается некоторый набор "входных параметров" (в англоязычной литературе используется слово feature) и уже дальше начинаются все чудеса и преобразования. В случае с финансовыми данными, этими параметрами могут быть цены, объемы торгов, день недели, технические индикаторы и много чего еще. Но откуда берутся эти параметры и почему они именно такие? До сих пор, этот вопрос я не рассматривал. &lt;br /&gt;
&lt;br /&gt;
Часто в различных обсуждениях проскакивает точка зрения, что в модель нужно напихать как можно больше различных параметров, а дальше мощные алгоритмы data mining сами разберутся, что им нужно, а что нет. На самом деле, это конечно чушь. Неограниченное увеличение количества входных параметров не только снижает общее понимание модели, но и приводит к оверифитингу. Кроме того, скорость работы некоторых моделей экспоненциально убывает с ростом количества параметров. Поэтому, слишком много параметров, это плохо. Слишком мало параметров, очевидно, тоже не хорошо. Ведь чем меньше параметров, тем меньше информации о системе мы имеем. Получается, что необходимо каким-то образом определять, когда параметров слишком много, а когда мало. Но даже этого не достаточно. Необходимо еще каким-то образом выбирать из имеющегося множества параметров те, которые наиболее нам важны. Те, которые несут в себе больше всего уникальной информации. Как это сделать? Об этом и поговорим.&lt;br /&gt;
&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Проблема отбора параметров решается в большой области знаний, под названием Dimensionality Reduction (уменьшение размерности). В свою очередь, эта область знаний делится на две части Feature Selection (выбор параметров) и Feature Extraction (преобразование параметров). Сегодня мы поговорим о Feature Selection. &lt;br /&gt;
&lt;br /&gt;
Что же из себя представляет Feature Selection? Предположим, что у нас есть некие данные, скажем цены на все акции на бирже и мы хотим по ним спрогнозировать движение рынка в целом (ничего себе задачка...). Что с этим делать? Понятно, что у нас может быть сотни тысяч акций и никакой метод опорных векторов или нейронная сеть не переварит это, а если даже и переварит, то неизбежно произойдет оверфитинг. Стало быть, нам надо часть данных отбросить. Какие именно? Можно, конечно, воспользоваться знаниями предметной области и вручную отобрать значимые параметры, которые, как нам кажется, должны играть наибольшую роль. На самом деле, этот подход является одним из лучших, если в наличии есть специалист глубоко понимающий предметную область. В случае финансов, это проблематично. В других областях, вроде анализа ДНК или обработки текста ситуация аналогична. Стало быть, процесс отбора параметров следует автоматизировать. &lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Решим задачу на коленке&lt;/h4&gt;&lt;br /&gt;
Чисто интуитивно, можно прикинуть несколько способов автоматизации этой задачи. Например, можно брать параметры по одному, тренировать какой-нибудь метод машинного обучения на каждом отдельном параметре и оценивать точность получаемой модели. Потом отсортировать параметры по точности модели, выбрать несколько, так называемых, лучших индивидуальных предикторов и натренировать модель уже по ним вместе. Этот подход имеет линейную сложность в зависимости от количества параметров и это хорошо. Но достаточно взглянуть на пример, и станет понятно, что этот способ не всегда работает:&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-xPo2y6NsQMU/Tkx2zBiKAXI/AAAAAAAABqI/ddQJdS9UlqI/s1600/distribution.PNG" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="216" width="246" src="http://1.bp.blogspot.com/-xPo2y6NsQMU/Tkx2zBiKAXI/AAAAAAAABqI/ddQJdS9UlqI/s320/distribution.PNG" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Очевидно, что по каждому из параметров значения не разделимы. Но при этом, если рассматривать связку (x,y), то данные очень легко разделить на две группы. Т.е. даже в системах где параметры по одному не несут никакой дополнительной информации, в совокупности они могут привести к хорошим результатам.&lt;br /&gt;
&lt;br /&gt;
Другой подход к решению задачи это полный перебор: перебрав все возможные комбинации параметров, тренируя на них некую модель и оценивая её точность можно выбрать лучший набор параметров. Метод безусловно хорош, но слишком ресурсоемкий и на практике не применим. &lt;br /&gt;
&lt;br /&gt;
Таких методов можно придумать очень много. Все они классифицируются по двум характеристикам. Рассматриваем ли мы параметры по одному или группами и каким образом мы оцениваем удачность подборки параметров.  &lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Классификация методов отбора&lt;/h4&gt;&lt;br /&gt;
По количеству рассматриваемых параметров, методы делятся на: &lt;br /&gt;
&lt;br /&gt;
1) Методы ранжирования параметров.&lt;br /&gt;
&lt;br /&gt;
В методах ранжирования мы тем или иным образом оцениваем качество каждого входного параметра и выбираем лучшие. Здесь часто используются стандартные статистические метрики. Например корреляцию с результирующим значением.&lt;br /&gt;
&lt;br /&gt;
2) Методы работающие с множествами параметров. &lt;br /&gt;
&lt;br /&gt;
На каждом шаге анализируется некоторая комбинация параметров. Вся разница состоит в том, как именно подбираются комбинации. Самый простой вариант, как уже упомяналось выше, это полный перебор всех комбинаций параметров. К сожалению, на практике параметров слишком много и этот подход не работает. Поэтому используется один из менее точных алгоритмов: жадный алгоритм (начать с одного параметра и последовательно добавлять другие, или наоборот начать со всех параметров и последовательно удалять до тех пор пока результат улучшается), генетический алгоритм, simulated annealing, метод градиентного спуска итп. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
По способу отбора алгоритмы делятся на:&lt;br /&gt;
&lt;br /&gt;
1) Фильтры. &lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-PvkLljhGK-I/TkwFAB2exmI/AAAAAAAABp4/Od975bGT2g0/s1600/filter.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="286" width="320" src="http://1.bp.blogspot.com/-PvkLljhGK-I/TkwFAB2exmI/AAAAAAAABp4/Od975bGT2g0/s320/filter.png" /&gt;&lt;/a&gt;&lt;/div&gt;Название говорит за себя. Очень распространены в совокупности с ранжированием параметров. Идея состоит в том, чтобы отобрать интересующие нас параметры по какому-то критерию. Часто используются стандартные статистические тесты. Этот подход полностью независимы от модели, которая используется для классификации. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2) Обертки. &lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-k2_GF5N1a8A/TkwFGOIsQuI/AAAAAAAABqA/YqRA92E4X2w/s1600/wrapper.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="203" width="320" src="http://3.bp.blogspot.com/-k2_GF5N1a8A/TkwFGOIsQuI/AAAAAAAABqA/YqRA92E4X2w/s320/wrapper.png" /&gt;&lt;/a&gt;&lt;/div&gt;В этих алгоритмах используется один из методов классификации для оценки качества подбора параметров. Например, мы можем взять набор параметров, натренировать на нем нейронную сеть, а потом оценить качество полученной нейронной сети. Таким образом мы можем поступить многократно с разными наборами параметров и в итоге выбрать лучший. Многие считают что такой подход хорош потому что он подбирает параметры работающие с данной конкретной моделью. Напомню однако, опять же, что если данных мало, то вероятность оверфитинга резко возрастает.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3) Встроенные методы.&lt;br /&gt;
&lt;br /&gt;
Здесь используется различная внутренняя информация методов классификации для выбора подходящих параметров. Эти методы набирают популярность последнее время, но мы остановимся на них в другой раз.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Пример&lt;/h4&gt;&lt;br /&gt;
&lt;br /&gt;
Перейдем к примеру, думаю, сразу все станет понятней. Допустим мы хотим узнать какие внешние факторы влияют на поведение индекса РТС. Не секрет, что Россия сырьевая страна и, наверное, можно было бы предположить, что влияет на нас более всего цена на нефть и курс доллара. Но как это проверить? Для проверки, предлагается взять значения индекса РТС и некоторое количество потенциальных параметров: курс доллара, цена на нефть brent, цена на нефть light, цена на золото, американский индекс s&amp;p500, европейский индекс ftse100, цена на серебро и просто некоторая случайная величина, для проверки на вшивость. &lt;br /&gt;
&lt;br /&gt;
Итак, запускаем R, собираем данные (нам потребуется последняя версия модуля rusquant (как его можно установить написано &lt;a href="http://www.algorithmist.ru/2011/08/quantmod-for-russian-stock-market.html"&gt;тут&lt;/a&gt;). Т.к. все эти параметры торгуются на разных рынках в разных часовых поясах, придется немного поработать над тем, чтобы выровнять их значения по времени. Собственно, я взял все значения в 18 вечера по Москве. &lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;library(rusquant)
"select.hours" &amp;lt;-
function(data, hour){
    return(data[format(index(data), format="%H")==hour])
}


from &amp;lt;- "2007-10-01"
hour &amp;lt;- "18"
period &amp;lt;- "hour"

RTSI &amp;lt;- select.hours(getSymbols("RTSI", src="Finam", from=from, auto.assign=FALSE, period=period), hour)
BRENT &amp;lt;- select.hours(getSymbols("ICE.BRN", src="Finam", from=from, auto.assign=FALSE, period=period), hour)
LIGHT &amp;lt;- select.hours(getSymbols("NYMEX.CL", src="Finam", from=from, auto.assign=FALSE, period=period), hour)
USDRUB &amp;lt;- select.hours(getSymbols("USDRUB", src="Finam", from=from, auto.assign=FALSE, period=period), hour)
GOLD &amp;lt;- select.hours(getSymbols("comex.GC", src="Finam", from=from, auto.assign=FALSE, period=period), hour)
SP500 &amp;lt;- select.hours(getSymbols("SP500", src="Finam", from=from, auto.assign=FALSE, period=period), hour)
FTSE &amp;lt;- select.hours(getSymbols("FTSE", src="Finam", from=from, auto.assign=FALSE, period=period), hour)
SILVER &amp;lt;- select.hours(getSymbols("comex.SI", src="Finam", from=from, auto.assign=FALSE, period=period), hour)

data &amp;lt;- cbind(Cl(RTSI), Cl(USDRUB), Cl(BRENT), Cl(LIGHT), Cl(GOLD), Cl(SP500), Cl(FTSE), Cl(SILVER))

data &amp;lt;- ROC(data[complete.cases(data)])
data &amp;lt;- data[complete.cases(data)][-1]

#добавим случайное значение
data &amp;lt;- cbind(data, rnorm(length(data$RTSI.Close), mean(data$RTSI.Close), sd(data$RTSI.Close)))

names(data) &amp;lt;- c("RTS", "SI", "BRENT", "LIGHT", "GOLD", "SP500", "FTSE", "SILV", "RND")

&lt;/pre&gt;&lt;br /&gt;
Теперь, когда данные собраны, неплохо было бы посмотреть визуально, что мы имеем, и что хотим сопоставлять. Посмотреть на данные всегда полезно, прежде чем запускать какие-то алгоритмы. С помощью R легко отобразить матрицу парных корреляций между различными параметрами:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;panel.cor &amp;lt;- function(x, y, digits=2, prefix="", cex.cor)
{
    usr &lt;- par("usr"); on.exit(par(usr))
    par(usr = c(0, 1, 0, 1))
    r &lt;- abs(cor(x, y, use="complete.obs"))
    txt &lt;- format(c(r, 0.123456789), digits=digits)[1]
    txt &lt;- paste(prefix, txt, sep="")
    if(missing(cex.cor)) cex &amp;lt;- 0.8/strwidth(txt)
    text(0.5, 0.5, txt, cex = cex * r)
}

pairs(~RTS+SI+BRENT+LIGHT+GOLD+SP500+FTSE+RND, data=data, lower.panel=panel.smooth, upper.panel=panel.cor)


&lt;/pre&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-N5aNNkzb3Qc/TkuedJNCDeI/AAAAAAAABpw/e01X6rTFc1Q/s1600/features.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="274" width="320" src="http://4.bp.blogspot.com/-N5aNNkzb3Qc/TkuedJNCDeI/AAAAAAAABpw/e01X6rTFc1Q/s320/features.png" /&gt;&lt;/a&gt;&lt;/div&gt;Видно, что все наши параметры, кроме случайной величины, сильно коррелируют между собой и с индексом РТС. Какие-то параметры коррелируют сильнее, какие-то слабее. В целом, картина кажется заслуживающей дальнейшего исследования. 

Множество алгоритмов feature selection реализовано в пакете FSelector. Попробуем, для начала, самый простой фильтр: посчитаем корреляцию между каждым из параметров и индексом РТС. 

&lt;pre class="java" name="code"&gt;library(FSelector)
linear.correlation(RTS~SI+BRENT+LIGHT+GOLD+SP500+FTSE+SILV+RND, data=data)
&lt;/pre&gt;Результат:
&lt;pre&gt;SI         0.54385755
BRENT      0.53846199
LIGHT      0.43151621
GOLD       0.26078314
SP500      0.61501880
FTSE       0.64300959
SILV       0.36046822
RND        0.06929931
&lt;/pre&gt;В этом тесте наибольшую значимость показали европейский ftse100, американский s&amp;p500, доллар и нефть. Вполне ожидаемо, что наименьшую значимость имеет случайное число. В принципе, на этом можно было бы и остановиться, выбрав 3-4 самых значимых параметров и передав их в модель. Но кто сказал, что это будет оптимальное решение?

Другой метод сортировки параметров основан на энтропии. Все кто изучали физику помнят, что энтропия системы это мера беспорядка. В данном случае, мы говорим о &lt;a href="http://ru.wikipedia.org/wiki/%D0%98%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D0%B0%D1%8F_%D1%8D%D0%BD%D1%82%D1%80%D0%BE%D0%BF%D0%B8%D1%8F"&gt;информационной энтропии&lt;/a&gt;, которая, как ни странно, тоже является, своего рода, мерой беспорядка в системе. 

&lt;pre class="java" name="code"&gt;information.gain(RTS~SI+BRENT+LIGHT+GOLD+SP500+FTSE+SILV+RND, data=data)
&lt;/pre&gt;Результат:
&lt;pre&gt;SI         0.32154920
BRENT      0.21683641
LIGHT      0.09690079
GOLD       0.03985278
SP500      0.35216883
FTSE       0.38601392
SILV       0.07476571
RND        0.00000000
&lt;/pre&gt;Мы получили другие значения, но при этом порядок в котором отсортированы наши параметры не изменился. 

Перейдем к более сложным методам оценки. Мы продолжим работать с пакетом FSelector и т.к. дальнейшие примеры будут основаны на методах обертках, нам нужна какая-то модель. Для простоты возьмем простейшую модель линейной регрессии. 

&lt;pre class="java" name="code"&gt;evaluator &amp;lt;- function(subset) {
    #k-fold cross validation
    k lt;- 5
    splits lt;- runif(nrow(data))
    results = sapply(1:k, function(i) {
      test.idx lt;- (splits &amp;gt;= (i - 1) / k) &amp; (splits lt; i / k)
      train.idx lt;- !test.idx
      test lt;- data[test.idx, , drop=FALSE]
      train lt;- data[train.idx, , drop=FALSE]
      model lt;- glm(as.simple.formula(subset, "RTS"), data=train, family = gaussian)
      delta lt;- test$RTS - predict(model, test)
      error.rate = sum(delta*delta) / nrow(test)
      return(1 - error.rate)
    })
    return(mean(results))
}

&lt;/pre&gt;На самом деле, у нас не так много параметров и мы можем просто перебрать все возможные комбинации решений. Это конечно занимает несколько минут на моем стареньком ноутбуке, но в данном случае, можно подождать.

&lt;pre class="java" name="code"&gt;exhaustive.search(names(data)[-1], evaluator)
&lt;/pre&gt;Результат:
SI,BRENT,FTSE 

Здесь у нас уже нет никаких цифр, нам выдается лучший из найденных наборов параметров. И т.к. мы решали методом полного перебора, то мы можем быть уверенными, что это вообще лучшее возможное решение для данной модели. Как видим, оно вполне соответствует нашим изначальным прогнозам. Индекс РТС более всего определяется значениями курса доллара, цены на нефть марки brent и европейским фондовым индексом. Честно говоря, я предполагал что здесь еще будет индекс S&amp;P500, но его не оказалось. Возможно, отчасти потому, что наши биржи слишком сильно разнесены по времени. Возможно если бы мы взяли время отсечки не 18 часов, а 23 часа, то S&amp;P500 играл бы существенно большую роль. 

Но вернемся к нашим алгоритмам. В данном случае, на входе было всего 8 параметров и нам пришлось ждать ответа несколько минут. Что если параметров было бы 100 или 1000 или еще больше? В таком случае мы можем воспользоваться алгоритмом градиентного спуска или одним из жадных алгоритмов:

&lt;pre&gt;&gt; hill.climbing.search(names(data)[-1], evaluator)
[1] "SI"    "BRENT" "SP500" "FTSE"  "SILV" 
&gt; forward.search(names(data)[-1], evaluator)
[1] "SI"    "BRENT" "GOLD"  "FTSE"  "SILV" 
&gt; backward.search(names(data)[-1], evaluator)
[1] "SI"    "BRENT" "SP500" "FTSE"  "SILV"  "RND" 
&lt;/pre&gt;&lt;br /&gt;
Результаты другие, тем не менее, они не далеки от оптимальных. &lt;br /&gt;
&lt;br /&gt;
Конечно же это далеко не все возможные подходы, и далеко не все, что можно сделать. Здесь еще можно рассмотреть много интересных вопросов, и к теме feature selection я еще обязательно вернусь.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3994427606477253566-7375413832985854945?l=www.algorithmist.ru' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/algorithmist/ru/~4/WM-KJmVrAYc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.algorithmist.ru/feeds/7375413832985854945/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.algorithmist.ru/2011/08/feature-selection-r.html#comment-form" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/7375413832985854945?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/7375413832985854945?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/algorithmist/ru/~3/WM-KJmVrAYc/feature-selection-r.html" title="Feature selection с примерами на R" /><author><name>SergE</name><uri>http://www.blogger.com/profile/01485674727390704780</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://4.bp.blogspot.com/_rst1xzlxZSU/S_t-TztXAKI/AAAAAAAABRs/LZpr_ILRYOU/S220/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-xPo2y6NsQMU/Tkx2zBiKAXI/AAAAAAAABqI/ddQJdS9UlqI/s72-c/distribution.PNG" height="72" width="72" /><thr:total>4</thr:total><feedburner:origLink>http://www.algorithmist.ru/2011/08/feature-selection-r.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DU8CSH09fip7ImA9WhdWEko.&quot;"><id>tag:blogger.com,1999:blog-3994427606477253566.post-3359899718812909656</id><published>2011-08-08T22:43:00.001+04:00</published><updated>2011-09-06T06:57:49.366+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-06T06:57:49.366+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="rusquant" /><category scheme="http://www.blogger.com/atom/ns#" term="R" /><title>Исследуем Российский рынок ценных бумаг с помощью R</title><content type="html">В прошлых статьях, посвященных среде разработки R, я многократно упоминал о расширении &lt;a href="http://www.quantmod.com/"&gt;quantmod&lt;/a&gt;, пожалуй, одном из самых полезных расширений для желающих строить математические модели финансовых рынков в R. Единственный недостаток этого расширения - отсутствие поддержки российских рынков, и этот недостаток я решил исправить. Для этого, было написано маленькое расширеньице, оно позволяет автоматически выкачивать данные для российских площадок с сайта Finam. По мере возможности, постараюсь добавить еще данные с сайта РТС. &lt;br /&gt;
&lt;br /&gt;
Установить это расширение просто. Если у вас R последней версии (2.13 на момент написания текста) то достаточно запустить его и выполнить команду:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;install.packages("rusquant", repos="http://R-Forge.R-project.org")
&lt;/pre&gt;&lt;br /&gt;
Если R более ранней версии, но вы используете Windows, то можно поступить так:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;install.packages("rusquant", contriburl="http://r-forge.r-project.org/bin/windows/contrib/latest/")
&lt;/pre&gt;&lt;br /&gt;
И третий вариант установки. Можно зайти на &lt;a href="http://r-forge.r-project.org/projects/rusquant/"&gt;http://r-forge.r-project.org/projects/rusquant/&lt;/a&gt; и вручную скачать либо исходники либо готовую версию.&lt;br /&gt;
&lt;br /&gt;
Вот и всё, после сообщения об успешной установке этого и всех зависимых пакетов, вы сможете приступить к работе. &lt;br /&gt;
&lt;br /&gt;
Например, можно построить дневной график фьючерса на индекс РТС:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;library(rusquant)
getSymbols("SPFB.RTS", from="2011-01-01", src="Finam")
chartSeries(SPFB.RTS)
&lt;/pre&gt;&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-UnDPOGYOpPo/Tj9Rl1JpccI/AAAAAAAABnw/ArIOQv-wCPc/s1600/rts.TIF" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="227" src="http://2.bp.blogspot.com/-UnDPOGYOpPo/Tj9Rl1JpccI/AAAAAAAABnw/ArIOQv-wCPc/s320/rts.TIF" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Кроме того, в отличии от оригинального quantmod, здесь есть маленький бонус, а именно, нам доступны не только дневные но и внутри-дневные данные. Например, можно посмотреть часовик фьючерса на золото:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;library(rusquant)
getSymbols("SPFB.GOLD", from=Sys.Date()-5, src="Finam", period="hour")
chartSeries(SPFB.GOLD)
&lt;/pre&gt;&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-ws13-f4Cv7w/Tj-V3jYqSXI/AAAAAAAABn4/x1-Ebd68nag/s1600/GOLD_HOUR.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="279" src="http://2.bp.blogspot.com/-ws13-f4Cv7w/Tj-V3jYqSXI/AAAAAAAABn4/x1-Ebd68nag/s320/GOLD_HOUR.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Или, даже, 5 минутку (будьте, однако осторожны выбирая диапазон, очень легко накачать реально много данных)&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;library(rusquant)
getSymbols("SPFB.GOLD", from=Sys.Date()-5, src="Finam", period="5min")
chartSeries(SPFB.GOLD)
&lt;/pre&gt;&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-ecTqs_N3Sq4/Tj-V8CMhkhI/AAAAAAAABoA/i6Tur5eCcew/s1600/GOLD_5MIN.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="279" src="http://3.bp.blogspot.com/-ecTqs_N3Sq4/Tj-V8CMhkhI/AAAAAAAABoA/i6Tur5eCcew/s320/GOLD_5MIN.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Можно строить математические модели и проводить анализ российских ценных бумаг. Можно воспользоваться любым из десятков индикаторов написанных для модуля TTR, функциями работы с временными данными модуля xts и множеством других возможностей расширений R.&lt;br /&gt;
&lt;br /&gt;
Всего доступны для загрузки 9 временных интервалов: 1min, 5min, 10min, 15min, 30min, hour, day, week, month. Доступные инструменты и их коды можно смотреть на сайте &lt;a href="http://www.finam.ru/analysis/export/default.asp"&gt;источника&lt;/a&gt; в поле имя контракта.&lt;br /&gt;
&lt;br /&gt;
Что планирую добавить в дальнейшем? В первую очередь хочется добавить опционы фортс, потом тиковые данные с финама. Может быть что-то еще, что окажется полезным. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3994427606477253566-3359899718812909656?l=www.algorithmist.ru' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/algorithmist/ru/~4/jeKwermCBoo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.algorithmist.ru/feeds/3359899718812909656/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.algorithmist.ru/2011/08/quantmod-for-russian-stock-market.html#comment-form" title="16 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/3359899718812909656?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/3359899718812909656?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/algorithmist/ru/~3/jeKwermCBoo/quantmod-for-russian-stock-market.html" title="Исследуем Российский рынок ценных бумаг с помощью R" /><author><name>SergE</name><uri>http://www.blogger.com/profile/01485674727390704780</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://4.bp.blogspot.com/_rst1xzlxZSU/S_t-TztXAKI/AAAAAAAABRs/LZpr_ILRYOU/S220/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-UnDPOGYOpPo/Tj9Rl1JpccI/AAAAAAAABnw/ArIOQv-wCPc/s72-c/rts.TIF" height="72" width="72" /><thr:total>16</thr:total><feedburner:origLink>http://www.algorithmist.ru/2011/08/quantmod-for-russian-stock-market.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0ICRn05cSp7ImA9WhdSGUk.&quot;"><id>tag:blogger.com,1999:blog-3994427606477253566.post-1682859025926601126</id><published>2011-07-29T10:42:00.001+04:00</published><updated>2011-07-29T18:26:07.329+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-07-29T18:26:07.329+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Topcoder" /><title>Topcoder. String compression.</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-EYHRVs3MFLY/TjIfGUxuKzI/AAAAAAAABno/Xq-AwbXZjnM/s1600/programmer.gif" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/-EYHRVs3MFLY/TjIfGUxuKzI/AAAAAAAABno/Xq-AwbXZjnM/s1600/programmer.gif" /&gt;&lt;/a&gt;&lt;/div&gt;В прошлый раз, я писал о своем желании провести детальный разбор решений лучших участников топкодера с целью постичь их тайное дао и примкнуть к их рядам. Итак отчитываюсь.&lt;br /&gt;
Первым делом я решил разобрать решение победителя последнего онлайн тура Topcoder Open 2011, участника из Китая, с ником ACRush. Его решение было написано на C++ и состояло из более чем 1800 строк кода. Для того чтобы это переварить, я решил переписать его решение на Java. &lt;br /&gt;
&lt;br /&gt;
Для тех кто не в курсе, расскажу условия задачи. На вход вам давалась некая строка, длинной до 100000 символов. Вам нужно было её сжать, представив в виде нескольких строк вида:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;1: abc2a3sd4
2: qwelwesdfdf
3: erwe2rew4dfds
4: qweqwe2dferw
&lt;/pre&gt;&lt;br /&gt;
Для того чтобы разжать эти строки в исходную, надо начать с первой строки и рекурсивно заменить все встречаемые цифры на строки с соответствующими индексами. Кроме того, на строки накладывается ряд ограничений: задано их количество, задана максимальная длина для каждой строки, запрещены циклические ссылки. &lt;br /&gt;
&lt;br /&gt;
При этом, точной декомпрессии не требуется. Вам надо получить результат разжимающийся как можно более ближе к исходному, но не обязательно полностью совпадающий. &lt;br /&gt;
&lt;br /&gt;
Важным моментом является то, как генерится исходная строка. Для этого сначала генерится от 4 до 8 строк подобных тем, что нам надо вернуть в виде решения. Делается декомпрессия в длинную строку, после чего в полученной строке некий процент символов заменяется случайными (в дальнейшем, я буду называть его процентом ошибок). &lt;br /&gt;
&lt;br /&gt;
Более точно условие задачи можно прочитать &lt;a href="http://www.topcoder.com/longcontest/?module=ViewProblemStatement&amp;amp;rd=14565&amp;amp;pm=11481"&gt;тут&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
В марафона часто возникают задачи, в которых собственный результат можно оценить.  В таких задачах, методика перебора нескольких способов решения и выбора лучшего, очень распространена среди лидеров. И решение ACRush не стало исключением. Я бы выделил в его решении 5 основных подходов. &lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Итак, решение.&lt;/h4&gt;&lt;br /&gt;
Важный момент о решении, который следовало бы сразу заметить:&lt;br /&gt;
&lt;br /&gt;
Нам важно правильно расставить цифры. Если цифры расставлены правильно, то нет ничего сложного в том, чтобы определить оптимальную комбинацию букв. Достаточно просто развернуть сжатое представление, сравнить с исходной строкой и посчитать какая буква и сколько раз попадает в ту или иную позицию в сжатом представлении. После чего, для каждой позиции, выбрать наиболее часто встречаемую букву. Это, вроде бы, простое умозаключение пришло в мою голову лишь на третий или четвертый день. В решении ACRush оно использовалось повсеместно.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Первый подход. Жадный алгоритм.&lt;/h4&gt;&lt;br /&gt;
Этот алгоритм дает минимальное решение для любых начальных данных. По сути, мы перебираем строки вида:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;xxxxxxxxx1
xxxxxxx2
xxxxx333333
xxxxxxxxxx
&lt;/pre&gt;&lt;br /&gt;
Постепенно увеличивая количество цифр и выбирая разные порядки заполнения.Этот подход работает очень быстро и видимо служил чем-то вроде страховки, чтобы вернуть хоть какой-то результат, если все пойдет не так. &lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Второй подход. Поиск в глубину.&lt;/h4&gt;&lt;br /&gt;
Этот подход дает идеальное решение для случаев когда процент ошибок в исходной строке мал. Он основан на том, что мы ищем наиболее вероятных кандидатов исходя из частоты вхождений символов. Идея простая, если посмотреть на мой самый первый пример, то понятно, что в разжатой строке чаще всего будет встречаться подстрока номер 2: qwelwesdfdf&lt;br /&gt;
И это свойство можно применить для поиска строк, использовавшихся для генерации. В своем решении, я сначала искал подстроки по частотам одного символа. Но точность этого подхода стремительно падала с увеличением ошибки. Потом, я стал искать на основе частот парных комбинаций символов. ACRush пошел еще на шаг дальше и искал наиболее часто встречаемые тройки последовательных символов. Таким образом, на каждом шаге поиска в глубину он выбирал подстроки из исходной, в которых тройки последовательных символов (триплеты) встречаются наиболее часто. Таких подстрок он выбирал сотню штук и для каждой из них оценивал, какое количество очков она даст, если ей воспользоваться (исходя из предполагаемого процента ошибки). Выбирал ту, которая дает максимальное количество очков и шел на следующий цикл поиска в глубину. &lt;br /&gt;
&lt;br /&gt;
Для коротких исходных строк ( &amp;lt; 500 символов) частоты триплетов не считались, просто перебирались все возможные подстроки и для них считалось максимально возможное количество очков. &lt;br /&gt;
&lt;br /&gt;
Поиск в глубину запускался многократно, с разными значениями предполагаемого процента ошибки. При этом, всякий раз когда решение доходило до последней или предпоследней строки, полученный результат оценивался и складывался в список лучших найденных результатов. Позже эти результаты использовались как основа для третьего подхода.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Третий подход. Динамическое программирование.&lt;/h4&gt;&lt;br /&gt;
Здесь, на вход нам подавались частичные решения найденные на предыдущих этапах. Частичные они были в том смысле, что мы могли найти, скажем, 2 или 3 предполагаемых строки из четырех. Поиск в глубину, во втором подходе, идет снизу вверх, т.е. из нашего примера мы сначала ищем строку № 2, выполняем замену, потом ищем строку № 4 и т.д. Слабым местом такого подхода является как раз замена. Ведь мы не знаем процент ошибки в данном конкретном месте строки. Можно, конечно, брать ошибку с запасом, но увеличение ошибки приводит к ложным срабатываниям. Таким образом лучше всего было бы, перебрать все возможные варианты расстановки и выбрать из них оптимальный. &lt;br /&gt;
Это и реализовал ACRush использовав динамическое программирование.  Все это проводилось для сотни наилучших частичных решений найденных к этому моменту. &lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Четвертый и пятый подходы.&lt;/h4&gt;&lt;br /&gt;
По моим ощущениям, 99% успеха являются результатом второго и третьего подхода. Однако, видимо для того чтобы совсем добить конкурентов, в решении было реализовано еще два подхода. Случайное блуждание и метод грубой силы.&lt;br /&gt;
Для случайного блуждания выбиралось наилучшее имеющееся решение и проводились всевозможные замены цифр в каждой из строк (я уже говорил, что расстановка цифр однозначно определяет максимальное количество очков).&lt;br /&gt;
Метод грубой силы выполнялся все оставшееся время решения. Заключался он в том, чтобы перебрать все возможные порядки строк и все возможные расстановки цифр, до тех пор пока время не кончится.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Сложилось общее впечатление, что код очень сжатый, зачастую в нескольких строчках скрывается огромное количество мелких деталей, без которых тяжело понять большую картину. А без понимания общей картины тяжело разбираться в деталях.  В любом случае, для меня это был полезный опыт, если кому-то еще это покажется полезным, буду рад :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3994427606477253566-1682859025926601126?l=www.algorithmist.ru' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/algorithmist/ru/~4/XsPg1ntpJhc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.algorithmist.ru/feeds/1682859025926601126/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.algorithmist.ru/2011/07/topcoder-string-compression.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/1682859025926601126?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/1682859025926601126?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/algorithmist/ru/~3/XsPg1ntpJhc/topcoder-string-compression.html" title="Topcoder. String compression." /><author><name>SergE</name><uri>http://www.blogger.com/profile/01485674727390704780</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://4.bp.blogspot.com/_rst1xzlxZSU/S_t-TztXAKI/AAAAAAAABRs/LZpr_ILRYOU/S220/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-EYHRVs3MFLY/TjIfGUxuKzI/AAAAAAAABno/Xq-AwbXZjnM/s72-c/programmer.gif" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.algorithmist.ru/2011/07/topcoder-string-compression.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUYDRXw9eip7ImA9WhdTFUU.&quot;"><id>tag:blogger.com,1999:blog-3994427606477253566.post-8901622110347200642</id><published>2011-07-13T22:52:00.000+04:00</published><updated>2011-07-13T22:52:54.262+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-07-13T22:52:54.262+04:00</app:edited><title>Немного вечерней философии...</title><content type="html">Сегодня для меня завершился очередной &lt;a href="http://www.topcoder.com/longcontest/?module=ViewStandings&amp;amp;rd=14565"&gt;Topcoder Open&lt;/a&gt;, не буду скрывать, в глубине души я все-таки надеялся попасть в финал. Но, надежды надеждами, а результаты так себе. И вроде бы с одной стороны можно успокаивать себя прогрессом, два года назад я вообще не прошел в третий тур, год назад я был на 66-ом месте, а в этом году предварительно на 52-ом, но уж больно медленный этот прогресс. И особенно обидно осознавать, читая описания решений лидеров, что идея то решения была правильной. Как всегда, не хватило каких-то деталей реализации, каких-то мелочей, из которых, конечно же, в нашем деле состоит всё. &lt;br /&gt;&lt;br /&gt;И вот сейчас я бы хотел полностью пересмотреть свой подход к решению подобных задач. Нутром чую, что что-то делаю не так. Конечно было бы здорово поучиться у лидеров лично, но где они? Кто из них пишет блог или читает лекции? Пока мне видится единственный вариант: детальный разбор лучших решений, вылизывание и оттачивание собственных, до тех пор пока они не окажутся столь же успешным как и у лидеров. &lt;br /&gt;&lt;br /&gt;Я постараюсь, да да, знаю, я уже много чего наобещал за последнее время, но все же, я постараюсь разбирать решения и публиковать тут детали реализации. Зачем? Прежде всего чтобы убедиться, что я сам всё понял, ведь лучший способ разобраться в проблеме это рассказать её другим людям, правда? &lt;br /&gt;&lt;br /&gt;Надеюсь, что среди читателей этого блога найдутся еще желающие улучшить свои навыки спортивного программирования, если да, то пишите в комментарии или в почту, пообщаемся. Учиться вместе намного веселей ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3994427606477253566-8901622110347200642?l=www.algorithmist.ru' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/algorithmist/ru/~4/h1rs4vXhgEY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.algorithmist.ru/feeds/8901622110347200642/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.algorithmist.ru/2011/07/blog-post.html#comment-form" title="7 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/8901622110347200642?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/8901622110347200642?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/algorithmist/ru/~3/h1rs4vXhgEY/blog-post.html" title="Немного вечерней философии..." /><author><name>SergE</name><uri>http://www.blogger.com/profile/01485674727390704780</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://4.bp.blogspot.com/_rst1xzlxZSU/S_t-TztXAKI/AAAAAAAABRs/LZpr_ILRYOU/S220/me.jpg" /></author><thr:total>7</thr:total><feedburner:origLink>http://www.algorithmist.ru/2011/07/blog-post.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0IMQHk9fyp7ImA9WhdTFEQ.&quot;"><id>tag:blogger.com,1999:blog-3994427606477253566.post-2984849876930522366</id><published>2011-07-12T21:26:00.001+04:00</published><updated>2011-07-12T21:26:21.767+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-07-12T21:26:21.767+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Data mining" /><category scheme="http://www.blogger.com/atom/ns#" term="R" /><title>Метод опорных векторов с примерами на R</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-ika34Mq6lcM/TcKrxEM2vjI/AAAAAAAABg8/rnf34rG3Ehw/s1600/svm_wiki.PNG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="261" src="http://3.bp.blogspot.com/-ika34Mq6lcM/TcKrxEM2vjI/AAAAAAAABg8/rnf34rG3Ehw/s400/svm_wiki.PNG" width="295" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Друзья, после продолжительного затишься, я продолжаю тему "&lt;a href="http://www.algorithmist.ru/search/label/Data%20mining"&gt;data mining с примерами на R&lt;/a&gt;". И эта статья посвящается методу опорных векторов (в английской литературе support vector machines или, сокращенно, svm). В интернете масса информации по этому методу и, как всегда, она изобилует сложными математическими формулами, за которыми теряется общая идея. Поэтому я решил написать эту статью без единой формулы, в конце концов, если кому-то нужны формулы, он всегда может обратиться в википедию, скриншот из которой вы можете увидеть слева.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Итак, в чем же состоит идея метода опорных векторов? Давайте, сначала рассмотрим очень простой случай. Предположим, что у нас есть множество точек на плоскости, часть которых относится к классу A, а другая часть к классу B и есть точки класс которых нужно определить. Задачу такого рода, по-научному можно определить как задачу классификации, причем, для её решения, нужен алгоритм обучения "с учителем". Метод опорных векторов как раз подходит именно для таких задач.&lt;br /&gt;
&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Рассмотрим, для начала, упрощенный случай. Пусть точки, принадлежащие разным классам, можно разделить с помощью прямой линии:&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-SS2ryccHKZQ/Tgl22FKJi6I/AAAAAAAABl8/-yA_qY5WqDE/s1600/svm1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/-SS2ryccHKZQ/Tgl22FKJi6I/AAAAAAAABl8/-yA_qY5WqDE/s1600/svm1.png" /&gt;&lt;/a&gt;&lt;/div&gt;Очевидный способ решения задачи в таком случае провести прямую так, чтобы все точки одного класса лежали по одну сторону от этой прямой, а все точки другого класса были на противоположной стороне. Тогда чтобы классифицировать неизвестные точки нам нужно просто посмотреть с какой стороны прямой они окажутся. Халява? Отнюдь, ведь можно провести бесконечное множество прямых удовлетворяющих нашему условию&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-v9eN8XcqXvg/Tgl3cMq8_eI/AAAAAAAABmA/5HITzvNgP4g/s1600/svm2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/-v9eN8XcqXvg/Tgl3cMq8_eI/AAAAAAAABmA/5HITzvNgP4g/s1600/svm2.png" /&gt;&lt;/a&gt;&lt;/div&gt;Какую из них выбрать? Интуитивно понятно, что нам бы хотелось прямую где-нибудь по центру. Такая вот прямая, очевидно, не является лучшим выбором:&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-QGwVNgA74ag/Tgl4AzrIWbI/AAAAAAAABmE/HnFO6Up0nvI/s1600/svm3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/-QGwVNgA74ag/Tgl4AzrIWbI/AAAAAAAABmE/HnFO6Up0nvI/s1600/svm3.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Интуитивно, мы видимо, что лучше всего выбрать прямую максимально удаленную от имеющихся точек. Тут, конечно, встает вопрос терминологии. Что такое расстояние между прямой и множеством точек? В методе опорных векторов этим расстоянием считается расстояние между прямой и ближайшей к ней точкой из множества. Именно такое расстояние и максимизируется в методе опорных векторов.&amp;nbsp; &lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-JpnBSVBgcY8/Tgl7tNfaQ-I/AAAAAAAABmU/rURrtLomTyE/s1600/svm4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/-JpnBSVBgcY8/Tgl7tNfaQ-I/AAAAAAAABmU/rURrtLomTyE/s1600/svm4.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Геометрически это выглядит так, как будто мы пытаемся провести прямую строго по центру между двумя множествами. Оказывается, что такая прямая существует лишь одна. И её не так уж сложно найти. Ближайшие к этой прямой точки из множеств называются опорными векторами.&lt;br /&gt;
&lt;br /&gt;
В простейшем случае, именно такую прямую и находит метод опорных векторов. Как именно он её находит? Чтобы ответить на этот вопрос пришлось бы привести кучу математических выкладок, которые в итоге сводятся к некоторой системе уравнений, которая, в свою очередь решается &lt;a href="http://en.wikipedia.org/wiki/Quadratic_programming"&gt;методами квадратичного программирования&lt;/a&gt;. &lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;А если всё не так гладко?&lt;/h4&gt;&lt;br /&gt;
В реальной жизни, к сожалению, данные далеко не всегда можно разделить линейно. И, даже когда это можно, возникают ситуации, в которых мы не хотим этого делать. Например в таком случае:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-5hGiZJ6GCeg/Tgl89w_QSzI/AAAAAAAABmY/ICmlYjnZssI/s1600/svm4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/-5hGiZJ6GCeg/Tgl89w_QSzI/AAAAAAAABmY/ICmlYjnZssI/s1600/svm4.png" /&gt;&lt;/a&gt;&lt;/div&gt;Очевидно, что красная точка справа является выбросом и было бы лучше её игнорировать. Что делать в таком случае? В этом случае на помощь к нам приходит, так называемый, алгоритм с мягким зазором (soft marging). Математически это выражается введением дополнительных параметров в систему уравнений, а по сути, мы просто назначаем некий штраф, за каждую точку, оказавшуюся на чужой стороне. Используя такой подход мы можем работать даже с линейно неразделимыми данными.&lt;br /&gt;
&lt;br /&gt;
Естественно, прямыми на плоскости все дело не ограничивается. Вместо плоскости могут быть сколь-угодно многомерные пространства, а вместо прямых гиперплоскости в этих пространствах. Сам алгоритм, при этом не меняется.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;А если все-таки нет прямой?&lt;/h4&gt;&lt;br /&gt;
Если бы это было всё, метод опорных векторов не был бы таким мощным. Действительно, в чем радость разделять данные линейно? С этим отлично могла бы справиться и простая линейная регрессия. На практике, мы не знаем структуру данных и очень редко их можно разделить прямой линией или плоскостью. Что если у нас на входе, например, такая картинка?&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-6MPJFSzBl3w/Tgocu23l2tI/AAAAAAAABmc/IcV_LBweKuE/s1600/svm4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/-6MPJFSzBl3w/Tgocu23l2tI/AAAAAAAABmc/IcV_LBweKuE/s1600/svm4.png" /&gt;&lt;/a&gt;&lt;/div&gt;Оказывается, что система уравнений составляющая метод опорных векторов содержит только скалярные произведения координат различных точек. И с точки зрения математики, можно заменить каждое скалярное произведение на некоторую функцию (ядро) отвечающую определенным требованиям. Замена скалярного произведения ядром позволяет перевести проблему в пространство с другими измерениями. И в этом пространстве данные могут оказаться разделяемыми. Для примера, предлагаю посмотреть ролик, в котором все наглядно показано.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://2.gvt0.com/vi/9NrALgHFwTo/0.jpg" height="266" width="320"&gt;&lt;param name="movie" value="http://www.youtube.com/v/9NrALgHFwTo&amp;fs=1&amp;source=uds" /&gt;&lt;param name="bgcolor" value="#FFFFFF" /&gt;&lt;embed width="320" height="266"  src="http://www.youtube.com/v/9NrALgHFwTo&amp;fs=1&amp;source=uds" type="application/x-shockwave-flash"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;br /&gt;
Таким образом, метод опорных векторов позволяет работать не только с линейно-разделяемыми данными, но и с более общими случаями. Кроме того, на практике, в отличии от, например, нейронных сетей, метод опорных векторов меньше страдает от перетренировки(overfitting): ситуаций, в которых алгоритм выявляет какие-то частные закономерности в тренировочных данных, в результате чего хорошо работает с тренировочным набором, но показывает очень плохой результат на новых и неизвестных данных.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Пример:&lt;/h4&gt;Уже по сложившейся традиции, я приведу пример из области финансовых рынков. Идея напрашивается сама собой, раз уж метод опорных векторов так хорошо классифицирует данные и способен работать как черный ящик с очень большим объемом входных параметров, то давайте скормим ему на вход как можно больше технических индикаторов и посмотрим, как хорошо он нам предскажет изменение цены.&lt;br /&gt;
Для анализа, мы возьмем индекс S&amp;amp;amp;P500, его значения могут быть загружены автоматически в программу на R с помощью модуля quantmod. А реализацию метода опорных векторов мы найдем в пакете e1071.  &lt;br /&gt;
&lt;br /&gt;
&lt;a href="http://www.algorithmist.ru/2011/03/r-language-for-traders.html"&gt;В своих прошлых статьях я уже писал о том, как настроить R и как устанавливать требуемые пакеты&lt;/a&gt;. Поэтому перейдем сразу к делу. &lt;br /&gt;
&lt;br /&gt;
Сначала нам предстоит провести подготовительную работу с данными, а именно, преобразовать их в удобоваримую форму:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;library(quantmod)
library(e1071)

#Будем исследовать индекс S&amp;amp;P500
getSymbols("^GSPC", from='2005-01-01')
data &amp;lt;- GSPC

#Накидаем в нашу модель побольше разных индикаторов
atr &amp;lt;- ATR(data)[,'atr']
ema7 &amp;lt;- EMA(Cl(data), n=7)[,1]
ema14 &amp;lt;- EMA(Cl(data), n=14)[,1]
ema50 &amp;lt;- EMA(Cl(data), n=50)[,1]
macd &amp;lt;- MACD(Cl(data))[,'macd']
macd.signal &amp;lt;- MACD(Cl(data))[,'signal']
rsi &amp;lt;- RSI(Cl(data))
cmo &amp;lt;- CMO(Cl(data))
dc.high &amp;lt;- DonchianChannel(Cl(data))[,'high']
dc.low &amp;lt;- DonchianChannel(Cl(data))[,'low']
adx &amp;lt;- ADX(data)[,'ADX']

#определим модель, здесь важно сдвинуть либо предсказываемые значения на день вперед либо предикторы на день назад
model &amp;lt;- specifyModel(Next(Delt(Cl(data))) ~ atr + ema7 + ema14 + ema50 + macd + macd.signal + rsi + cmo + dc.high + dc.low + adx)

#разделим данные на две части - тренировочные и тестовые
train.window &amp;lt;- c('2005-01-01','2009-01-01')
validate.window &amp;lt;- c('2009-01-01', '2011-06-29')

train &amp;lt;- na.omit(as.data.frame(modelData(model, data.window=train.window)))
validate &amp;lt;- na.omit(as.data.frame(modelData(model, data.window=validate.window)))

#мы хотим покупать или продавать, когда индекс изменяется как минимум на 0.5% в ту или иную сторону, остальные случаи нас не интересуют
signals&amp;lt;-function(x) {

	if(x &amp;gt;= -0.005 &amp;amp;&amp;amp; x&amp;lt;=0.005) { result&amp;lt;-"none" } else
	if(x &amp;gt; 0.005) { result&amp;lt;-"buy" } else
	if(x &amp;lt; -0.005) { result&amp;lt;-"sell" }

	result
}

#классифицируем имеющиеся данные, не забыв при этом выбросить из них сами цены, чтобы алгоритм не подсматривал ;)
class&amp;lt;-sapply(train$Next.Delt.Cl.data,signals)
train&amp;lt;-cbind(train,class)
train$Next.Delt.Cl.data &amp;lt;- NULL

class&amp;lt;-sapply(validate$Next.Delt.Cl.data,signals)
validate&amp;lt;-cbind(validate,class)
validate$Next.Delt.Cl.data &amp;lt;- NULL
&lt;/pre&gt;&lt;br /&gt;
Теперь когда у нас готова модель данных мы можем запустить наш алгоритм для анализа. Тут надо заметить, что метод опорных векторов, как впрочем и любой алгоритм data mining имеет множество параметров. На данном шаге мы выберем некоторые значения параметров произвольно:  &lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;#подготовка данных закончилась, можно запускать SVM на тренировочных данных
sv&amp;lt;-svm(class~.,train,gamma=0.01,cost=5,kernel="radial")

#а теперь посмотрим, насколько точно наш алгоритм предсказывает тестовые данные
table(actual=validate$class,predicted=predict(sv,newdata=validate,type="class"))
&lt;/pre&gt;&lt;br /&gt;
Полученные результаты оказываются лишь немного лучше, чем бросание монетки: &lt;br /&gt;
&lt;pre&gt;predicted
actual  buy none sell
  buy   60  136    3
  none  39  227    3
  sell  52  102    4
&lt;/pre&gt;&lt;br /&gt;
Что делать? Попробуем оптимизировать параметры, в конце концов, в документации к svm английским по белому сказано "Parameters of SVM-models usually must be tuned to yield sensible results! " Попробуем подобрать более оптимальные значения. К счастью для нас, нет никакой необходимости делать это вручную, модуль e1071 содержит метод tune который сделает всю работу. Для этого он применит кросс-валидацию. В процессе кросс-валидации тренировочные данные разбиваются на две части, модель тренируется на одной части, а тестируется на другой и так многократно. В итоге, выбираются лучшие параметры и модель с лучшими параметрами тренируется снова уже на полном наборе тренировочных данных. Все просто. Приступим:  &lt;br /&gt;
&lt;pre class="java" name="code"&gt;#попробуем подобрать оптимальные параметры для алгоритма методом кросс-валиадации, эта строчка может выполняться несколько часов перебирая все возможные 

комбинации параметров
tuned &amp;lt;- tune(svm, class~., data = train,ranges = list(gamma = c(0.0001,0.001,0.05,0.1,0.2,0.3), cost = c(1,5,10,20,50,100,120,130)),tunecontrol = tune.control(sampling = "cross"),cross=3)

tuned

&lt;/pre&gt;&lt;br /&gt;
Дальше мы просто копируем найденные оптимальные значения и запускаем наш алгоритм снова  &lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;#подготовка данных закончилась, можно запускать SVM на тренировочных данных
sv&amp;lt;-svm(class~.,train,gamma=0.05,cost=10,kernel="radial")

#а теперь посмотрим, насколько точно наш алгоритм предсказывает тестовые данные
table(actual=validate$class,predicted=predict(sv,newdata=validate,type="class"))
&lt;/pre&gt;&lt;br /&gt;
Результаты, конечно не идеальны, но очевидно, что стали намного лучше.  &lt;br /&gt;
&lt;pre&gt;predicted
actual buy none sell
  buy   73  123    3
  none  46  215    8
  sell  48   99   11
&lt;/pre&gt;&lt;br /&gt;
На всякий случай замечу, что это не готовая торговая система и не стоит бросаться с ней на биржу, но, в целом, выглядит заманчиво.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3994427606477253566-2984849876930522366?l=www.algorithmist.ru' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/algorithmist/ru/~4/x7VIMPJVbYM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.algorithmist.ru/feeds/2984849876930522366/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.algorithmist.ru/2011/07/support-vector-machines-with-examples.html#comment-form" title="7 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/2984849876930522366?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/2984849876930522366?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/algorithmist/ru/~3/x7VIMPJVbYM/support-vector-machines-with-examples.html" title="Метод опорных векторов с примерами на R" /><author><name>SergE</name><uri>http://www.blogger.com/profile/01485674727390704780</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://4.bp.blogspot.com/_rst1xzlxZSU/S_t-TztXAKI/AAAAAAAABRs/LZpr_ILRYOU/S220/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-ika34Mq6lcM/TcKrxEM2vjI/AAAAAAAABg8/rnf34rG3Ehw/s72-c/svm_wiki.PNG" height="72" width="72" /><thr:total>7</thr:total><feedburner:origLink>http://www.algorithmist.ru/2011/07/support-vector-machines-with-examples.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C04HQn0_cCp7ImA9WhdSGEg.&quot;"><id>tag:blogger.com,1999:blog-3994427606477253566.post-1379231828283353538</id><published>2011-06-16T06:44:00.003+04:00</published><updated>2011-07-28T15:18:53.348+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-07-28T15:18:53.348+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Математика" /><category scheme="http://www.blogger.com/atom/ns#" term="Цифровая обработка сигналов" /><title>Фильтр Калмана, руководство полного идиота</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-EOfrtFcY__w/TfjjEI44wdI/AAAAAAAABlc/dNJyJ9PZWXw/s1600/Kalman_dummies.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://3.bp.blogspot.com/-EOfrtFcY__w/TfjjEI44wdI/AAAAAAAABlc/dNJyJ9PZWXw/s320/Kalman_dummies.png" width="255" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Фильтру Калмана посвящено множество статей и книг, совсем недавно была опубликована &lt;a href="http://habrahabr.ru/blogs/algorithm/120133/"&gt;статья на хабре&lt;/a&gt;. Однако, чтение подобных статей у меня всегда опасение: а не идиот ли я? К сожалению, разобраться в чем же собственно суть дела, за всеми этими многоэтажными индексами и закорючками довольно сложно. Да и нужно ли? Здесь я не буду в очередной раз расписывать алгоритм и его математические основы, об этом можно почитать в книжках, википедии или где угодно еще. Сегодня я постараюсь изложить практическую сторону вопроса.  &lt;br /&gt;
&lt;br /&gt;
Итак, что же такое фильтр Калмана. Предположим у вас есть некая величина X изменяющаяся во времени. У вас есть некий прибор, измеряющий величину X с некоторой погрешностью. Вы хотите оценить, чему на самом деле равна величина X. Например, в случае акций X&lt;sub&gt;k&lt;/sub&gt; может быть истинной ценой акции в момент времени k (она определяется доходами компании, курсом валют, ожиданиями инвесторов итп). У нас есть только один способ измерить эту величину - пойти на биржу и посмотреть котировки. Но котировки на бирже подвержены влиянию массы факторов, цены колеблются в широком диапазоне и измеренная величина Y&lt;sub&gt;k&lt;/sub&gt; может отличаться от X&lt;sub&gt;x&lt;/sub&gt;. Алгоритм Калмана позволяет нам оценить истинное значение X&lt;sub&gt;k&lt;/sub&gt; на основе истории изменения измеряемой величины Y&lt;sub&gt;k&lt;/sub&gt;. При этом, естественно, делается ряд предположений о связи между Y и X и характере изменения X. &lt;br /&gt;
&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
&lt;h4&gt;Предположение номер 1&lt;/h4&gt;&lt;br /&gt;
X&lt;sub&gt;k&lt;/sub&gt; = A&lt;sub&gt;k&lt;/sub&gt; * X&lt;sub&gt;k-1&lt;/sub&gt; +  B&lt;sub&gt;k&lt;/sub&gt; * U&lt;sub&gt;k&lt;/sub&gt; + W&lt;sub&gt;k-1&lt;/sub&gt;&lt;br /&gt;
&lt;br /&gt;
Т.е. величина X в момент времени k представляет собой линейную комбинацию величины X в прошлый момент времени, управляющего сигнала U и шума W. В реальных задачах часто управляющий сигнал U отсутствует. &lt;br /&gt;
&lt;br /&gt;
Что такое управляющий сигнал? Вернемся к нашему примеру с акциями. Предположим, что &lt;a href="http://ru.wikipedia.org/wiki/%D0%A4%D0%B5%D0%B4%D0%B5%D1%80%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F_%D1%80%D0%B5%D0%B7%D0%B5%D1%80%D0%B2%D0%BD%D0%B0%D1%8F_%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0"&gt;ФРС США&lt;/a&gt; активно печатает деньги, например 600 млрд долларов, эти деньги льются широкой рекой на рынок и неизбежно двигают цены акций вверх. Это и есть управляющий сигнал. &lt;br /&gt;
&lt;br /&gt;
Но вернемся к нашей формуле, нам потребуется сделать еще одно важное уточнение. Шум w должен иметь нормальное распределение с дисперсией dW. На практике, часто так оно и есть, здесь на руку нам играет &lt;a href="http://ru.wikipedia.org/wiki/%D0%A6%D0%B5%D0%BD%D1%82%D1%80%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F_%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F_%D1%82%D0%B5%D0%BE%D1%80%D0%B5%D0%BC%D0%B0"&gt;центральная предельная теорема&lt;/a&gt;, ведь шум это, как правило, совокупность большого множества неизвестных факторов. &lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Предположение номер 2&lt;/h4&gt;&lt;br /&gt;
Y&lt;sub&gt;k&lt;/sub&gt; = H&lt;sub&gt;k&lt;/sub&gt; * X&lt;sub&gt;k&lt;/sub&gt;  + V&lt;sub&gt;k&lt;/sub&gt;&lt;br /&gt;
&lt;br /&gt;
Т.е. наша измеряемая величина Y это линейная комбинация реальной величины X и погрешности измерения V. При этом, делается дополнительное предположение что погрешность измерения тоже имеет нормальное распределение со своей дисперсией dV. Очень часто на практике H = 1. &lt;br /&gt;
&lt;br /&gt;
Что мы только что сделали? Мы построили модель, описывающую нашу систему, при этом, заметьте, пока никакого фильтра Калмана не упоминалось. &lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Что мы хотим сделать? &lt;/h4&gt;&lt;br /&gt;
Мы хотим получить некое уравнение &lt;br /&gt;
&lt;br /&gt;
X&lt;sub&gt;k&lt;/sub&gt; = F(X&lt;sub&gt;k-1, k-2...&lt;/sub&gt;, Y&lt;sub&gt;k, k-1...&lt;/sub&gt;, dW, dV)&lt;br /&gt;
&lt;br /&gt;
позволяющее нам оценивать истинное значение X исходя из исторических оценок и измеряемых величин. Придирчивый читатель заметит, что здесь я непоследовательно использую X, действительно раньше я использовал X для обозначения истинного значения величины, а теперь для &lt;b&gt;оценки&lt;/b&gt; истинного значения. Это важный момент, ведь реально истинного значения мы никогда не сможем узнать. Я просто стараюсь не загромождать формулы новыми сущностями.&lt;br /&gt;
&lt;br /&gt;
Дальше обычно идут многостраничные математические выкладки, включающие в себя взятие частных производных, экстраполяции, коррекции. Опустим всё это, интересующиеся легко найдут массу материала в гугле. В конце концов, получается выражение для оценки X:&lt;br /&gt;
&lt;br /&gt;
X&lt;sub&gt;k&lt;/sub&gt; = K&lt;sub&gt;k&lt;/sub&gt; * Y&lt;sub&gt;k&lt;/sub&gt; + (1 - K&lt;sub&gt;k&lt;/sub&gt;) * X&lt;sub&gt;k-1&lt;/sub&gt;&lt;br /&gt;
&lt;br /&gt;
Где K&lt;sub&gt;k&lt;/sub&gt; - это Калмановский коэффициент усиления. Именно эту величину, в процессе работы и определяет алгоритм фильтра Калмана. При этом фильтр Калмана находит оптимальную величину коэффициента усиления при заданных дисперсиях dW и dV. Чем лучше вы оцените эти дисперсии, тем лучше будет работать фильтр Калмана. &lt;br /&gt;
&lt;br /&gt;
А теперь, представьте, что X и Y это не одномерные величины, а вектор. A, B и H - матрицы. Погрешность и шум V и W тоже векторы. Оказывается, что при этом, все уравнения будут работать точно так же, и алгоритм фильтра Калмана легко обобщается на многомерные величины. &lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;А теперь, по традиции, пример на R&lt;/h4&gt;&lt;br /&gt;
Фильтр Калмана можно найти в нескольких модулях R, для примера я буду использовать реализацию из модуля dlm. В данном примере мы применим фильтр Калмана к значениям индекса S&amp;amp;amp;P500. &lt;br /&gt;
&lt;br /&gt;
Предположим, что управляющего сигнала нет, это конечно, на самом деле, не так, но оценка управляющего сигнала это отдельная задача. Кроме того, отдельными задачами являются оценки параметров дисперсий dV и dW. Для нашего примера я просто подобрал некоторые значения эмпирическим путем. Если же подойти к этой задаче основательно, то их вполне можно оценить исходя из волатильности на рынке. &lt;br /&gt;
&lt;br /&gt;
Итак, пример:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;library(dlm)
library(quantmod)

getSymbols("^GSPC", from="2010-09-01")
y &amp;lt;- as.ts(Cl(GSPC))
x &amp;lt;- dlmFilter(y, dlmModPoly(1, dV=0.1, dW=0.01))

plot(y, type='l')
lines(x$f[-1], col = "red")
#Добавим EMA для сравнения
lines(EMA(y), col = "green")
&lt;/pre&gt;&lt;br /&gt;
Для сравнения, на график, зеленым цветом, было добавлено 10-дневное &lt;a href="http://ru.wikipedia.org/wiki/%D0%A1%D0%BA%D0%BE%D0%BB%D1%8C%D0%B7%D1%8F%D1%89%D0%B0%D1%8F_%D1%81%D1%80%D0%B5%D0%B4%D0%BD%D1%8F%D1%8F"&gt;экспоненциальное скользящее среднее&lt;/a&gt;  Получилась картинка: &lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-CwhMZBETA0Y/TjFFSNcCqaI/AAAAAAAABnk/NJiP8D48Zpg/s1600/kalman.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="281" width="320" src="http://2.bp.blogspot.com/-CwhMZBETA0Y/TjFFSNcCqaI/AAAAAAAABnk/NJiP8D48Zpg/s320/kalman.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Большое спасибо читателю по имени Kirill за найденную в коде примера ошибку. &lt;br /&gt;
&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3994427606477253566-1379231828283353538?l=www.algorithmist.ru' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/algorithmist/ru/~4/QlyikL_rr7U" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.algorithmist.ru/feeds/1379231828283353538/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.algorithmist.ru/2011/06/kalman-filter-for-complete-idiot.html#comment-form" title="7 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/1379231828283353538?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/1379231828283353538?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/algorithmist/ru/~3/QlyikL_rr7U/kalman-filter-for-complete-idiot.html" title="Фильтр Калмана, руководство полного идиота" /><author><name>SergE</name><uri>http://www.blogger.com/profile/01485674727390704780</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://4.bp.blogspot.com/_rst1xzlxZSU/S_t-TztXAKI/AAAAAAAABRs/LZpr_ILRYOU/S220/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-EOfrtFcY__w/TfjjEI44wdI/AAAAAAAABlc/dNJyJ9PZWXw/s72-c/Kalman_dummies.png" height="72" width="72" /><thr:total>7</thr:total><feedburner:origLink>http://www.algorithmist.ru/2011/06/kalman-filter-for-complete-idiot.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkcMR30-cSp7ImA9WhZWGEs.&quot;"><id>tag:blogger.com,1999:blog-3994427606477253566.post-4203148571073323376</id><published>2011-05-20T06:54:00.001+04:00</published><updated>2011-05-20T06:54:46.359+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-05-20T06:54:46.359+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="работа" /><title>Пока Дойче. Привет Одноклассники!</title><content type="html">Как некоторым уже стало известно, затишье в моем блоге последние пару недель было вызвано сменой работы. Да, отныне я более не сотрудник Deutsche Bank, а работаю в компании Одноклассники.ру Это, однако, отнюдь не значит что финансовые рынки мне более не интересны. Скорее наоборот, теперь я освобожден от ограничений, накладываемых контрактом инвестиционного банка и могу свободно спекулировать на бирже. Так что, рассчитываю в ближайшее время опробовать на практике множество идей, о которых пока мог только теоретизировать. Надеюсь так же, что и для графоманской деятельности вскоре найдется время и мой блог пополнится множеством новых замечательных статей. &lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-e5GABzCRYMU/TdUSSDV7TTI/AAAAAAAABhU/VLM-1O4r5AM/s1600/images.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="144" src="http://3.bp.blogspot.com/-e5GABzCRYMU/TdUSSDV7TTI/AAAAAAAABhU/VLM-1O4r5AM/s400/images.jpg" width="192" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
О работе в дойче хочу сказать, что это лучшее из всех мест где мне доводилось работать прежде. И это единственное место, из которого я уходил с некоторым ощущением грусти и сожаления. Люди, с которыми мне довелось там познакомиться и поработать, это действительно высококлассные специалисты и очень талантливые разработчики. Желаю им всем удачи и всяческих успехов.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-0EgUJ62CV7w/TdUSV3Yhp6I/AAAAAAAABhc/4zwGGJCbSRI/s1600/7823-npadvhover.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="151" src="http://4.bp.blogspot.com/-0EgUJ62CV7w/TdUSV3Yhp6I/AAAAAAAABhc/4zwGGJCbSRI/s400/7823-npadvhover.jpg" width="192" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
В то же время, я надеюсь, что мое новое место работы будет еще лучше и еще интересней. Надеюсь найти здесь сложные задачи и получить бесценный опыт разработки высоконагруженных многопользовательских систем. Думаю, что мне найдется здесь чему поучиться и будет возможность принести пользу, как специалист.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3994427606477253566-4203148571073323376?l=www.algorithmist.ru' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/algorithmist/ru/~4/BvEy8-NlgkQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.algorithmist.ru/feeds/4203148571073323376/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.algorithmist.ru/2011/05/its-time-to-move-on.html#comment-form" title="6 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/4203148571073323376?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/4203148571073323376?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/algorithmist/ru/~3/BvEy8-NlgkQ/its-time-to-move-on.html" title="Пока Дойче. Привет Одноклассники!" /><author><name>SergE</name><uri>http://www.blogger.com/profile/01485674727390704780</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://4.bp.blogspot.com/_rst1xzlxZSU/S_t-TztXAKI/AAAAAAAABRs/LZpr_ILRYOU/S220/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-e5GABzCRYMU/TdUSSDV7TTI/AAAAAAAABhU/VLM-1O4r5AM/s72-c/images.jpg" height="72" width="72" /><thr:total>6</thr:total><feedburner:origLink>http://www.algorithmist.ru/2011/05/its-time-to-move-on.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkQGR307fyp7ImA9WhZXE0Q.&quot;"><id>tag:blogger.com,1999:blog-3994427606477253566.post-5452314736244454751</id><published>2011-05-03T06:37:00.003+04:00</published><updated>2011-05-03T06:38:46.307+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-05-03T06:38:46.307+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Data mining" /><category scheme="http://www.blogger.com/atom/ns#" term="R" /><title>Кластерный анализ с примерами на R</title><content type="html">Доброго дня, уважаемый читатель! Перед вами очередной опус из &lt;a href="http://www.algorithmist.ru/search/label/Data%20mining"&gt;серии о data mining&lt;/a&gt;. В прошлый раз я рассказал &lt;a href="http://www.algorithmist.ru/2011/04/nearest-neighbour-with-example-in-r.html"&gt;о методе ближайших соседей&lt;/a&gt;. Сегодня, как логическое продолжение, поговорим о кластерном анализе или кластеризации. С устоявшейся терминологией тут проблемы, т.к. большинство публикаций на английском и приходится придумывать русский эквивалент английским терминам. Потому и я иногда буду тоже скатываться на англицизмы. &lt;br /&gt;
&lt;br /&gt;
Почему это логическое продолжение? Потому что идеи лежащие в основе этих подходов очень похожи. Напомню, что суть метода ближайших соседей состоит в том, что для каждого объекта мы ищем ближайших к нему соседей и на основании имеющихся данных о соседях делаем вывод об исходном объекте, на языке data mining, это называется обучением с учителем. Для того, чтобы этот подход работал, нужно иметь набор тренировочных данных. &lt;br /&gt;
&lt;br /&gt;
А что если у нас есть просто данные и мы ничего не знаем об их структуре? Но зато, у каждого элемента данных есть набор характеристик (например, если речь идет о людях: возраст, пол, образование итп). Так вот, задача кластерного анализа состоит в том, чтобы разбить объекты на группы (кластеры) так чтобы объекты в каждой группе были некоторым образом похожи. Тем самым раскрывается внутренняя структура данных. При этом, нам не требуются тренировочные данные. Такой подход носит название обучения без учителя. &lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
&lt;h4&gt;Предварительная работа&lt;/h4&gt;Рассмотрим, к примеру, набор данных:&lt;br /&gt;
&lt;br /&gt;
&lt;table border="1" cellpadding="0" cellspacing="0" style="border-collapse: collapse;"&gt;&lt;tr&gt;&lt;td&gt;Имя&lt;/td&gt;&lt;td&gt;Возраст&lt;/td&gt;&lt;td&gt;Город&lt;/td&gt;&lt;td&gt;Доход&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Петя&lt;/td&gt;&lt;td&gt;25&lt;/td&gt;&lt;td&gt;Москва&lt;/td&gt;&lt;td&gt;120000&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Вася&lt;/td&gt;&lt;td&gt;34&lt;/td&gt;&lt;td&gt;Киров&lt;/td&gt;&lt;td&gt;45000&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Маша&lt;/td&gt;&lt;td&gt;27&lt;/td&gt;&lt;td&gt;Самара&lt;/td&gt;&lt;td&gt;15000&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Света&lt;/td&gt;&lt;td&gt;36&lt;/td&gt;&lt;td&gt;Нью-Йорк&lt;/td&gt;&lt;td&gt;150000&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Катя&lt;/td&gt;&lt;td&gt;24&lt;/td&gt;&lt;td&gt;Москва&lt;/td&gt;&lt;td&gt;30000&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;&lt;br /&gt;
Каким образом можно разбить эти данные на кластеры? Можно по зарплате: Петя и Света в один кластер, Вася и Катя во второй, а Маша в третий. Можно по возрасту: Петя, Маша и Катя в один, Вася и Света во второй. Можно еще разбить по месту жительства или полу. И всякий раз результат будет получаться разный. Какой результат лучше? Ответить можно только зная задачу, для чего именно проводится анализ. Данные сами по себе никак не определяют возможные группировки. Чтобы получить однозначный результат нам необходимо ввести понятие расстояния. Причем, мы можем использовать сразу несколько характеристик объектов для определения расстояния между ними, например зарплату, возраст и пол. &lt;br /&gt;
&lt;br /&gt;
Существуют различные меры расстояний, но для нас интуитивно понятным является классическое Евклидово расстояние и, справедливости ради, замечу, что его в большинстве задач более чем достаточно. Важно лишь правильно нормализовать данные. Как в нашем примере: зарплаты измеряются в тысячах, а возраст в годах. Непосредственно сравнивать эти две величины нельзя. А вот если предварительно привести их к диапазону от 0 до 1 то они станут вполне сравнимыми. &lt;br /&gt;
&lt;br /&gt;
Таким образом, кластерный анализ, в первую очередь состоит из работы над данными. Требуется выбрать интересующие нас характеристики, нормализовать их и выбрать подходящую меру расстояний. И только после этого можно переходить к алгоритмам кластерного анализа.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Алгоритмы Кластерного Анализа&lt;/h4&gt;Известно множество алгоритмов кластерного анализа. Далеко не полная и не единственная возможная классификация представлена на рисунке ниже.&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-jRddIJI5lh0/Tb6Q2ksPc7I/AAAAAAAABg0/LE3PMMctFJw/s1600/Diagram1.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="132" width="400" src="http://2.bp.blogspot.com/-jRddIJI5lh0/Tb6Q2ksPc7I/AAAAAAAABg0/LE3PMMctFJw/s400/Diagram1.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Итак, первым делом, все алгоритмы кластерного анализа делятся на две группы: иерархические и неиерархические. Первые строят не просто разбиение на классы, а иерархию разбиений. Результатом их работы, как правило, является дендрограмма, на основе которой, пользователь может сам выбрать желаемое разбиение. Неиерархические алгоритмы, напротив, в результате работы выдают некоторое конкретное разбиение и имеют ряд параметров, позволяющих настраивать алгоритм для имеющихся данных. Естественно, вторые работают быстрее чем первые. &lt;br /&gt;
&lt;br /&gt;
Все методы кластеризации работают с данными в виде векторов в многомерном пространстве. Каждый вектор определяется значениями нескольких направлений, а направления и есть известные нам характеристики (пол, возраст, образование). Характеристики могут быть как количественные, так и качественные и искусство специалиста по data mining состоит в том, чтобы правильно отобрать и нормализовать эти характеристики, а потом выбрать подходящую меру расстояний. И только после этого в игру вступают алгоритмы кластеризации.&lt;br /&gt;
&lt;br /&gt;
К числу наиболее популярных, неиерархических алгоритмов относится алгоритм k-средних. Он особенно популярен в силу простоты реализации и скорости работы. Главным его недостатком является сходимость к локальному минимуму и зависимость результата от начального распределения. Кроме того, требуется заранее знать предполагаемое число кластеров k.&lt;br /&gt;
&lt;br /&gt;
Итак, сам алгоритм:&lt;br /&gt;
1) Выбрать k случайных центров в пространстве, содержащем исходные данные&lt;br /&gt;
2) Приписать каждый объект из множества исходных данных кластеру исходя из того, какой центр к нему ближе&lt;br /&gt;
3) Пересчитать центры кластеров используя полученное распределение объектов&lt;br /&gt;
4) Если алгоритм не сошелся, то перейти к п. 2. Типичные критерии схождения алгоритма это либо среднеквадратичная ошибка, либо отсутствие перемещений объектов из кластера в кластер.&lt;br /&gt;
&lt;br /&gt;
Существует множество вариаций этого алгоритма, некоторые из них пытаются выбирать наилучшее начальное распределение, другие позволяют кластерам разбиваться на части и объединяться. Примером такой модификации является алгоритм ISODATA, реализованный в маткаде и многих других математических библиотеках.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Иерархическая кластеризация&lt;/h4&gt;По своему подходу, все алгоритмы иерархической кластеризации делятся на два типа: сверху-вниз и снизу-вверх. Первые начинают с одного большого кластера состоящего из всех элементов, а за тем, шаг за шагом разбивают его на все более и более мелкие кластеры. Вторые, напротив, начинают с отдельных элементов постепенно объединяя их в более и более крупные кластеры. Для пользователя, принципиальной разницы между этими двумя подходами нет, куда более значимо то, каким способом алгоритм пользуется для вычисления расстояний между кластерами. По этому признаку иерархические алгоритмы делятся на алгоритмы с одиночной связью и с полной связью (существуют и другие подходы, но они менее популярны). В алгоритмах с одиночной связью расстоянием между двумя кластерами считается минимальное расстояние между всеми парами элементов из этих двух кластеров. В алгоритмах с полной связью расстоянием считается максимальное расстояние между всеми парами элементов из двух кластеров. &lt;br /&gt;
&lt;br /&gt;
Таким образом, алгоритмы с полной связью склонны находить более компактные кластеры. Алгоритмы с одиночной связью, напротив, склонны выявлять сильно вытянутые и сложны формы. Но часто страдают из-за шумов.&lt;br /&gt;
На рисунке ниже приведен классический пример работы алгоритма одиночной и полной связи. Нам даны два кластера, соединенных "дорожкой шумов". Слева результат работы алгоритма с одиночной связью, а справа алгоритма с полной связью:&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-V_09BkMurhg/Tb1PoXcLhGI/AAAAAAAABgU/tYmEDKxyPkk/s1600/Screenshot-1.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="134" width="400" src="http://4.bp.blogspot.com/-V_09BkMurhg/Tb1PoXcLhGI/AAAAAAAABgU/tYmEDKxyPkk/s400/Screenshot-1.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Видно, что алгоритм с одиночной связь дал не самый адекватный результат. С другой стороны, алгоритмы с полной связью не способны выявить концентрические кластеры, такие как на рисунке:&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-gjBXYiUOtS8/Tb2iMgprNAI/AAAAAAAABgs/9ucQBqMYsT8/s1600/Clusters.PNG" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="216" width="246" src="http://1.bp.blogspot.com/-gjBXYiUOtS8/Tb2iMgprNAI/AAAAAAAABgs/9ucQBqMYsT8/s400/Clusters.PNG" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Таким образом, выбор алгоритма определяется конкретной задачей. Но на практике, кластеры сложной формы встречаются редко, а шумы часто и поэтому чаще применяются алгоритмы с полной связью.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Алгоритмы основанные на теории графов&lt;/h4&gt;На практике, в чистом виде, применяются редко, т.к. их вычислительная сложность не позволяет обрабатывать большие графы. Чаще используются в сочетании с другими алгоритмами, такими как k-средних. Классический пример, это алгоритм минимального покрывающего дерева. Идея состоит в том, чтобы представить весь набор данных в виде графа, вершины которого это наши элементы данных, а вес каждого ребра равен расстоянию между соответствующими элементами (помните, здесь тоже нет никаких чудес, сначала придется определить понятие расстояния). Тогда, мы можем построить минимальное покрывающее дерево на данном графе, а затем последовательно удалять ребра с наибольшим весом. Кластером считается множество элементов, соединенных "остатком" дерева. С каждым убранным ребром, количество кластеров увеличивается. &lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Статистические алгоритмы&lt;/h4&gt;Этот подход очень популярен в области распознавания изображений. Идея такая: предположим, что каждый кластер в наших данных, подчиняется некоторому распределению, как правило предполагается Гаусовское распределение. Далее, методами статистики, мы определяем параметры допустимых распределений и их центры. На этом основан, например, алгоритм &lt;a href="http://en.wikipedia.org/wiki/Expectation-maximization_algorithm"&gt;EM-кластеризации&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Пример:&lt;/h4&gt;&lt;i&gt;&lt;br /&gt;
Для примера будет использоваться программная среда R. Что это такое, где взять и как настроить можно почитать &lt;a href="http://www.algorithmist.ru/2011/03/r-language-for-traders.html"&gt;тут&lt;/a&gt;.&lt;br /&gt;
&lt;/i&gt;&lt;br /&gt;
Как всегда, пример у меня из области финансовых рынков. Допустим, у нас есть акции большого количества разных компаний. Одни из них растут, другие падают и мы накопили большую историю изменения их цен. Можем ли мы на основе этих данных объединить акции в группы? Логично было бы предположить, что акции из одного сектора рынка растут или падают вместе. И было бы логично если полученные группы это как-то отразили.&lt;br /&gt;
&lt;br /&gt;
Для начала, я выкачал котировки примерно 20 российских компаний  из разных областей, за два года с периодом 1 день. Для удобства, все они собраны в одном файле и доступны &lt;a href="https://github.com/Edunov/Samples/blob/master/stocks.csv"&gt;здесь&lt;/a&gt;. Попробуем проанализировать их средствами кластерного анализа. Т.к. цены разных акций отличаются очень сильно, и абсолютная величина цены нам совершенно не интересна, а интересно относительное изменение, приведем все цены к общему знаменателю: а именно, вместо самой цены будем считать логарифмическое изменение цены:&lt;br /&gt;
X&lt;sub&gt;i&lt;/sub&gt; = log(C&lt;sub&gt;i&lt;/sub&gt;) - log(C&lt;sub&gt;i-1&lt;/sub&gt;) ,где C&lt;sub&gt;i&lt;/sub&gt; - цена закрытия в i-ый день&lt;br /&gt;
&lt;br /&gt;
&lt;pre  class="java" name="code"&gt;x &lt;- read.table('C:\\prj\\CNSVN\\stats\\stocks.csv', sep=',', header=TRUE) #читаем файл с ценами 
x &lt;- log(x) #логарифмируем цены
x &lt;- x[-1] #отбросим колонку с датой
x &lt;- apply(x, 2, diff) #считаем разницу между последовательными элементами
x &lt;- t(x) #транспонируем таблицу
kmeans(x, 5, 1000000) #будем разбивать на 5 кластеров, максимум 1000000 иттераций
&lt;/pre&gt;
Получаем результат:
&lt;table border="1" cellpadding="0" cellspacing="0" style="border-collapse: collapse;"&gt;&lt;tr&gt; &lt;td&gt;1&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;5&lt;/td&gt; &lt;/tr&gt;
&lt;td&gt;RASP&lt;/td&gt; &lt;td&gt;AFLT, PMTL, PLZL, MMBM, MTSI, VZRZ&lt;/td&gt; &lt;td&gt;GAZP, LKOH, VTBR, URKA, TRNFP, SIBN, SBER, ROSN, GMKN, SNGS&lt;/td&gt; &lt;td&gt;OGK1, OGK2, OGK5 &lt;/td&gt; &lt;td&gt;KMAZ, AVAZ&lt;/td&gt;
&lt;tr&gt;&lt;/tr&gt;
&lt;/table&gt;Сразу заметна тенденция к группировке в кластеры компаний из одного сектора: автопроизводители попали в пятый кластер, нефтедобывающие компании и крупные банки в третий, все генерирующие компании в четвертый. Если запустить этот алгоритм снова, то результат может получится другой, но тенденция, тем не менее, сохранится.

Почему было решено разбивать именно на 5 кластеров, а не 6 или 4? К сожалению, решение вопроса о количестве кластеров не алгоритмизируется, в конечном счете это всегда должен решать пользователь. Но с использованием иерархической кластеризации это решение можно отложить на потом:

&lt;pre  class="java" name="code"&gt;x &lt;- read.table('C:\\prj\\CNSVN\\stats\\stocks.csv', sep=',', header=TRUE) #читаем файл с ценами 
x &lt;- log(x) #логарифмируем цены
x &lt;- x[-1] #отбросим колонку с датой
x &lt;- apply(x, 2, diff) #считаем разницу между последовательными элементами
x &lt;- t(x) #транспонируем таблицу

hc &lt;- hclust(dist(x))

plot(hc)

&lt;/pre&gt;
Вот за что я люблю R! Нам понадобилось заменить всего одну строчку в прежнем примере и получилось следующая картинка:

&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-vmrQCfTYsf8/Tb2cNupC1nI/AAAAAAAABgc/AGh13drzvZI/s1600/Screenshot.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="347" width="400" src="http://4.bp.blogspot.com/-vmrQCfTYsf8/Tb2cNupC1nI/AAAAAAAABgc/AGh13drzvZI/s400/Screenshot.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
На этой картинке уже гораздо лучше видны традиционные сектора нашей экономики. Видны и выколотые точки. Выколотые точки часто могут стать предметом особого исследования, ведь если компания ведет себя не как все в её отрасли, значит она что-то делает по-другому. Знание подобной информации может дать дополнительное преимущество в торговле.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3994427606477253566-5452314736244454751?l=www.algorithmist.ru' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/algorithmist/ru/~4/1UV3fJm2GLk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.algorithmist.ru/feeds/5452314736244454751/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.algorithmist.ru/2011/05/clustering-with-example-in-r.html#comment-form" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/5452314736244454751?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/5452314736244454751?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/algorithmist/ru/~3/1UV3fJm2GLk/clustering-with-example-in-r.html" title="Кластерный анализ с примерами на R" /><author><name>SergE</name><uri>http://www.blogger.com/profile/01485674727390704780</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://4.bp.blogspot.com/_rst1xzlxZSU/S_t-TztXAKI/AAAAAAAABRs/LZpr_ILRYOU/S220/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-jRddIJI5lh0/Tb6Q2ksPc7I/AAAAAAAABg0/LE3PMMctFJw/s72-c/Diagram1.png" height="72" width="72" /><thr:total>4</thr:total><feedburner:origLink>http://www.algorithmist.ru/2011/05/clustering-with-example-in-r.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A08FRnc5eip7ImA9WhZRF0k.&quot;"><id>tag:blogger.com,1999:blog-3994427606477253566.post-7334871538960984838</id><published>2011-04-14T06:55:00.002+04:00</published><updated>2011-04-14T06:56:57.922+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-04-14T06:56:57.922+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Data mining" /><category scheme="http://www.blogger.com/atom/ns#" term="R" /><title>Метод поиска ближайшего соседа с примерами на R</title><content type="html">Продолжаю серию про методы и алгоритмы &lt;a href="http://ru.wikipedia.org/wiki/%D0%98%D0%BD%D1%82%D0%B5%D0%BB%D0%BB%D0%B5%D0%BA%D1%82%D1%83%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85"&gt;data mining&lt;/a&gt;. &lt;a href="http://www.algorithmist.ru/2011/04/linear-regression-with-examples-in-r.html"&gt;Предыдущая статья была посвящена линейной регрессии&lt;/a&gt;. Эта статья посвящается методу ближайшего соседа. Идея метода ближайшего соседа очень проста на интуитивном уровне. Давайте рассмотрим простой пример. &lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Простой пример метода ближайшего соседа&lt;/h4&gt;Рассмотрим зарплаты людей, в зависимости от их места жительства.  Предположим, что вы живете в центре Москвы и что рядом с вами живут люди с зарплатой 150 тыс рублей.  Тогда, можно предположить, что и ваша зарплата близка к этой величине. Если же вы проживаете в &lt;a href="http://maps.yandex.ru/-/CBQY7OOd"&gt;поселке Новотагилка Челябинской области&lt;/a&gt;, и зарплаты ваших соседей около 3 тыс рублей, то скорее всего и ваша зарплата близка к 3 тыс рублей. Естественно, что здесь нет 100% точной зависимости, кто-то может жить в центре Москвы на пенсию в 6 тыс, а кто-то в Новотагилке зарабатывать 50. Но если единственная информация, которой мы владеем о человеке является его место жительства, то мы можем воспользоваться приведенным подходом для оценки его доходов. &lt;br /&gt;
&lt;br /&gt;
Аналогично работает и метод ближайшего соседа&lt;a name='more'&gt;&lt;/a&gt;, разница лишь в том, что вместо коллег ищутся некоторым образом схожие элементы, причем схожесть определяется множеством различных факторов. Если, как и в нашем примере, речь идет о зарплатах, то могут быть использованы так же данные о профессии, образовании, стаже работы, поле и возрасте.  Дополнительные данные, с одной стороны, усложняют задачу вводя дополнительные измерения. С другой стороны, они позволяют гораздо более точно предсказать результат. Например, если вы знаете что данный  человек программист, работает и живет в москве, имеет опыт около 10 лет, то предсказать его зарплату можно значительно точнее.&lt;br /&gt;
&lt;h4&gt;А в попугаях я гораздо длиннее &lt;/h4&gt;Объекты, которые находятся близко друг другу склонны иметь близкие значения предсказываемых величин — это главная идея метода ближайших соседей. Если вы знаете предсказываемое значение для одного из элементов, вы можете предсказать его и для ближайших соседей.&lt;br /&gt;
&lt;br /&gt;
Основная сложность в применении этого метода состоит в том, что нам необходимо четко определить понятие «близости».  Это, в свою очередь, приводит нас к понятию расстояния между элементами. На практике можно использовать различные виды расстояний, но чаще всего достаточно простого евклидова. При выборе мерила расстояний важно учитывать единицы измерения. Ведь нам, возможно, придется включить в одну величину свойства разной природы, например  доход (деньги), возраст (годы), вес (кг) итд. Кроме того, например деньги можно мерить в рублях, копейках, долларах, золотых слитках и много чем еще, в зависимости от единицы измерения расстояния между элементами будут получаться разные. Выбор единиц измерения невозможно автоматизировать, эта задача остается за пользователем алгоритма и зависит от конкретной области применения и имеющихся данных.&lt;br /&gt;
&lt;h4&gt;А если нет элемента?&lt;/h4&gt;Что делать, например, если нам даны  временные данные, такие как котировки на бирже. И задача в том, чтобы предсказать изменение цены через некоторый промежуток времени. В этом случае мы не можем определить даже понятие элемента, о понятии близости и говорить не приходится.&lt;br /&gt;
&lt;br /&gt;
Обычно эта проблема решается следующим образом, выбирается некоторое последовательное число котировок. Например 10, первые 9 из них объявляются предикторами, а последнее искомым значением. Получается что-то вроде движущегося окна. Например, если у нас есть 100 последовательных значений, мы можем сначала взять значения 1-9 для тренировки, 10 — как искомое. Потом значения 2-10 для тренировки, 11 — искомое. Естественно, что при таком разбиении часть данных будет перекрываться, но искомое значение, каждый раз новое. &lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Метод k ближайших соседей&lt;/h4&gt;Главная проблема метода ближайшего соседа это его чувствительность к  выбросам (outliers) в тренировочных данных.  Возьмем пример на картинке:&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-ZbxeGF4GQbY/TaWsSLslgHI/AAAAAAAABfw/5IfHB0dAWQE/s1600/image001.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="399" width="400" src="http://1.bp.blogspot.com/-ZbxeGF4GQbY/TaWsSLslgHI/AAAAAAAABfw/5IfHB0dAWQE/s400/image001.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
У нас есть два класса элементов, красные и зеленые. И есть неопознанный элемент (отмечен кружочком), он явно лежит в области красных элементов. Но был классифицирован как зеленый только потому что находится рядом с выбросом (другим зеленым элементом)&lt;br /&gt;
&lt;br /&gt;
Очевидный способ улучшить положение - учитывать мнение нескольких ближайших соседей вместо одного. Это улучшение приводит нас к другому методу: метод k ближайших соседей. Идея состоит в том, чтобы взять не просто ближайшего соседа, а k ближайших соседей и предсказывать значения искомой величины на основе взвешенной оценки. &lt;br /&gt;
&lt;br /&gt;
Метод k ближайших соседей, так же, позволяет оценить точность прогноза. Если все k ближайших соседей имеют одно и то же значение, то шансы что проверяемый объект будет иметь такое же значение очень высоки. Если же среди этих объектов 40% имеют одно, а 60% другое значение, то вероятность правильного прогноза существенно снижается. Таким образом мы можем использовать распределение голосов между соседями для оценки точности прогноза.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;Пример&lt;/h4&gt;&lt;br /&gt;
Пример сегодня будет на основе... биржевых котировок. Что может быть жизненней? Не считать же нам &lt;a href="http://en.wikibooks.org/wiki/Data_Mining_Algorithms_In_R/Classification/kNN#Case_Study"&gt;пестики и тычинки ирисов&lt;/a&gt; – кстати, если серьезно, по ссылке хороший пример применения метода ближайшего соседа.&lt;br /&gt;
&lt;br /&gt;
Итак, задача: предсказать движения индекса &lt;a href="http://en.wikipedia.org/wiki/S&amp;P500"&gt;S&amp;P500&lt;/a&gt; по его изменениям за прошлые 4 дня. Изменения считаем от закрытия к закрытию.  К счастью для нас, ничего кроме R и нескольких дополнительных модулей к нему не нужно:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="java" name="code"&gt;#раскоментировать если библиотека knnflex или quanmod не установлена
#install.packages("knnflex")
#install.packages("quantmod")

library(knnflex)
library(quantmod)

#получаем данные по индексу S&amp;P500 с 2007 года
getSymbols("^GSPC", from="2007-10-01")

#считаем доходность по дням от закрытия до закрытия
roc &lt;- ROC(Cl(GSPC)) 

# записываем в каждый ряд x изменения за прошедшие 4 дня
x &lt;- cbind(lag(roc), lag(roc, k=2), lag(roc, k=3), lag(roc, k=4))

# в у записываем то, что нам надо предсказать
y &lt;- roc

#тренировочные данные - это все данные за исключением последних 30 дней
train &lt;- 1:(length(roc)-30)

#проверочные данные это все остальные данные, т.е. данные за последние 30 дней.
test &lt;- (1:(length(roc)))[-train]

#Это самая важная строчка! 
#Считаем расстояние между каждой парой точек, в нашем случае это просто Евклидово расстояние 
#в 4-х мерном пространстве 
kdist &lt;- knn.dist(x)

#считаем методом k-ближайших соседей результат
#первый параметр – индексы тренировочных данных
#второй параметр – индексы тестовых данных
#третий параметр – вектор с результатами для тренировочных данных
#четвертый параметр – матрица расстояний посчитаная строчкой выше
#k=3 – используем 3 ближайших соседа
preds &lt;- knn.predict(train,test,y,kdist, k=3)

#связываем результат с исходными данными
results &lt;- cbind(preds, y[test])

#считаем доходность за месяц
last(cumprod(1+ results[,2] * ifelse(results[,1]&lt; 0, -1, 1)))
&lt;/pre&gt;
Результат работы:
&lt;pre&gt;2011-04-12      1.024020
&lt;/pre&gt;Т.е. доходность за 30 дней составила 2.4%.&lt;br /&gt;
&lt;br /&gt;
Вот такой простой и интуитивно понятный метод позволяет иногда давать достаточно точные прогнозы.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3994427606477253566-7334871538960984838?l=www.algorithmist.ru' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/algorithmist/ru/~4/8JJ8Iq5wsGw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.algorithmist.ru/feeds/7334871538960984838/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.algorithmist.ru/2011/04/nearest-neighbour-with-example-in-r.html#comment-form" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/7334871538960984838?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/7334871538960984838?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/algorithmist/ru/~3/8JJ8Iq5wsGw/nearest-neighbour-with-example-in-r.html" title="Метод поиска ближайшего соседа с примерами на R" /><author><name>SergE</name><uri>http://www.blogger.com/profile/01485674727390704780</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://4.bp.blogspot.com/_rst1xzlxZSU/S_t-TztXAKI/AAAAAAAABRs/LZpr_ILRYOU/S220/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/-ZbxeGF4GQbY/TaWsSLslgHI/AAAAAAAABfw/5IfHB0dAWQE/s72-c/image001.png" height="72" width="72" /><thr:total>4</thr:total><feedburner:origLink>http://www.algorithmist.ru/2011/04/nearest-neighbour-with-example-in-r.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkIDRX06eSp7ImA9WhdXEUU.&quot;"><id>tag:blogger.com,1999:blog-3994427606477253566.post-8271707721307197989</id><published>2011-04-12T06:51:00.005+04:00</published><updated>2011-08-24T14:09:34.311+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-08-24T14:09:34.311+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Data mining" /><category scheme="http://www.blogger.com/atom/ns#" term="R" /><title>Линейная регрессия с примерами на R</title><content type="html">Меня давно преследует идея пройтись по ключевым алгоритмам &lt;a href="http://ru.wikipedia.org/wiki/%D0%98%D0%BD%D1%82%D0%B5%D0%BB%D0%BB%D0%B5%D0%BA%D1%82%D1%83%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%B0%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85"&gt;data mining&lt;/a&gt;, систематизировать имеющиеся знания и составить некий обзор с примерами. Собственно, этой статьей и ознаменую начало данной серии. Для примеров будет использоваться &lt;a href="http://www.r-project.org/"&gt;программная среда R&lt;/a&gt;, о которой я уже писал &lt;a href="http://www.algorithmist.ru/2011/03/r-language-for-traders.html"&gt;в одном из прошлых постов&lt;/a&gt;.    &lt;h4&gt;Линейная регрессия &lt;/h4&gt;Строго говоря, линейная регрессия не является алгоритмом data mining. Это один из методов пришедших из статистики. В статистике, под регрессией,  обычно подразумевают прогнозирование, в той или иной форме.  Существует множество различных типов регрессий, но в основе любого из них лежит одна и та же идея: построить модель, связывающую предсказываемое значение с  исходными данными (предикторами), минимизируя ошибку. &lt;a name='more'&gt;&lt;/a&gt; &lt;br /&gt;
&lt;br /&gt;
Линейная регрессия это простейший вариант регрессии.  Рассмотрим, для примера, линейную регрессию с одним предиктором  и одним предсказываемым значением. Такую регрессию легко нарисовать на графике X-Y. Для этого по оси абсцисс X мы отмечаем значения предиктора, а по оси ординат Y значения предсказываемой величины. Тогда простая линейная регрессия это прямая, проведенная таким образом, чтобы минимизировать расхождение между истинными значениями предсказываемой величины и точками на линии, соответствующими значениям предикторов. &lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-JjjxmxVUKYo/TaMR_pCL4lI/AAAAAAAABfg/iK2PXu_iVkQ/s1600/image001.png" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="319" width="320" src="http://3.bp.blogspot.com/-JjjxmxVUKYo/TaMR_pCL4lI/AAAAAAAABfg/iK2PXu_iVkQ/s320/image001.png" /&gt;&lt;/a&gt;&lt;/div&gt;На языке математики, мы можем описать нашу линейную модель в виде уравнения:  Y = a+b*X, где X – это предиктор или исходные данные, а Y – это предсказываемая величина. А саму задачу переформулировать в виде: найти коэффициенты a и b минимизирующие величину ошибки. &lt;br /&gt;
Когда модель построена, а коэффициенты найдены мы можем использовать полученное уравнение для предсказания неизвестных нам значений.  &lt;br /&gt;
&lt;h4&gt;Пример:&lt;/h4&gt;Возьмем пример из области финансов. Ни для кого не секрет, что цены акций нефтедобывающих компаний сильно зависят от цен на нефть. В нулевом приближении, мы можем считать что &lt;br /&gt;
&lt;br /&gt;
&lt;i&gt; Цена Акции = K * Цена барреля нефти + Некая константа &lt;/i&gt; &lt;br /&gt;
&lt;br /&gt;
Если мы можем определить константу и коэффициент, то мы можем по цене на нефть предсказывать цену акции. Если возникает отклонение, то можно купить &lt;a href="http://en.wikipedia.org/wiki/Spread_trade"&gt;спред &lt;/a&gt;и на этом заработать,  в теории, естественно. &lt;br /&gt;
&lt;br /&gt;
А теперь практика, возьмем R, и проведем линейную регрессию цен акций компании Роснефть по цене на нефть. Исходные данные можно взять &lt;a href="https://github.com/Edunov/Samples/blob/master/rosn.txt"&gt;здесь&lt;/a&gt;:&lt;br /&gt;
&lt;pre class="java" name="code"&gt;data &lt;- read.csv('C:\\ rosn.txt', sep='\t') #читаем данные из файла
fit &lt;- lm(data$ROSN ~ data$BRN ) #строим линейную регрессию
summary(fit) #печатаем результат
&lt;/pre&gt;
Результат:
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;img border="0"  src="http://1.bp.blogspot.com/-XQfVosBf0Z0/TaMWpo8NZgI/AAAAAAAABfo/dujhgYXZt9Y/s1600/image003.png" /&gt;
&lt;/div&gt;На рисунке, интересующие нас коэффициенты, обозначены (1). Подставив их в уравнение регрессии, получим выражение: 


&lt;i&gt;ROSN = 61.405 + 1.944*Brent &lt;/i&gt;


Помимо значений, собственно, коэффициентов, R показывает нам величины ошибок, или стандартного отклонения, для каждого коэффициента. Но это не все...  


Например, нам может быть интересно, объясняют ли вообще хоть что-нибудь наши коэффициенты. Чтобы проверить это, мы, выдвигаем нулевую гипотезу, что, к примеру, коэффициент 61.405 является лишь результатом погрешности и его значением можно пренебречь. Для проверки такой гипотезы, используется &lt;a href="http://ru.wikipedia.org/wiki/T-%D0%BA%D1%80%D0%B8%D1%82%D0%B5%D1%80%D0%B8%D0%B9_%D0%A1%D1%82%D1%8C%D1%8E%D0%B4%D0%B5%D0%BD%D1%82%D0%B0"&gt;t-критерий Стьюдента&lt;/a&gt;. Здесь R за нас делает всю работу, вычисляя как саму величину t так и степень значимости нашей гипотезы Pr(&gt;|t|). Так, в нашем случае величина (2) 0.0036 означает что мы на 100*(1-0.0036) = 99.64% уверены в том, что свободный член в нашем выражении отличен от нуля.


Далее мы можем проверить, насколько точно наша модель описывает данные. Для этого используются коэффициенты R&lt;sup&gt;2&lt;/sup&gt;  (3). Чем ближе величина этих значений к 1, тем лучше. 1 это идеальный результат, означающий, что модель на 100% описывает данные. 


И, наконец, последнее, что мы можем проверить, это то, насколько предсказываемая величина зависит от предикторов. Для этого выдвигается нулевая гипотеза, что предсказываемая величина вообще не зависит от предикторов. Для этой гипотезы определяется &lt;a href="http://en.wikipedia.org/wiki/P-value"&gt;p-значение&lt;/a&gt; (4). В нашем случае, оно получилось равным 2.65*10^-8. Т.е. мы можем быть уверенны на  99.99999735%, что предсказываемая величина действительно зависит от предикторов. Обычно, имеет смысл смотреть на этот параметр в первую очередь, ведь он определяет, насколько вообще наша модель адекватна.


Да, кстати, график чуть выше, как раз показывает наши данные и результат линейной регрессии. 


&lt;h4&gt;Что если мои данные не описываются прямой линией? &lt;/h4&gt;Линейная регрессия с одним предиктором это простейший вариант регрессии, на практике возможен целый ряд усложнений позволяющих решать гораздо более сложные задачи:


1) добавление нескольких предикторов  – дополнительные предикторы могут добавить в модель  больше информации и, тем самым, улучшить результаты. Уравнение линейной регрессии, в таком случае может иметь следующий вид: 
Y = a + b1*X1 + … bn*Xn 


2)предварительное преобразование предикторов - Возведение предикторов в степень либо извлечение корней является одним из способов увеличить возможности линейной регрессии. На практике, нередко приходится проводить большое количество различных преобразований с целью выяснить, какое из них даст наилучшую оценку.


3)в уравнение модели могут быть добавлены члены содержащие произведение предикторов


4)часто возникает задача предсказания вероятности возникновения события. Т.е. предсказываемая величина принимает лишь два значения 1 и 0 или да и нет. Для решения такого рода задач применяется &lt;a href="http://ru.wikipedia.org/wiki/%D0%9B%D0%BE%D0%B3%D0%B8%D1%81%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B0%D1%8F_%D1%80%D0%B5%D0%B3%D1%80%D0%B5%D1%81%D1%81%D0%B8%D1%8F"&gt;логистическая регрессия&lt;/a&gt;. 


К счастью, все эти и множество других дополнений реализованы в R и готовы к использованию. Информацию о том, как ими пользоваться можно почерпнуть набрав 
&lt;pre class="java" name="code"&gt;&gt; ?lm
&lt;/pre&gt;в командном интерфейсе R.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3994427606477253566-8271707721307197989?l=www.algorithmist.ru' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/algorithmist/ru/~4/BQZhKMzVKrg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.algorithmist.ru/feeds/8271707721307197989/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.algorithmist.ru/2011/04/linear-regression-with-examples-in-r.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/8271707721307197989?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/8271707721307197989?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/algorithmist/ru/~3/BQZhKMzVKrg/linear-regression-with-examples-in-r.html" title="Линейная регрессия с примерами на R" /><author><name>SergE</name><uri>http://www.blogger.com/profile/01485674727390704780</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://4.bp.blogspot.com/_rst1xzlxZSU/S_t-TztXAKI/AAAAAAAABRs/LZpr_ILRYOU/S220/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-JjjxmxVUKYo/TaMR_pCL4lI/AAAAAAAABfg/iK2PXu_iVkQ/s72-c/image001.png" height="72" width="72" /><thr:total>0</thr:total><feedburner:origLink>http://www.algorithmist.ru/2011/04/linear-regression-with-examples-in-r.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkIMRn44fyp7ImA9WhZSFk4.&quot;"><id>tag:blogger.com,1999:blog-3994427606477253566.post-212363428303365597</id><published>2011-04-01T08:03:00.000+04:00</published><updated>2011-04-01T08:03:07.037+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-04-01T08:03:07.037+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="1 Апреля" /><title>Зачем Goldman Sachs крысы или генетические алгоритмы и нейронные сети на службе в инвестиционных банках</title><content type="html">&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://3.bp.blogspot.com/-BSs0VXMvBPo/TZRPjSJj35I/AAAAAAAABfI/zjCsBV3yHMI/s1600/rat1.jpg" imageanchor="1" style="clear:left; float:left;margin-right:1em; margin-bottom:1em"&gt;&lt;img border="0" height="248" width="320" src="http://3.bp.blogspot.com/-BSs0VXMvBPo/TZRPjSJj35I/AAAAAAAABfI/zjCsBV3yHMI/s320/rat1.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;a href="http://en.wikipedia.org/wiki/Algo_trading"&gt;Алгоритмический трейдинг&lt;/a&gt; в наше время составляет более 80% сделок практически на всех биржах. Это значит, что среднестатистический трейдер это уже не безумный человек, бегающий с телефоном, и кричащий "покупаем - продаем", это тихо жужащий в углу сервер с толстым каналом в интернет и надежной системой охлаждения. И это уже давно ни для кого не секрет. Но так было до недавнего времени. Последние годы ситуация стремительно меняется...&lt;br /&gt;
&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Крупнейшие инвестиционные банки годами нанимали физиков и математиков, чтобы те разрабатывали им алгоритмы, основанные на цифровой обработке сигналов или нейронных сетях. А вы знаете, в чем самая большая проблема нейронных сетей? Даже самые мощные искуственные нейронные сети не способны превзойти по интеллекту мышь или крысу. Это тяжело принять, но это так, искуственный интеллект все еще в зачаточном состоянии. И инвестиционные банки это знают гораздо лучше чем кто-либо, но недавно они нашли выход. &lt;br /&gt;
&lt;br /&gt;
Вместо бесконечной армии безумно дорогих трейдеров, программистов и математиков, которым надо платить большую зарплату, бонус и которых могут переманить конкуренты, инвестиционные банки берут на работу крыс. Да да, самых обыкновенных крыс. &lt;br /&gt;
&lt;br /&gt;
Существуют отточенные методики тренировки: крыса сажается перед торговым терминалом, ей показываются текущие курсы, а перед ней ставится две кнопки: купить и продать. Когда крыса совершат удачую сделку, ей автоматически выдается кусочек еды. Когда крыса ошибается её бьет током. Поверьте, крыса, мотивированная ударами тока, это гораздо дешевле и эффективней чем трейдеры, мотивированные годовым бонусом.&lt;br /&gt;
&lt;br /&gt;
Таким образом, вместо ненадежных искуственных нейронных сетей, банки стали использовать естественные нейронные сети в мозгу крыс. И это уже дает свои результаты. Прибыль Goldman Sachs в прошлом году превзошла 8 млрд долларов.&lt;br /&gt;
&lt;br /&gt;
Но технологии, однако, не стоят на месте. Вы слышали про &lt;a href="http://ru.wikipedia.org/wiki/%D0%93%D0%B5%D0%BD%D0%B5%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC"&gt;генетические алгоритмы&lt;/a&gt;? Генетические алгоритмы были придуманы природой и заимствованы человеком. Но нет ничего эффективней самой природы. Следующим шагом в алгоритмическом трейдинге стала генетическая селекция крыс. Лучших крыс-трейдеров принуждают спариваться и приносить потомство. Среди потомства снова отбираются лучшие трейдеры и спариваются и так до бесконечности. Таким образом уже через 5-6 поколений получаются крысы заточенные под тот или иной финансовый инструмент. Одни способны безупречно торговать индексом S&amp;amp;P500, другие фьючерсами на золото, третьи валютами на форексе. &lt;br /&gt;
&lt;br /&gt;
Естественно все эти методики держатся в секрете от мировой общественности. Банки опасаются гнева со стороны "зеленых", судебных исков за жестокое обращение с животными и развития домашнего крысиного трейдинга. Поэтому, они по-прежнему нанимают человекотрейдеров и программистов чтобы создать иллюзию работы, но реально основная масса денег зарабатывается тренированными и генетически-отобранными крысами. &lt;br /&gt;
&lt;br /&gt;
Коллеги, не останемся же и мы равнодушными, почему Россия опять позади планеты всей, это при том, что по показателю ККНД (количество крыс на душу населения) мы лидируем? Пока еще не позно, начинаем собирать крыс и сажать их за наши компьютеры. Будущие &lt;a href="http://ru.wikipedia.org/wiki/%D0%A3%D0%BE%D1%80%D1%80%D0%B5%D0%BD_%D0%91%D0%B0%D1%84%D1%84%D0%B5%D1%82"&gt;Уоррен Баффеты&lt;/a&gt; сейчас мечутся по подворотням и подвалам, не упустите свой шанс!


&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="http://3.bp.blogspot.com/-YHR8z6L313o/TZRPq4iVU3I/AAAAAAAABfQ/Dv2NQnZTsTk/s1600/lab-rat.jpg" imageanchor="1" style="margin-left:1em; margin-right:1em"&gt;&lt;img border="0" height="286" width="243" src="http://3.bp.blogspot.com/-YHR8z6L313o/TZRPq4iVU3I/AAAAAAAABfQ/Dv2NQnZTsTk/s320/lab-rat.jpg" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3994427606477253566-212363428303365597?l=www.algorithmist.ru' alt='' /&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/algorithmist/ru/~4/CuLG6ob3ojQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.algorithmist.ru/feeds/212363428303365597/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.algorithmist.ru/2011/04/goldman-sachs.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/212363428303365597?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/3994427606477253566/posts/default/212363428303365597?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/algorithmist/ru/~3/CuLG6ob3ojQ/goldman-sachs.html" title="Зачем Goldman Sachs крысы или генетические алгоритмы и нейронные сети на службе в инвестиционных банках" /><author><name>SergE</name><uri>http://www.blogger.com/profile/01485674727390704780</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="28" height="32" src="http://4.bp.blogspot.com/_rst1xzlxZSU/S_t-TztXAKI/AAAAAAAABRs/LZpr_ILRYOU/S220/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/-BSs0VXMvBPo/TZRPjSJj35I/AAAAAAAABfI/zjCsBV3yHMI/s72-c/rat1.jpg" height="72" width="72" /><thr:total>2</thr:total><feedburner:origLink>http://www.algorithmist.ru/2011/04/goldman-sachs.html</feedburner:origLink></entry></feed>

