京橋のバイオインフォマティシャンの日常

南国のビーチパラソルの下で、Rプログラムを打ってる日常を求めて、、Daily Life of Bioinformatician in Kyobashi of Osaka

【LINUX/Macの基本コマンド⑥】chmodコマンドで、ファイルモード(ファイル権限など)を変更

はじめに

chmodコマンドは、change modeに由来するコマンドで、 ファイルモード(ファイルのアクセス権限など)を変更できます。

アクセス権限のことを、単に、パーミッションと言うときもあります。

ファイルごとの権限表記の見方や、chmodコマンドを用いた権限変更の方法などを解説します。 実際、ファイルのアクセス権限やchmodコマンドの使い方は詳しく知ってて損はないです。

lsコマンドで、現在のパーミッション設定を表示する

まず、lsコマンド用いて、ファイルのアクセス権限を表示します。 -lhオプションを使用します。

lは詳細表示、hではファイル容量に単位をつけます。 aでディレクトリや隠しファイルを含めて表示します。

#ディレクトリ・ファイル権限の表示
ls -alh

#drwxr-xr-x   7 sas  staff   224B  1 17 21:24 .
#drwx------@ 57 sas  staff   1.8K  1 17 23:38 ..
#-rw-r--r--@  1 sas  staff   6.0K  1 17 21:24 .DS_Store
#-rw-r--r--@  1 sas  staff   3.6K  1 18 00:11 chmod.Rmd
#-rw-r--r--@  1 sas  staff   8.8K  1 17 21:24 test1.txt
#-rw-r--r--@  1 sas  staff    44K  1 17 21:24 test2.txt
#-rw-r--r--@  1 sas  staff   316K  1 17 21:24 test3.txt

ここで、-rw-r--r--@の部分がファイルのアクセス権限になります。。。 もうすでに、、この文字列の羅列は見たくない感じがしますね。

まずは、-rw-r--r--@ 1 sas staff 247B 1 17 21:10 chmod.Rmd全体について、それぞれの要素を分解して、説明します。

記法 概要
- ファイル(-) or ディレクトリ(d) or シンボリックリンク(|)
rw-r--r--@ アクセス権限
@ 拡張属性
1 ハードリンクの数
sas 所有者
staff グループ名
247B ファイルサイズ
1 17 21:10 最終更新日時
chmod.Rmd ファイル名

また、ファイルに付いてる属性が確認できる方法があります。 追加で、@マークを使用します。

#属性の表示
ls -lh@

#total 752
#-rw-r--r--@ 1 sas  staff   672B  1 17 23:28 chmod.Rmd
#  com.apple.TextEncoding    15B 
#  com.apple.lastuseddate#PS     16B 
#  com.apple.macl    72B 
#  com.apple.metadata:_kMDItemUserTags   42B 
#-rw-r--r--@ 1 sas  staff   8.8K  1 17 21:24 test1.txt
#  com.apple.TextEncoding    15B 
#  com.apple.lastuseddate#PS     16B 
#  com.apple.macl    72B 
#  com.apple.metadata:_kMDItemUserTags   42B 
#  com.apple.metadata:kMDLabel_j7tdv3zsbybglfmys5p2yfq76m    89B 
#-rw-r--r--@ 1 sas  staff    44K  1 17 21:24 test2.txt
#  com.apple.TextEncoding    15B 
#  com.apple.lastuseddate#PS     16B 
#  com.apple.macl    72B 
#  com.apple.metadata:_kMDItemUserTags   42B 
#-rw-r--r--@ 1 sas  staff   316K  1 17 21:24 test3.txt
#  com.apple.TextEncoding    15B 
#  com.apple.lastuseddate#PS     16B 
#  com.apple.macl    72B 
#  com.apple.metadata:_kMDItemUserTags   42B 

ファイルのアクセス権限の見方について

-rw-r--r--@の部分には、以下のような意味が含まれます。

アクセス権限の表記には、-rwxXなどがあって、それぞれ下表に示す意味があります。

Mode 名称 説明
- 権限無し
r 読み込み/リード ファイルを読み出し可能、ディレクトリを参照可能(一覧表示)
w 書き込み/ライト ファイルやディレクトリに書き込み(新規作成、削除)可能
x 実行権限 ファイルを実行可能 or ディレクトリに移動可能
X 特殊実行 現在の権限に関係なく実行権限を付与

また、所有者(u)、グループ(g)、他ユーザー(o)の順で、3文字ずつ、アクセス権限が記載されています。

全てのユーザーに実行権限(a+x)を追加する

権限追加の際には、+を用います。 +の前は全ユーザー(a)を指定して、+の後は実行権限(x)を指定します。

#ファイル権限表示
ls -lh test1.txt
#-rw-r--r--@ 1 sas  staff   8.8K  1 17 21:24 test1.txt

#実行権限の追加
chmod a+x test1.txt

#ファイル権限表示
ls -lh test1.txt
#-rwxr-xr-x@ 1 sas  staff   8.8K  1 17 21:24 test1.txt

グループ(g)、他ユーザー(o)の箇所に、xが追加されています。

他ユーザーの実行権限(o-x)を削除する

