Fuzzyな結合

たとえば,財務データを使った投資モデルを考えるとき,決算日から即日データが更新されるわけではないので発表までのラグを考慮する必要がある.
考慮しないで捕らぬ狸の皮算用を喜ぶのはLook-aheadバイアス(先読みバイアス)とよび,その筋の分析屋に配属された新人がよく陥りがちな罠である.

ほかにもマーケットに絡むバイアスの例として,たとえばゴイスーにイケイケなマーケットのトレーディングモデルのバックテストを考える.
種々の事情により,直近の投資ユニバースしかとれなかったとする.
そのなかから,モメンタムトレードのバックテストをすると,オペレーションじたいは正確であっても結果のChartがExplodeする.
ユニバース指数自体はたいしたことないのに.

慣れると自分は天才でないことがわかるのだが,ごく希に有頂天になって周囲に自分の才能を吹聴する輩がいる.
これが,Survivorshipバイアスである.オマエは天才ではない.愚かなだけだ.
そんな報告を受けたら,そのチャートを元に個人トレーダになることを勧めることが周囲にも会社にも貢献するにちがいない.

そう,経済の発展と繁栄の背後には,死屍累々がいることを忘れてはならない.
USAとかJAPANとかUSJとか,あの頃が忘れられつつあるときに落とし穴がある.


閑話休題,月次データと四半期データがあったとき,これをうまいことバインドさせる方法は次の通り.
dd1にDATE,valueからなる月次データと,(めんどくさいから)そこから3ヵ月取り出したデータをくっつける.
普通に***_joinすると一致行しか結合されずにほかはNAになるんだけど,fuzzyjoinなるものをつかうと大なり,小なりで結合できる.

さいごにunique()でダブりをとらないといけないのだが,なぜそうなるかとか,そういう話はわかりません.

> require(tidyverse)
> require(fuzzyjoin)
> # Fuzzyな結合
> dd1 <- tibble(DATE=c(200101:200512))
> dd1 %>% mutate(value=runif(412), 
+                MM=DATE-round(DATE/100,0)*100) %>% 
+   filter(MM<=12 & MM>0) %>% 
+   select(-MM) -> dd1
> dd1 %>% 
+   mutate(MM=DATE%%3) %>% 
+   filter(MM==0) %>% 
+   select(-MM) %>% 
+   rename(DATE1=DATE, value1=value) %>% 
+   mutate(DATE2=lead(DATE1)) -> dd2
> 
> dd2 %>% 
+   fuzzy_right_join(dd1, .,
+                    by = c("DATE" = "DATE1", "DATE" = "DATE2"),
+                    match_fun = list(`>=`, `<`)) %>%
+   unique()
# A tibble: 57 × 5
     DATE      value  DATE1     value1  DATE2
    <int>      <dbl>  <int>      <dbl>  <int>
1  200103 0.90545862 200103 0.90545862 200106
2  200104 0.51608878 200103 0.90545862 200106
3  200105 0.81122443 200103 0.90545862 200106
4  200106 0.06892801 200106 0.06892801 200109
5  200107 0.25179652 200106 0.06892801 200109
6  200108 0.09716054 200106 0.06892801 200109
7  200109 0.45138717 200109 0.45138717 200112
8  200110 0.60871467 200109 0.45138717 200112
9  200111 0.58150930 200109 0.45138717 200112
10 200112 0.37703122 200112 0.37703122 200202
# ... with 47 more rows

データは平等

ノンプロがRで株価とかファイナンスの分析をするとき,quantmodパッケージを使うのは常套手段.
そのあとPerformanceAnalyticsパッケージとかでポートフォリオ分析というかバックテストをすることが多いと思う.
そんな一連の流れをdplyrチェーンで成し遂げてしまう恐ろしいパッケージが存在するらしい.

本記事はこちらの借景.とはいえ,丸パクリDeNAいと思う.
www.business-science.io