権限削除の際には、-を用います。 -の前は他ユーザー(o)を指定して、-の後は実行権限(x)を指定します。

#ファイル権限表示
ls -lh test1.txt
#-rwxr-xr-x@ 1 sas  staff   8.8K  1 17 21:24 test1.txt

#他ユーザーの実行権限の削除
chmod o-x test1.txt

#ファイル権限表示
ls -lh test1.txt
#-rwxr-xr--@ 1 sas  staff   8.8K  1 17 21:24 test1.txt

よく見ると、他ユーザー(o)の箇所のxが削除されました。

3つの数字列で、アクセス権限を与える

アクセス権限は、3つの数字の組み合わせでも変更できます。 下表の0, 1, 2, 3, 4, 5, 6, 7の数値を使用します。

権限の数字 概要
0 すべて、不許可
1 実行権限
2 書き込み許可
3 書き込み許可、実行権限
4 読み込み許可
5 読み込み許可、実行権限
6 読み込み許可、書き込み許可
7 読み込み許可、書き込み許可、実行権限

3つの数字列は、 所有者(u)、グループ(g)、他ユーザー(o)の順で、 権限を記述します。

例えば、所有者(u)にすべての許可(7)、 グループ(g)と他ユーザー(o)に読み込み許可だけ(4)を与える場合 744を指定します。意外と簡単ですね。。

ディレクトリ下のすべてのフォルダ・ファイルのパーミッションを変更する

chmod -R 777 [ディレクトリ]

-Rはディレクトリ全体への適用を意味します。 また、777は、すべてのユーザーに読み込み・書き込み・実行の権限を与えます。

まとめ

著者もそうでしたが、 ファイルやディレクトリのアクセス権限は、LINUXを使い始めのときには難しい概念でした。。

この記事が、LINUXやMacを使い始めた方の少しでも理解を助けられたら、嬉しいですね。。

参考資料

ja.wikipedia.org

【R言語と仮想通貨】ビットコイン(BTC)の平均足(Heikin-Ashi)のキャンドルチャートを作成してみた件

はじめに

平均足(Heikin-Ashi)とは、通常「1日分」のデータを使ってローソクチャートを作成するところを、その倍の「2日分」のデータを用いて作成するローソクチャートのことを言います。

そのため、細かなノイズが省かれて、トレンドの流れを視覚的に把握しやすいチャート**になると考えられています。また、海外でも、「Heikin-Ashi」と言われるほど、一般的に浸透しているテクニカルなチャートのようですね。

平均足の見方としては、 「陽線が連続すると、上昇トレンド」、 「陰線が連続すると、下降トレンド」と分かり易いです。 また、ローソク足の実体の大きさがトレンドの勢いを示すようです。

今回、ビットコイン(BTC)のデータとggplot2を使って、平均足のキャンドルチャートを作成することにします。 また、ストキャスティクスRSIなどのテクニカル指標も併せてやってみたいと思います。

関連パッケージ

まずは、Rで、関連パッケージをインストールします。

#パッケージ・インストール
pack <- c("quantmod", "dygraphs", "htmltools", "magrittr", "TTR", "xts", "ggplot2", "gridExtra", "lubridate")
install.packages(pack[!(pack %in% unique(rownames(installed.packages())))])

#ロード
for(n in 1:length(pack)){ eval(parse(text = paste0("library(", pack[n], ")"))) }; rm("n", "pack")

また、平均足を計算する関数などのスクリプト(Heikin_Ashi.R)を読み込みます。 上記のインストール実行も含まれます。

source("https://gist.githubusercontent.com/kumeS/2fc0b2e020044710226d1bed51c45ad2/raw/3de658478008d4045f9914f8a0fddba7bc2b8eb5/Heikin_Ashi.R")

BTCの月足データの平均足チャートを作成する

まずは、BTCの月足データから、平均足チャートを作成します。

quantmod::getSymbols関数で、periodicityを「monthly」に設定することで月足データが得られます。期間は、「2014-10-01」以降としています。

#月足データの取得: 2014年10月以降
BTCm <- getSymbols("BTC-USD",
           from = "2014-10-01", 
           to = "2023-12-31", 
           periodicity = 'monthly', 
           src = "yahoo", 
           verbose = F,
           auto.assign=FALSE)

#列名の変更
BTCm <- setNames(BTCm, 
                 nm = c("Open", "High", "Low", "Close", "Volume", "Adjusted"))

#表示
head(BTCm)
#              Open    High     Low   Close     Volume Adjusted
#2014-10-01 387.427 411.698 289.296 338.321  902994450  338.321
#2014-11-01 338.650 457.093 320.626 378.047  659733360  378.047
#2014-12-01 378.249 384.038 304.232 320.193  553102310  320.193
#2015-01-01 320.435 320.435 171.510 217.464 1098811912  217.464
#2015-02-01 216.867 265.611 212.015 254.263  711518700  254.263
#2015-03-01 254.283 300.044 236.515 244.224  959098300  244.224