コピペできた方がいいでしょ?以下,そのままRstudioに貼り付けて結果を確認してください.

install.packages("tidyquant")
require(tidyquant)

# FB, AMZN, NFLX, GOOGの4本値と修正株価,出来高が入ったテストファイル
data(FANG)
FANG

# PerformanceAnalysisの関数がtq_performanceで呼び出せる
FANG %>%
  group_by(symbol) %>%
  # 調整済終値を日次リターンに変換
  tq_transmute(ohlc_fun   = Ad,
               mutate_fun = periodReturn,
               period     = "daily") %>%
  # ベンチマークNULLで日次リターンのSharpeRatioを計算
  # それぞれの引数はperformanceAnalyticsとおなじ
  # FUN="Stdev"は,そのままだとES/SD/VaRのシャープ比が出るので,SDSharpeを出すため
  tq_performance(Ra              = daily.returns,
                 Rb              = NULL,
                 performance_fun = SharpeRatio,
                 Rf              = 0,
                 p               = 0.95,
                 FUN             = "StdDev")

つぎは月次リバランスでポートフォリオを作ってみる.

# リバランスウエイトを設定
weights <- c(0.50, 0.25, 0.25, 0.00)

FANG %>%
  group_by(symbol) %>%
  tq_transmute(ohlc_fun   = Ad,
               mutate_fun = periodReturn,
               # 月次リバランスなので
               period     = "monthly") %>%
  tq_portfolio(assets_col  = symbol,
               returns_col = monthly.returns,
               weights     = weights) -> FANG_portfolio

FANG_portfolio

# 初期投資額
init.investment <- 10000

# チャートを描く.geom_smoothで平滑化ラインを挿入
FANG_portfolio %>%
  mutate(wealth.index = init.investment * cumprod(1 + portfolio.returns)) %>%
  ggplot(aes(x = date, y = wealth.index)) +
  geom_line(size = 2, color = palette_light()[[3]]) +
  geom_smooth(method = "loess") +
  labs(title = "Individual Portfolio: Comparing the Growth of $10K",
       subtitle = "Quickly visualize performance",
       x = "Year|Month-end", y = "Investment Value") +
  theme_tq() +
  scale_y_continuous(labels = scales::dollar)

# パフォーマンスをテーブルで出力
FANG_portfolio %>%
  tq_performance(Ra = portfolio.returns,
                 Rb = NULL,
                 performance_fun = table.Stats)

株価を指数表示にしてプロットしたいこともありましょう.

FANG %>%
  group_by(symbol) %>%
  tq_transmute(ohlc_fun   = Ad,
               mutate_fun = periodReturn,
               period     = "monthly") %>%
  mutate(wealth.index = 10000 * cumprod(1 + monthly.returns)) %>%
  ggplot(aes(x = date, y = wealth.index, color = symbol)) +
  geom_line(size = 1.5) +
  labs(title = "Stocks: Comparing the Growth of $10K",
       subtitle = "New theme for financial visualizations",
       x = "", y = "Investment Value") +
  scale_y_continuous(labels = scales::dollar) +
  theme_tq() +
  scale_color_tq(theme = "light")

とんでもない.財務諸表やらバリュエーションやらが手に入る
getの引数はhelp参照.

AAPL_data <- tq_get("AAPL", get = c("key.ratios", "key.stats"))
AAPL_data

# key.ratioの項目をextendする
AAPL_data %>% 
  unnest(key.ratios)
AAPL_data %>%
  unnest(key.ratios) %>%
  unnest(data)

# key.statsの項目をextendする
AAPL_data %>% 
  unnest(key.stats, .drop = TRUE) %>% t()

ここからが恐ろしい.指数構成銘柄もとれる!!

tq_index("DJI")

# とれる指数リストはこちら(いまのところ米国指数だけ)
tq_index_options()

取引所上場銘柄のリストも手に入る!!

tq_exchange("NASDAQ")

# とれる取引所リストはこちら(今のところ米国だけ)
tq_exchange_options()