#表示
tail(BTCm)
#               Open     High      Low    Close       Volume Adjusted
#2022-09-01 20050.50 22673.82 18290.31 19431.79 1.123272e+12 19431.79
#2022-10-01 19431.11 20988.39 18319.82 20495.77 9.579034e+11 20495.77
#2022-11-01 20494.90 21446.89 15599.05 17168.57 1.224532e+12 17168.57
#2022-12-01 17168.00 18318.53 16398.14 16547.50 5.413567e+11 16547.50
#2023-01-01 16547.91 19964.32 16521.23 19909.57 2.162587e+11 19909.57
#2023-01-14 19907.83 21035.62 19907.83 20832.73 4.514126e+10 20832.73

#ggChandlesによるキャンドルチャート
p1 <- ggChandles(x=BTCm,
                 term=c("2020-01-01", "2023-12-31"),
                 TsukiAshi=TRUE)

#平均足のキャンドルチャート
p2 <- ggChandles(heikin_ashi(BTCm),
                 term=c("2020-01-01", "2023-12-31"),
                 Tittle="BTC-USD Heikin-Ashi", 
                 TsukiAshi=TRUE)

#複数図のプロット
gridExtra::grid.arrange(p1, p2, nrow = 1)

通常のローソク足だと、上がったり下がったりがノイズ的に見えてますが、 平均足だと、2021年末以降ずっと下落トレンドですね。

一目瞭然ですね!!

月足データのストキャスティクスRSIオシレーター

続いて、ストキャスティクスRSIオシレーター(以下、StochRSI)を計算します。

StochRSIは、通常のRSI(Relative Strength Index、相対力指数)がN日間の期間中、相対的にどの水準を推移しているかを示す指標です。通常のRSIよりも、ある期間中の変動幅(高値・安値)に対する値位置をより明確に見極められます。

また、今回自作したstRSI関数は、StochRSIをより鋭敏にした関数です。

#月足データのRSI
BTCm.stoch <- stochRSI(BTCm)
BTCm.st <- stRSI(BTCm)

#プロット
a <- with(BTCm.stoch, 
          stochRSI[index(BTCm.stoch) >= "2020-01-01"])
plot(a, main="BTCm.stoch / BTCm.st", ylim=c(-0.01,1.01))
lines(BTCm.st, col=alpha("red", 0.7), lwd=2)

#月足の平均足データのRSI
BTCm.stoch.heikin <- stochRSI(heikin_ashi(BTCm))
BTCm.st.heikin <- stRSI(heikin_ashi(BTCm))

#プロット
a <- with(BTCm.stoch.heikin, 
          stochRSI[index(BTCm.stoch.heikin) >= "2020-01-01"])
plot(a,
     main="BTCm.stoch.heikin / BTCm.st.heikin",
     ylim=c(-0.01,1.01))
lines(BTCm.st.heikin, col=alpha("green", 0.7), lwd=2)

次には、キャンドルチャートと両方併せて、プロットしてみます。

#月足のローソクチャート
p <- ggChandles(BTCm,
                term=c("2020-01-01", "2023-12-31"),
                TsukiAshi=TRUE)

#stochRSI、stRSIのプロット
BTCm.s <- cbind(BTCm.stoch, BTCm.st)
p2 <- ggChart(x=BTCm.s, y="stochRSI",
              term=c("2020-01-01", "2023-12-31"),
              Tittle="BTC: stochRSI / stRSI")
p2 <- p2 + geom_line(aes(x=Index, y=stRSI), 
                     colour=alpha("red", 0.7),
                     linetype="solid")

#月足のローソクチャート
p1 <- ggChandles(heikin_ashi(BTCm),
                term=c("2020-01-01", "2023-12-31"),
                TsukiAshi=TRUE,
                Tittle="BTC-USD Heikin")

#平均足のローソクチャート
BTCm.sh <- cbind(BTCm.stoch.heikin, BTCm.st.heikin)
p3 <- ggChart(x=BTCm.sh, y="stochRSI", 
              term=c("2020-01-01", "2023-12-31"),
              Tittle="BTC: stoch.heikin / st.heikin")
p3 <- p3 + geom_line(aes(x=Index, y=stRSI), 
                     colour=alpha("green", 0.7),
                     linetype="solid")

##複数図の作成
layout <- rbind(c(1, 1, 2, 2), c(1, 1, 2, 2), c(3, 3, 4, 4))
gridExtra::grid.arrange(p, p1, p2, p3, layout_matrix=layout)

stRSIの結果から、2021年10月ごろが最後のピークだったってことがよく分かりますね。

BTCの週足データの平均足チャートを作成する

次に、BTCの週足データから、平均足チャートを作成してみます。 月足データと同じ手順で行います。

#週足データの取得:  2014年10月以降
BTCw <- getSymbols("BTC-USD",
           from = "2014-10-01", 
           to = "2023-12-31", 
           periodicity = 'weekly', 
           src = "yahoo", 
           verbose = F,
           auto.assign=FALSE)

#列名の変更
BTCw <- setNames(BTCw, 
                 nm = c("Open", "High", "Low", "Close", "Volume", "Adjusted"))

#表示
head(BTCw)
#              Open    High     Low   Close    Volume Adjusted
#2014-09-29 387.427 391.379 289.296 320.510 209452896  320.510
#2014-10-06 320.389 382.726 302.560 378.549 341152804  378.549
#2014-10-13 377.921 411.698 368.897 389.546 156902070  389.546
#2014-10-20 389.231 392.646 342.877 354.704 113691800  354.704
#2014-10-27 354.777 359.984 320.626 325.892 107075700  325.892
#2014-11-03 325.569 363.626 325.077 363.264 116793470  363.264