すごくね!?

このパッケージでは,ソースはUS Yahooとか,Morningstarとか,基本的に米国のデータしか扱えないところが残念.
ただ,ちかいうちに誰かがYahoo Japanとかから引っ張ってこれるような仕組みを作るのでしょう.
こうやって簡単にプログラムできて,データが自由に使えるようになると,気合いがある人はバランスファンドの1%近い信託報酬を節約できるようになるのでしょう.
すでに,米国ではそうだってことですね.

一方,こういうパッケージに限って,日本のプロの方々は社内のしがらみで会社のPCから使えなかったりするらしい.

Rで英文テキストマイニング

ここ数年,特に英文のテキストマイニング環境の整備が著しい.
Rでも,tidytextパッケージなんて白眉なものも出てきているし.
tidytextは,ここで素人がいろいろ言うよりも,こちらがおすすめ.
qiita.com

ここでは,ネットサーフィンしていて知った恐ろしく簡単なOpiniton miningの野良パッケージでセンチメント分析をする.
ちなみに,ここで挙げる例は下のリンクの丸パクりなので新味はない.あくまで個人用メモ.
github.com

実行するとわかるが著しくもっさり.気合いの入った文章で実行するには忍耐か別の方法をおすすめする.
まず,githubからインストールするのでdevtoolsが入っていない方はインストールする.
依存関係のあるNLPパッケージも同様.

# install.packages(c("NLP","devtools"))
devtools::install_github("sfeuerriegel/SentimentAnalysis")

インストールしたらパッケージを呼び出して,軽くテスト.
analyzeSentiment関数の入力は,ここでは文字列ベクトルにしてるが,document-term matrixでもコーパスでもいいらしい.

> require(SentimentAnalysis)
> require(NLP)
> sentiment <- analyzeSentiment("Yeah, this was a great soccer game of the German team!")

さて,この文章はどんなセンチメントか?

> convertToBinaryResponse(sentiment)$SentimentGI
[1] positive
Levels: negative positive

Sugeeeee!!
ちなみに,心理社会学で広く用いられている辞書;Harvard-IVをつかった辞書ベースのオピニオンマイニングをしている.

複数でもOK

> documents <- c("Wow, I really like the new light sabers!",
+                "That book was excellent.",
+                "R is a fantastic language.",
+                "The service in this restaurant was miserable.",
+                "This is neither positive or negative.",
+                "The waiter forget about my a dessert -- what a poor service!")
> sentiment <- analyzeSentiment(documents)
> sentiment$SentimentGI
[1]  0.3333333  0.5000000  0.5000000 -0.6666667  0.0000000 -0.6000000
> convertToDirection(sentiment$SentimentGI)
[1] positive positive positive negative neutral  negative
Levels: negative neutral positive

こんな感じで各文書のセンチメントスコアやオピニオンを出してくれる.

Rで全部NAの列だけ削除する.その他のNAは0に置換する.

いかにデータ分析していなかったかが,Blogの更新でわかる.
手を動かすのを休むと,どんどんめんどくさがりに傾倒していく私.

ウォーミングアップがてら,こんな話.

こんなふうに,全部NAの行と,ところどころNAが入ったセルがあるとする.

dat <- matrix(c(26,70,10,
                28,NA,59,
                96,84,35,
                NA,85,15,
                35,50,7,
                39,NA,61,
                80,81,32,
                51,4,92,
                NA,NA,NA,
                70,16,29,
                22,4,13,
                31,85,56,
                51,86,43,
                NA,NA,NA,
                78,59,7,
                18,34,33,
                32,14,NA,
                7,NA,14,
                95,25,47,
                39,36,63), ncol=3, byrow=T)

こんなdatに対して,NAの入った行を全部削除するならna.omit()すればよろしい.
全部NAの列は削除し,その後でNAを0に置換することを考える.
普通にRのコードを書くと,