#ggChandlesによるキャンドルチャート
p1 <- ggChandles(x=BTCw,
                 term=c("2021-06-01", "2023-12-31"),
                 lwd=0.15)

#平均足のキャンドルチャート
p2 <- ggChandles(heikin_ashi(BTCw),
                 term=c("2021-06-01", "2023-12-31"),
                 Tittle="BTC-USD Heikin-Ashi",
                 lwd=0.15)

#複数図のプロット
gridExtra::grid.arrange(p1, p2, nrow = 1)

2022年初め以降の傾向だと、週足データでの上昇トレンドは、続いて4週間というのが良いとこかも。

続いて、stRSIの結果も一緒にプロットしてみます。

#週足のローソクチャート
p <- ggChandles(BTCw,
                term=c("2021-01-01", "2023-12-31"),
                lwd=0.15)

#週足データのRSI
BTCw.stoch <- stochRSI(BTCw)
BTCw.st <- stRSI(BTCw)

#stochRSI、stRSIのプロット
BTCw.s <- cbind(BTCw.stoch, BTCw.st)
p2 <- ggChart(x=BTCw.s, y="stochRSI",
              term=c("2021-01-01", "2023-12-01"),
              Tittle="BTC: stochRSI / stRSI")
p2 <- p2 + geom_line(aes(x=Index, y=stRSI), 
                     colour=alpha("red", 0.7),
                     linetype="solid")

#週足のローソクチャート
p1 <- ggChandles(heikin_ashi(BTCw),
                term=c("2021-01-01", "2023-12-01"),
                Tittle="BTC-USD Heikin",
                lwd=0.15)

#週足の平均足データ
BTCw.stoch.heikin <- stochRSI(heikin_ashi(BTCw))
BTCw.st.heikin <- stRSI(heikin_ashi(BTCw))

#平均足のローソクチャート
BTCw.sh <- cbind(BTCw.stoch.heikin, BTCw.st.heikin)
p3 <- ggChart(x=BTCw.sh, y="stochRSI", 
              term=c("2021-01-01", "2023-12-01"),
              Tittle="BTC: stoch.heikin / st.heikin")
p3 <- p3 + geom_line(aes(x=Index, y=stRSI), 
                     colour=alpha("green", 0.7),
                     linetype="solid")

##複数図の作成
layout <- rbind(c(1, 1, 2, 2), c(1, 1, 2, 2), c(3, 3, 4, 4))
gridExtra::grid.arrange(p, p1, p2, p3, layout_matrix=layout)

週足のstRSIの結果から、局所的な高値がより分かり易くなったように見えますね。

BTCの日足データの平均足チャートを作成する

今回、stRSIの期間を、14、25、50、75日に設定して計算して、下図としてプロットしてみます。 過去180日間の日足データを用います。

#日足の取得 + 列名変更: 過去180日間
BTCd <- getSymbols("BTC-USD",
           from = lubridate::today()-lubridate::ddays(180), 
           to = lubridate::today(), 
           periodicity = 'daily', 
           src = "yahoo", 
           verbose = F,
           auto.assign=FALSE)  %>% 
  setNames(nm = c("Open", "High", "Low", "Close", "Volume", "Adjusted"))

#表示
head(BTCd)
#               Open     High      Low    Close      Volume Adjusted
#2022-07-18 20781.91 22633.03 20781.91 22485.69 39974475562 22485.69
#2022-07-19 22467.85 23666.96 21683.41 23389.43 48765202697 23389.43
#2022-07-20 23393.19 24196.82 23009.95 23231.73 42932549127 23231.73
#2022-07-21 23233.20 23388.32 22431.15 23164.63 33631012204 23164.63
#2022-07-22 23163.75 23671.93 22603.42 22714.98 31421555646 22714.98
#2022-07-23 22706.98 22977.21 22002.91 22465.48 24021799169 22465.48

#日足のローソクチャート
p <- ggChandles(BTCd,
                term=c("2022-10-01", "2023-12-31"),
                lwd=0.15)

#日足データのstRSI
BTCd.st14 <- stRSI(BTCd, n = 14)
BTCd.st25 <- stRSI(BTCd, n = 25)
BTCd.st50 <- stRSI(BTCd, n = 50)
BTCd.st75 <- stRSI(BTCd, n = 75)

#列の結合と列名の変更
BTCd.s <- cbind(BTCd.st14, BTCd.st25, BTCd.st50, BTCd.st75) %>%
  setNames(nm = c("stRSI14", "stRSI25", "stRSI50", "stRSI75"))

#stochRSI、stRSIのプロット
p2 <- ggChart(x=BTCd.s, y="stRSI14",
              term=c("2022-10-01", "2023-12-31"),
              Tittle="BTC: stRSI") +
  geom_line(aes(x=Index, y=stRSI25), 
            colour=alpha("red", 0.7),
            linetype="solid") +
  geom_line(aes(x=Index, y=stRSI50), 
            colour=alpha("blue", 0.7),
            linetype="solid") +
  geom_line(aes(x=Index, y=stRSI75), 
            colour=alpha("green", 0.7),
            linetype="solid")