dat <- dat[!apply(is.na(dat), 1, all),]
dat[is.na(dat)] <- 0

こんな感じ.dplyrチェーンでやろうとすると,

require(tidyverse)
dat %>%
  filter(rowSums(is.na(.)) != ncol(.)) %>%
  mutate_all(funs(replace(., is.na(.), 0)))

こんな感じ.

winpythonからTheano/Kerasを使う

Rと違ってPythonの環境を整えるのは結構面倒くさい.
Anaconda使えって話なんだけど,諸事情によりWinpythonのポータビリティが心地よいこともある.
ダウンロード激重だけどオールインワンだし,環境変数のPATH通すところとか億劫でなければ,比較的使いやすいと思う.
http://winpython.github.io/

そんな環境でTHEANO/KERASを使う時の注意点
(以下はWinPython-64bit-3.5.2.2Qt5を想定)

まず,最初からWinpythonにTheanoやらKerasやらは入ってる.
だけど,使えるようになっていない(と思う.)

何も考えないで
import keras
とすると,やたらといろいろ怒られる.
All-in-oneのWinpythonながら,細かいところまでは気配りできていない.

まず,mklがあるnumpy使えよ,と怒られる.
MKLってのは,近年フリーで提供されるようになった,Intel謹製Math Kernel Libraryってので,行列計算を高速化するヤツね.
(脱線するが,de-facto standardのintel visual Fortranは高すぎねーか?)

従順な私は,
http://www.lfd.uci.edu/~gohlke/pythonlibs/#numpy
から,
http://www.lfd.uci.edu/~gohlke/pythonlibs/dp2ng7en/numpy-1.11.2+mkl-cp35-cp35m-win_amd64.whl
をダウンロードして,
WinPython Control Panel.exe > Add packages > ファイル選択 でインストールする.
(適宜Versionに合わせてDLすべし.激重かつ激遅.)

その前に,WinPython Control Panel.exe の Uninstall packagesタブからNumpyをアンインストールすることを忘れない.


これでimport kerasをし....てもまだ怒られる.
どうやらC++コンパイルができないらしい.
Rtools(https://cran.r-project.org/bin/windows/Rtools)を入れてたからgccは入ってるはずなのに!!


よくよく聞いてみると,64bitのコンパイルができないとかなんとか言ってる.
どうも,mingw_64にパスが通っていなかったらしい.

環境変数のPATHに,私の場合 C:\Rtools\mingw_64\bin; を追加して,

>>> import keras
Using Theano backend.

成功なのだ!!

Tex/Lyxでpngの変換

 \xi は串ですか?臭いですか?財ですか?具材ですか??
私は具材と呼ぶことが多いです.

どうでもいいことはさておき,OSをクリーンインストールすると面倒くさいことがたくさん.
TexというかLyxでpngが変換できない.
png形式のファイルからeps形式に変換するための情報がありません.
設定で変換子を指定してください.」
と怒られた.
この解消方法.

Xcodeがインストールされていることが前提.
https://developer.apple.com/download/more/
からOSおよびXcodeのバージョンにあったCommand line toolをダウンロード・インストール

MacPortsがインストールされてなかったら,
https://www.macports.org/install.php
から,適当なMacPortsをインストール.

次に,
http://www.imagemagick.org/script/binary-releases.php
にあるとおり,ターミナルに

sudo port install ImageMagick

と入力.

あとは,lyxのpreference>ファイル処理>変換子で
変換元の形式:png,変換先の形式: epsとして,変換子に

convert $$i -flatten $$o

を指定する.-flattenを指定しないと,背景が変になるので注意.

ターミナルで

identify -list format

とすると,変換できる形式リストが見られる.
これで大概の画像フォーマットはconvertの指定でどうにかなる.

ただし,wmf/emf -> epsの方法を模索したけど見つからなかった...
だけどWindowsExcelのチャートとかlyxに普通にコピペして画像にすると,メタファイルの形式になるわけで...