#日足のローソクチャート
p1 <- ggChandles(heikin_ashi(BTCd),
                term=c("2022-10-01", "2023-12-31"),
                Tittle="BTC-USD Heikin",
                lwd=0.15)

#日足の平均足データ
BTCd.st14.heikin <- stRSI(heikin_ashi(BTCd), n = 14)
BTCd.st25.heikin <- stRSI(heikin_ashi(BTCd), n = 25)
BTCd.st50.heikin <- stRSI(heikin_ashi(BTCd), n = 50)
BTCd.st75.heikin <- stRSI(heikin_ashi(BTCd), n = 75)

#列の結合と列名の変更
BTCd.sh <- cbind(BTCd.st14.heikin, BTCd.st25.heikin,
                 BTCd.st50.heikin, BTCd.st75.heikin) %>%
  setNames(nm = c("stRSI14", "stRSI25", "stRSI50", "stRSI75"))

#平均足のローソクチャート
p3 <- ggChart(x=BTCd.sh, y="stRSI14", 
              term=c("2022-10-01", "2023-12-31"),
              Tittle="BTC: stoch.heikin / st.heikin") +
  geom_line(aes(x=Index, y=stRSI25), 
            colour=alpha("red", 0.7),
            linetype="solid") +
  geom_line(aes(x=Index, y=stRSI50), 
            colour=alpha("blue", 0.7), 
            linetype="solid") +
  geom_line(aes(x=Index, y=stRSI75), 
            colour=alpha("green", 0.7), 
            linetype="solid")

##複数図の作成
layout <- rbind(c(1, 1, 2, 2), c(1, 1, 2, 2), c(3, 3, 4, 4))
gridExtra::grid.arrange(p, p1, p2, p3, layout_matrix=layout)

このstRSIの結果では、もう一杯一杯の上昇をしているのだが、さぁ、どうなるのでしょうかね。

dygraphsで複数のローソクチャートを描く

最後に、dygraphsで、複数のローソクチャートを描く方法を紹介します。プロット結果をリストでまとめて、htmltools::browsableとhtmltools::tagListを使用します。

#CSS設定
css <- "
.dygraph-title {
background-color: white;
color:black;
text-align: left;
margin-left: 10%;
}
.dygraph-ylabel {
text-align:center; font-size: 15px;
margin-left: 0px;
margin-top: -5px;
}"

#書き出し
write(css, "dygraph.css")

#日足データのdygraphプロット
a <- dygraph(BTCd[,c("Open", "High", "Low", "Close")]) %>%
 dyCandlestick() %>%
 dyAxis("y", label = "Price / USD", labelWidth=20) %>%
 dyAxis("x", label = "Date", rangePad=5, labelHeight=20) %>%
 dyCSS("dygraph.css")

#平均足データのdygraph
b <- dygraph(heikin_ashi(BTCd)) %>%
 dyCandlestick() %>%
 dyAxis("y", label = "Price / USD", labelWidth=20) %>%
 dyAxis("x", label = "Date", rangePad=5, labelHeight=20) %>%
 dyCSS("dygraph.css")

#リスト作成
plotobj <- list(a, b)

#レンダー
htmltools::browsable(htmltools::tagList(plotobj))

https://kumes.github.io/Blog/R_BTC/09.html

まとめ

平均足のローソクチャート、RSIは、まだまだ奥が深いですね。

補足

月足データにケルトナー・チャンネルを重ねてみると、、

Keltner Channels (ケルトナー・チャンネル)は、移動平均の上下に設定されたボラティリティ・ベースの包括的な指標です。

この指標は、ボリンジャーバンドに似ていますが、ケルトナー チャネルは、チャネル距離を設定するために、ATR(Average True Range)を使用しています。

#21月のKeltner Channels
kc <- keltnerChannels(BTCm[,c("High", "Low", "Close")], 
                      n = 21)

##Keltner Channels列の結合
BTCm.kc <- cbind(BTCm, kc)

#列名の変更
colnames(BTCm.kc) <- c("Open", "High", "Low", "Close",
                       "Volume", "Adjusted",
                       "dn", "mavg", "up")

#キャンドルチャート作図
p <- ggChandles(BTCm.kc,
                term=c("2020-01-01", "2023-12-31"),
                TsukiAshi=TRUE)

#Keltner Channelsの作図
p <- p + geom_line(aes(x=Index, y=dn), 
                   colour="blue", linetype="dashed")
p <- p + geom_line(aes(x=Index, y=up), 
                   colour="red", linetype="dashed")
p <- p + geom_line(aes(x=Index, y=mavg), 
                   colour="black", linetype="dashed")

#作図
p

ほぼ願望を込めて、、下限線は12000ドル当たりで引いています。悪しからず。。

Rコード

gist.github.com

参考資料

stackoverflow.com

array.cell-innovator.com

stackoverflow.com

stackoverflow.com

rdrr.io

github.com

stackoverflow.com

stackoverflow.com

qiita.com

id.fnshr.info

misaki-blog.com

R言語で、皆さん憧れの、ランダムウォーク・シミュレーションをやってみた件

はじめに

ランダムウォークと聞いて、その響からも、皆さん、さぞ憧れのところでしょうか。

ランダムウォークとは、英語で書くとRandom walkで、次に進む位置が確率的に無作為(ランダム)に決定される運動のことを指します。

もう少し、噛み砕いて言うと、、ランダムにガチャガチャっと動くやつってことです。高校物理で習ったような、ブラウン運動のモデルでもあります。

この記事では、1次元と2次元のランダムウォークについて紹介します。

ランダムウォークをどう利用するか??ということで、、最後に、 Google検索などで取得した画像にランダムウォークでモーションをつけて可視化してみたいと思います。

それでは、始めましょう。

実行環境

以下が、実行環境です。

実行環境
macOS Big Sur (バージョン11.5.2)
MacBook Air (M1, 2020)
チップ Apple M1
メモリ 16GB

パッケージ準備

今回使用する、Rパッケージを準備します。

#パッケージ・インストール
pack <- c("GoogleImage2Array", "animation", "ggplot2", "gganimate", "rtweet", "tidyverse")
install.packages(pack[!(pack %in% unique(rownames(installed.packages())))])

#ロード
for(n in 1:length(pack)){ eval(parse(text = paste0("library(", pack[n], ")"))) }; rm("n", "pack")

1次元ランダムウォーク

まずは、1次元のランダムウォークを解説します。この運動は、1つの軸上を前後に移動するので、試行回数で展開して挙動を見てみます。

シンプルな実行例としては、-1(後進)あるいは1(前進)をランダムサンプリングするということを、N回試行した後に、cumsum関数で、累積和を求める方法です。また、ランダムサンプリングには、sample関数を使用します。

また、類似のことは、stats::arima.sim関数でもできるようです。今回は、割愛します。

では、実行してみます。

#乱数生成: 省略可
set.seed(123)

#試行回数の設定
n <- 1000

#実行: 初期位置として、最初の0はあった方が良い
x <- c(0, cumsum(sample(c(-1, 1), size=n, replace=TRUE)))

#プロット
plot(x, type="l")

一次元ランダムウォーク

1つだけではなくて、続いて、複数の一次元のランダムウォークを生成してみます。

#空変数の作成
X <- c()

#試行回数の設定
n <- 1000

#実行
for(i in 1:10){
#ランダムウォーク生成
x <- c(0, cumsum(sample(c(-1, 1), size=n, replace=TRUE)))
#列結合
X <- cbind(X, x)
}

#表示
head(X)
#     x  x x x x  x x x  x  x
#[1,] 0  0 0 0 0  0 0 0  0  0
#[2,] 1  1 1 1 1  1 1 1 -1 -1
#[3,] 2  0 0 0 2  0 0 2 -2  0
#[4,] 3 -1 1 1 3 -1 1 3 -3 -1
#[5,] 4 -2 0 2 2 -2 0 4 -4 -2
#[6,] 3 -1 1 1 3 -1 1 3 -5 -3

#プロット
par(mgp=c(2.5, 1, 0))
matplot(X, type="l", lty=1, xlab="Number of trials", 
        col = rainbow(10, alpha = 0.6))

複数の一次元ランダムウォーク

このように試行ごとで、全然パターンが違うというのは、やはり興味深いですね。

次に、これをアニメーション表示してみます。

1000回分の試行をすべてアニメーションにすると、冗長なのとサイズが大きくなるので、初めの100回分のみで、アニメーションを作成してみました。

#試行回数の制限
k <- 100

#アニメーション作成
animation::saveGIF({for(i in 2:k){
matplot(c(1:nrow(X))[1:i], X[1:i,], type="l",
        lty=1, lwd=1.5, col=rainbow(ncol(X), alpha = 0.5),
        xlab="Number of trials", ylab="", xlim=c(0, k),
        ylim=c(min(X[1:k,]), max(X[1:k,])), cex.lab=1.2, cex.axis=1.2)
matpoints(t(rep(i, ncol(X))), t(X[i,]), pch=16, col=rainbow(ncol(X), alpha = 0.7), cex=1.25)
}}, movie.name = "01_motion.gif", interval = 0.1, nmax = k, ani.width = 500, ani.height = 500, dpi=500)

一次元ランダムウォーク・アニメーション

次に、ggplotで作図する場合には、データセットの形式を少し変える必要があります。

#空変数の作成
X <- c()

#試行回数の設定
n <- 1000

#10回の繰り返し実行
for(i in 1:10){
#ランダムウォーク生成
x <- c(0, cumsum(sample(c(-1, 1), size=n, replace=TRUE)))

#DF作成
x1 <- data.frame(trials=0:n, x=x, sim=i)

#行結合
X <- rbind(X, x1)
}

#表示
head(X)
#  trials  x sim
#1      0  0   1
#2      1 -1   1
#3      2  0   1
#4      3  1   1
#5      4  2   1
#6      5  3   1

#プロット
ggplot(data = X, aes(x = trials, y = x, color = factor(sim))) + 
  geom_line(alpha = 0.6) +
  theme(legend.position = "none") 

ggplotでのプロット

2次元ランダムウォーク: 2方向移動

次に、2次元のランダムウォークでは、2方向と全方向の移動を考慮します。

まず、2方向移動は、x軸あるいはy軸の方向にランダムウォークで移動するというものです。

その為、x軸あるいはy軸の方向に前進するか、後進するかをその時その時にランダムで決定されます。

では、実行してみます。

#試行回数の設定
n <- 1000

#実行(1)
x <- sample(c(-1, 1), size=n, replace=TRUE)
#実行(2)
y <- sample(c("x", "y"), size=n, replace=TRUE)

#DF作成
Dat <- data.frame(trials=0:n, x=0, y=0)

#xあるいはy列に割り振る
for(i in 1:n){ Dat[c(i+1),y[i]] <- x[i] }

#累積計算
Dat$cx <- cumsum(Dat$x)
Dat$cy <- cumsum(Dat$y)

#表示
head(Dat)
#  trials  x  y cx cy
#1      0  0  0  0  0
#2      1 -1  0 -1  0
#3      2  0 -1 -1 -1
#4      3  0 -1 -1 -2
#5      4  1  0  0 -2
#6      5  0 -1  0 -3

#プロット
plot(Dat$cx, Dat$cy, type="l", xlab="x", ylab="y")

2方向の二次元ランダムウォーク

何度か実行してみたところ、ヘンテコな幾何学模様が沢山生成されますね。

2次元ランダムウォーク: 全方向移動

次に、全方向移動に移動する場合のランダムウォークを検討します。 この場合には「0から2π(パイ)」の間の一様乱数を生成します。 実際のやり方としては、runif関数で、0から1の間の離散型一様分布の乱数を発生させて、2π、Rでの記法は2*piで補正します。

#試行回数の設定
n <- 1000

#ランダムに値を生成
r_vec <- 2 * pi * runif(n = n, min = 0, max = 1)

#DF作成
Dat <- data.frame(trials=0:n, x=0, y=0)

#集計
Dat$x[2:nrow(Dat)] <- cumsum(cos(r_vec))
Dat$y[2:nrow(Dat)] <- cumsum(sin(r_vec))

#表示
head(Dat)
#  trials         x          y
#1      0 0.0000000 0.00000000
#2      1 0.9454171 0.32586263
#3      2 1.7773490 0.88074037
#4      3 1.1780515 0.08021397
#5      4 1.0230444 1.06812733
#6      5 2.0069765 0.88958413

#プロット
plot(Dat$x, Dat$y, type="l", xlab="x", ylab="y")

全方向移動のランダムウォーク

何度か実行してみたところ、ガチャガチャしてて、ランダムウォークっぽい感じですね。

続いて、複数の二次元のランダムウォークを生成してみます。

#試行回数の設定
n <- 200

#空変数
Z <- c()

#20回の繰り返し実行
for(i in 1:20){
#ランダムに値を生成
r_vec <- 2 * pi * runif(n = n, min = 0, max = 1)

#DF作成
Dat <- data.frame(trials=0:n, x=0, y=0)

#集計
Dat$x[2:nrow(Dat)] <- cumsum(cos(r_vec))
Dat$y[2:nrow(Dat)] <- cumsum(sin(r_vec))

#シミュレーション回数
Dat$sim <- i

#行結合
Z <- rbind(Z, Dat)
}

#表示
head(Z)
#  trials         x         y sim
#1      0 0.0000000 0.0000000   1
#2      1 0.8785386 0.4776714   1
#3      2 1.3394305 1.3651277   1
#4      3 2.3038196 1.6296150   1
#5      4 3.2821351 1.4224948   1
#6      5 3.2640218 0.4226589   1

#プロット
ggplot(data = Z, aes(x = x, y = y, color=factor(sim))) + 
  geom_path(alpha = 0.6) +
  theme(legend.position = "none") 
#注釈: geom_lineではなく、geom_pathを使用すること。

全方向のランダムウォークの繰り返し実験

それでは、ggplot2のアニメーションも以下のように実行できます。

#アニメーション作図
p <- ggplot(Z, aes(x = x, y = y, color=factor(sim))) + 
  geom_point(size = 4, show.legend = F) + 
  geom_path(size = 1, alpha = 0.5) + 
  gganimate::transition_reveal(along = trials) + 
  coord_fixed(ratio = 1) + 
  labs(title = "2D Random Walk", subtitle = "Trials: {frame_along}")

#実行: 40秒くらいかかる
p

#gif画像の出力: 実行時間 60秒くらい
gganimate::animate(p, nframes=max(Z$trials)+1, fps = 10, width = 500, height = 500,
        renderer = gganimate::gifski_renderer("02_motion.gif"))

っgpぉtでのランダムウォーク・アニメーション

ランダムウォークで画像表示のアニメーションを作成してみると、こうなる。

早速、画像を準備します。

それでは、GoogleImage2array関数を使って、Google画像検索をして、猫の画像を取得します。

次に、その画像を使って、アニメーションを作成したいと思います。

#「猫」画像の取得
CatImg <- GoogleImage2array("猫", gl="ja")

#画像表示
display.array(CatImg)

猫画像の一覧

こういう、猫の画像です。

続いて、先程の2次元のランダムウォークの結果を使って、 猫画像それぞれをrasterImage関数で作図してみます。

#実行
animation::saveGIF({for(l in 1:(max(Z$trials)+1)){
#空プロットの作成
plot(Z[,2], Z[,3], type="n", axes = FALSE, xlab="", ylab="")
  
#線の作図
for(m in 1:max(Z$sim)){
Z1 <- Z[Z$sim == m,]
lines(x = Z1$x[1:l], y = Z1$y[1:l], col=rainbow(max(Z$sim), alpha = 0.7)[m])
}

#画像サイズのパラメータ計算(6%)
a1 <- diff(range(Z[,2]))*0.06
b1 <- diff(range(Z[,3]))*0.06

#ラスター画像をプロット
for(n in 1:dim(CatImg$array)[1]){
  ff <- t(as.raster(CatImg$array[n,,,]))
  ZZ <- Z[Z$sim == n,]
  rasterImage(ff, ZZ[l,2]-a1, ZZ[l,3]-b1, 
               ZZ[l,2]+a1, ZZ[l,3]+b1)
}}}, movie.name = "03_motion.gif", interval = 0.1, nmax = max(Z$trials)+1, ani.width = 600, ani.height = 600, dpi=500)

https://github.com/kumeS/Blog/blob/master/R_random_walk/03_motion.gif

さてさて、こう言う感じになります。

実際、ani.width = 1000, ani.height = 1000, dpi=600に変更すると、サイズが27MBくらいになりますが、見易い解像度になります。

ブログにはアップできないのですが、、こちらにアップしています。

twitterから収集した画像を使ってみたら、こうなる。

では、続いて、twitterから収集した画像でもやってみましょう。

クエリは「猫島」にしました。 クエリオプションとして指定した、"filter:media"でメディアを含むツイートだけに(ほぼ?)絞れます。 また、"include_rts = TRUE"では、リツイートも検索に含められます。

また、ライブラリのバージョン(現在"rtweet v1.0.2"を使用)に寄るのか、URL情報は、他の方の記事とはやや違うロケーションに格納されている感じでした。

#検索
tweets <- search_tweets(q="猫島 filter:media", n = 200, include_rts = TRUE) 

#urlを収集
url <- c()
for(n in 1:nrow(tweets)){
  #n <- 1
  a <- tweets$entities[[n]]$media$media_url
  b <- a[!is.na(a)]
  if(any(!is.na(b))){
      url <- c(url, b)
  }
}

#重複を除く
url2 <- unique(url)

#ダウンロード関数の定義
dl_url <- function(dat){
    download.file(dat, destfile = basename(dat), mode ="wb")
    Sys.sleep(0.5)
    }

#ダウンロード実行! 
#url2[1:20] %>% walk(dl_url)

#画像読み込み: ここではEBImageを使います
Img <- c()
for(j in 1:20){
  a <- EBImage::resize(EBImage::readImage(url2[j]), w=64, h=64)
  Img <- EBImage::abind(Img, a, along=4)
}

#attrのdimnames属性を消す
attr(Img, "dimnames") <- NULL

#詳細表示
str(Img)
#num [1:64, 1:64, 1:3, 1:20] 0.101 0.1941 0.0994 0.1437 0.1112 ...

画像はアレイ形式として、重ねていきます。

ちなみに、walk関数というのを初めて知りましたが、こう使うんですね。

続いて、アニメーションを作成します。

#アニメーション実行
animation::saveGIF({for(l in 1:(max(Z$trials)+1)){
#空プロットの作成
plot(Z[,2], Z[,3], type="n", axes = FALSE, xlab="", ylab="")

#線の作図
for(m in 1:max(Z$sim)){
Z1 <- Z[Z$sim == m,]
lines(x = Z1$x[1:l], y = Z1$y[1:l], col=rainbow(max(Z$sim), alpha = 0.7)[m])
}

#画像サイズのパラメータ計算(6%)
a1 <- diff(range(Z[,2]))*0.06
b1 <- diff(range(Z[,3]))*0.06

#ラスター画像をプロット
for(n in 1:dim(Img)[4]){
  ff <- t(as.raster(Img[,,,n]))
  ZZ <- Z[Z$sim == n,]
  rasterImage(ff, ZZ[l,2]-a1, ZZ[l,3]-b1, 
               ZZ[l,2]+a1, ZZ[l,3]+b1)
}}}, movie.name = "04_motion.gif", interval = 0.1, nmax = max(Z$trials)+1, ani.width = 1000, ani.height = 1000, dpi=600)

実際、こうなりました。じゃーん、、、解像度ワル!!

https://github.com/kumeS/Blog/blob/master/R_random_walk/04_motionB.gif

まとめ

ランダムウォークを見ていると、ココロが落ち着くところがありますね。 ヒトの中にも、類似する、ランダム性があるのでしょうね。

あと、Rでの簡易プロットの場合には、タンパク質の立体構造を線図にしたときと、ほぼ同じに見てますね。

参考資料

stackoverflow.com

financetrain.com

www.anarchive-beta.com

www.anarchive-beta.com

note.com

https://qiita.com/swathci/items/59542fb174a01cb079b8