KNIMEとか倉庫

KNIMEやEXCELなどの備忘録です。

【R】パイプ演算子を学びたい

・お題:パイプ演算子というやつを使うと、分かりやすいコードが書けるらしい。どんなものか、体験してみたい。

 

・先日Tidyverseを調べた際に、dplyrパッケージのチートシートpipesという記述があった(以下)。

github.com

pipesは日本語でパイプ演算子と呼ばれるらしく、使いこなすと便利らしい。以下のサイトを参考に、ちょっと試してみたい。

qiita.com

 

・まず、%>%という入力がめんどくさい。先のサイトに、以下の記述があった。私はwinなので、Ctrl + Shift + mでいけた。

・適当に試してみる。1から10までの数を含むベクトルを作って、その成分を足し算したい。

> library(tidyverse)
> data<-1:10
> data
 [1]  1  2  3  4  5  6  7  8  9 10
> sum(data)#いつもの書き方だとこんな感じ。
[1] 55
> data %>% sum()#パイプ演算子を使うとこんな感じ。%>%の前のオブジェクトが、後の関数の引数として扱われる。
[1] 55

・なんとなく、Pythonでオブジェクトのメソッドにアクセスする際のドットと似たようなイメージで行けそう。

・複数の引数を用いる関数の場合はどうなるか?function関数で2つ引数を持つ関数を作って試してみた。

> f<-function(a,b) a-b#aとbを引数に与えると、aからbを引き算してくれる関数をfと定義。

> data2<-5

> data2 %>% f(3)#fにはパイプ演算子でdata2を第1引数として、直接引数として3を第2引数として渡した。
[1] 2

・ということで、5-3で2が返ってきた。パイプ演算子で繋ぐと、第1引数として渡されるらしい。設定した関数の第2引数に初期値を与えておけば、引数を指定しない場合は初期値が代入されるので、引数なしで計算できる。

> f2<-function(a,b=10) a-b
> data2 %>% f2()
[1] -5

 

・パイプ演算子の何が便利かというと、処理を繋げて順番に見ることができる点なんだろうと思う。

・例えば、あるデータフレームから特定の列を抜き出してその平均値を算出し、何らかの回帰式に放り込んで数字を出す、などの処理がある場合、順繰りにパイプ演算子を使えば処理を追っていける。

・データフレームを作成する。

> SampleID<-c("20230101_A","20230101_B","20230101_C")
> Data_Value<-c(18,22,19)
> Data_Unit<-c("uM","uM","uM")
> df<-data.frame(SampleID,
+                Data_Value,
+                Data_Unit)
> View(df)

・何らかの回帰式は、例えばf(x)=5*x-6にしてみる。

・式を全部ネストすると以下のような感じ。書くのが面倒だし、なんだか分かりにくい。。

> 5*mean(df$Data_Value)-6
[1] 92.33333

・次に、パイプ演算子を使ってみる。回帰式をfuncで定義する。

> func<-function(x) 5*x-6

> df$Data_Value %>% mean() %>% func()
[1] 92.33333

・dfからData_Valueをとってきて、平均取って回帰式に放り込んでいるのが分かりやすい。以下のように記述すれば、Scoreに計算結果を格納してかつprint関数で表示することもできる。

> Score<-df$Data_Value %>% mean() %>% func() %>% print()
[1] 92.33333

 

・慣れればとても便利そう。

 

おわり。

 

【R】処理を速くしてみたい

・お題:Rでfor文を使うと処理が遅いので、for文を使わずに処理を速くしてみたい。

 

・以下のサイトを参考にさせて頂いた。

book.mynavi.jp

 

・とりあえず、以前のようにfor文で処理を記載してみる。1から1億まで整数を二乗して足してもらった。

> m=0
> tic()
> for (n in 1:1000000000) {
+     m<-m+n**2
+ }
> toc()
26.75 sec elapsed

 

・forを使わずに、何とかしてみる。今回は問題が単純なので、二乗して足す操作に1:100000000をそのまま突っ込む。いわゆるベクトル化?というやつらしい。確かに所要時間が短くなった。

> m<-0
> tic()
> m<-sum((1:1000000000)**2)
> toc()
4.96 sec elapsed

 

・処理内容によっては、function関数やapply系関数を使うと良いみたい。

 

おわり。

【R】処理時間を計測してみたい

・お題:処理にかかった時間を取得したい。

 

・tictocという可愛らしい名前のパッケージを使ってみる。以下を参照した。

qiita.com

・install.packages("tictoc")でパッケージをインストール。

 

・tictocを起動し、参考通りに実行してみる。tic()とtoc()で処理を挟むだけで、かかった時間を算出してくれる。

> library(tictoc)

> tic()
> Sys.sleep(1)
> toc()
1.03 sec elapsed

 

・実際の計算でも試してみる。こんな理不尽な計算でも愚直にやってくれる。

> m=0
> tic()
> for (n in 1:100000000) {
+     m=m+n
+ }
> toc()
2.13 sec elapsed

 

 

おわり。

 

【R】繰り返し処理してみたい。

・お題:for文やwhile文を使って、繰り返し処理をしてみたい。

 

for文を使ってみる。1から10まで足し算することを考える。Pythonで書くとこんな感じ。rangeはわざわざリスト化する必要はないけれど、Rと似たような感じにしたくてリストにしてみた。list(range(1,11))は[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]になる。

m=0
for n in list(range(1,11)):
    m=m+n
print(m)

これで、55が返ってくる。

・RStudioでforと打つと、候補が勝手に出てきてくれた。これはとても便利。

・variableのところに変数を、vectorのところにベクトルを放り込めば何とかなりそう。続きを書くとこんな感じ。forの後を()で包んで、そのあとの処理を{}で包む感じ。1:10は
1  2  3  4  5  6  7  8  9 10になってる。

> m<-0

> for (n in 1:10) {
+     m<- m + n
+ }
> m
[1] 55

 

Pythonでいうzipはどうなるか。複数のイテラブルなオブジェクトから要素を順番にセットで抜き出して変数に格納してくれるのがzip。

a=[1,2,3,4,5]
b=[6,7,8,9,10]

c=[]
for m,n in zip(a,b):
    c.append(m*n)
print(c)

で[6, 14, 24, 36, 50]が返ってくる。

 

・似たようなことをRでやってみると、以下のような感じ?

> a<-c(1,2,3,4,5)
> b<-c(6,7,8,9,10)

> c<-mapply(function(m, n) m*n, a, b)
> c
[1]  6 14 24 36 50

・mapplyはmapply(function, vector, vector, vector,...)みたいな感じで使うっぽい。vectorはlistでも別に良いので、イテラブルなオブジェクトならOKっぽい(Rでの呼び方はこれであっているのか?)。取り出した要素を掛け算するだけだけれど、今回はあえてfunction関数で処理を定義する。

・function関数は、直後の()内で変数を指定し、そのあとに処理を記述する。無名関数を使ったり、functionをオブジェクトにしてmapply関数の第1引数として放り込む方が見やすいと思うけれど、今回はPythonの先の例に似たような感じで書いてみた。

 

・次に、while文を試してみる。1から5まで整数を書き出すことを考える。処理が終わったら、Fin.を打ち出してほしい。Pythonで書くと以下の感じ。

n = 1
while n <= 5:
    print(n)
    n=n+1
print("Fin.")

これで以下が出力される。

1
2
3
4
5
Fin.

・Rでも同様に記述してみる。こんな感じだろうか。

> n<-1
> while (n<=5) {
+     print(n)
+     n=n+1
+ }
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
> print("Fin.")
[1] "Fin."

 

おわり。

【R】ggplot2で作図してみたい

・お題:先日インストールしたパッケージ群tidyverseに、ggplot2というグラフを作成するパッケージが入っていた。ggplot2でいろいろ作図してみたい。

 

・以下のサイトを参考にさせて頂いた。非常に丁寧に解説なさっているので、ぜひそちらをご参照いただきたい。

heavywatal.github.io

 

・サイトの紹介だけでは身につかないので、実際に手を動かしてグラフを作成してみる。せっかくなので、データセットを変えてみたい。以下のサイトを参考に、「餌別のヒナの齢に対する体重」ChickWeightを使ってみる。

www.trifields.jp

 

・データを眺めてみると、Chickにひよこ番号、Dietに餌の種類、Timeに体重測定の日、weightに体重が入っているっぽい。weightとTimeは数字、ChickとDietはfactorっぽい。ひよこは50羽、餌の種類は4種類あるみたい。

> library(tidyverse)

> View(ChickWeight)

> str(ChickWeight)
Classes ‘nfnGroupedData’, ‘nfGroupedData’, ‘groupedData’ and 'data.frame':    578 obs. of  4 variables:
 $ weight: num  42 51 59 64 76 93 106 125 149 171 ...
 $ Time  : num  0 2 4 6 8 10 12 14 16 18 ...
 $ Chick : Ord.factor w/ 50 levels "18"<"16"<"15"<..: 15 15 15 15 15 15 15 15 15 15 ...
 $ Diet  : Factor w/ 4 levels "1","2","3","4": 1 1 1 1 1 1 1 1 1 1 ...

 

・とりあえず、1番の餌を与えられたひよこを抽出する。

> data1<-filter(ChickWeight,Diet=="1")

 

・順を追って作図していく。

・とりあえず、キャンバスを作成。

>ggplot(data = data1)#キャンバス準備

・軸をマッピング

> ggplot(data = data1) +
     aes(x = Time, y = weight)#横軸Time、縦軸weightとしてマッピング

・散布図をプロット。

> ggplot(data = data1) + 
     aes(x = Time, y = weight) +
     geom_point()#散布図をプロット

・散布図に色を付ける。

> ggplot(data = data1) + 
     aes(x = Time, y = weight) +
     geom_point(mapping = aes(color = Chick), alpha = 0.6)#geom_***の中に色などの工夫を入れられる。

・ベーステーマを変更。

> ggplot(data = data1) + 
     aes(x = Time, y = weight) +
     geom_point(mapping = aes(color = Chick), alpha = 0.6)+
     theme_classic()#ベーステーマを変えてみる。

・凡例の順番を変更。

> ggplot(data = data1) + 
     aes(x = Time, y = weight) +
     geom_point(mapping = aes(color = Chick), alpha = 0.6)+
     guides(color = guide_legend(reverse = TRUE))+#凡例の順番を逆にする。
     theme_classic()

・タイトルと軸を変更。
> ggplot(data = data1) + 
     aes(x = Time, y = weight) +
     geom_point(mapping = aes(color = Chick), alpha = 0.6)+
     guides(color = guide_legend(reverse = TRUE))+
     labs(title = "えさ1",
          x = "測定日",
          y = "体重(g)")+
     theme_classic()

・箱ひげ図を描いてみる。

> ggplot(data = data1) + 
     aes(x = Time, y = weight) +
     geom_boxplot(mapping = aes(group = Time))+
     theme_classic()

・餌の種類ごとに箱ひげ図を描いてみる。

> ggplot(data = ChickWeight) +
     aes(x = Time, y = weight) +
     geom_boxplot(mapping = aes(group = Time, fill = Diet))+#fillで色をDietで指定。
     facet_wrap(~Diet) +#Dietごとにグラフを作成。チルダが必要っぽい。
     theme_classic()

・色のセットを変えたい。以下のサイトを参考に設定した。

k-metrics.netlify.app

・まずはカラーパレットの確認。

> RColorBrewer::display.brewer.all()

・Accentのカラーセットを使って作図。

>ggplot(data = ChickWeight) +
    aes(x = Time, y = weight, fill = Diet) +
    geom_boxplot(mapping = aes(group = Time))+
    facet_wrap(~Diet) +
    scale_fill_brewer(palette = "Accent")+
    theme_classic()

 

・最後に、グラフをオブジェクトにして保存してみる。先のggplotの前にgg<-を付けてggというオブジェクトに格納(割愛)。

> ggsave(plot = gg, filename = "test01.pdf", device = "pdf")
Saving 7 x 7 in image

 

おわり。

【R】Tidyverseに関するメモ

・お題:Pythonもそうだけれど、Rでは便利機能を備えたソフトウェアをインストールすることで、機能を拡張することができる。少し調べてみたところ、データサイエンスやりたいならとりあえずTidyverseというパッケージ集をインストールしておくと便利らしい。

 

・Tidyverseはパッケージ集であり、正しいことは以下の公式サイトを確認していただきたい。

www.tidyverse.org

・以下がイメージ図。それぞれ、どんなパッケージなのか見てみる。正しいことはそれぞれのリンク先をご確認いただきたい。チートシートも載っているので、とても助かる。。

・ggplot2:いろいろなグラフを描くことができるライブラリ。とても綺麗なグラフが描ける。

ggplot2.tidyverse.org

 

・dplyr:データの表を弄るのにとても便利。あと、pipes(%>%)と呼ばれる表記方法がとても便利。

dplyr.tidyverse.org

 

・tidyr:雑然としたデータをtidy dataにしたり、前処理をしたりするパッケージ。tidy dataは解析系のパッケージでデータをハンドリングする際の基本になるデータのまとめ方だけれど、何せ人間にはぱっと見で分かりにくく、そこを解決してくれるのはとてもありがたい。

tidyr.tidyverse.org

 

・readr:データファイルを読み込むパッケージ。普通じゃない区切りのデータを読み込むのに大事っぽい?

readr.tidyverse.org

 

・purrr:ベクトルを使ってデータ処理する機能を強化してくれるパッケージ。map関数とかがイメージしやすいかも。

purrr.tidyverse.org

 

・tibble:data.frameのモダン版を提供するパッケージらしい。。?

tibble.tidyverse.org

 

・stringr:文字列操作や正規表現の機能を拡張するパッケージ。

stringr.tidyverse.org

 

・forcats:ファクターを使ってレベルや値の順序の変更など、因子に関する面倒なところを解決してくれるっぽい。理解が追い付いていない。。forcats.tidyverse.org

 

・前置きが長くなったけれど、tidyverseを使ってみる。とりあえずインストール。

> install.packages("tidyverse")

 

・tidyverseを起動。すると、何か出てきた。

> library(tidyverse)

-- Attaching packages ----------------- tidyverse 1.3.2 --
v ggplot2 3.3.6      v purrr   0.3.4 
v tibble  3.1.8      v dplyr   1.0.10
v tidyr   1.2.0      v stringr 1.4.1 
v readr   2.1.2      v forcats 0.5.2 
-- Conflicts -------------------- tidyverse_conflicts() --
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()

 

・素人を狼狽させないでくれ、、と思いつつ、Conflictsって何か調べてみた。どうも、dplyrとstatsにfilterという関数が含まれており、名前が被っているらしい。dplyrがstatsに勝ったようで、この後に単にfilter関数を使うと、dplyrのfilter関数が出てくることになる。

 

・この謎のコロン2つは、パッケージを指定して関数を呼び出すときに使うらしい。dplyr::filter()で「dplyrというパッケージのfilterという関数」、stats::filter()で「statsというパッケージのfilterという関数」を表すみたい。「statsというパッケージのfilterという関数」を使う予定もないので、とりあえず無視しておく。

 

・tidyverseしか起動していないが、これで先のパッケージ群が起動したことになっている。デフォルトでRではパッケージが起動していれば関数を指定するだけで処理を実行できる(Pythonでもimportのやり方によっては関数名だけで処理できる)。

 

・Rを起動すると呪文のようにtidyverseを起動するようになりそう。R起動時に自動でLibraryを読み込ませるには、以下のサイトを参考に設定すれば良いかも。

stats.biopapyrus.jp

 

 

おわり。

 

 

 

【R】データの型を確認したり変更したりしたい

・お題:データの型が合わないと、うまく処理してくれない。データの型の確認方法や、変更方法をメモしておきたい。

 

・とりあえず、オブジェクトを作ってみる。

> a<-1
> b<-"2"

 

・それぞれのオブジェクトの型を確認する。Rではclass関数で出てくるっぽい。もっと正確には、以下のサイトのように型の概念が存在するらしい。

qiita.com

・とりあえず、class関数でみてみる。

> class(a)
[1] "numeric"
> class(b)
[1] "character"

 

・例えば、それぞれのオブジェクトに1を足したいが、そのまま1を足すと案の定、characterで認識されているbでエラーが出た。

> a+1
[1] 2
> b+1
Error in b + 1 : non-numeric argument to binary operator

 

・Rで型を変更する場合、as.関数を使う。これには非常に多くの関数が用意されており、as.まで入力すると候補がたくさん出てくる。ここから選んで使えばOKっぽい。

 

・例えば、文字列として認識されているbを数字の2として認識させたければ、as.numeric関数を使えばいけそう。ただし、数字の2にはなるけれど、bに数字の2が代入されるわけではないので、代入してあげる必要がある。

> b<-as.numeric(b)
> b
[1] 2

 

・文字列を処理する際に、paste関数でくっつけることがある。numeric型だとうまくいかないからcharacterにしなきゃなぁ、、と思ったところ、何故かうまくいく。例が悪かったけれど、as.character関数があるので、これで文字に型を変えられる。

> paste("A","B",sep="")
[1] "AB"

> paste("a",a,sep="")#なんかできた。
[1] "a1"

> as.character(a)
[1] "1"

 

・データフレームでもやってみる。とりあえず、データフレームを作る。

> col1<-c("1","2","3","4")
> col2<-c("a","b","c","d")
> df<-data.frame(col1,col2)
> df
  col1 col2
1    1    a
2    2    b
3    3    c
4    4    d
> str(df)
'data.frame':    4 obs. of  2 variables:
 $ col1: chr  "1" "2" "3" "4"
 $ col2: chr  "a" "b" "c" "d"

 

・col1をnumericに変更してみる。

> df$col2<-as.numeric(df$col2)
> str(df)
'data.frame':    4 obs. of  2 variables:
 $ col1: num  1 2 3 4
 $ col2: chr  "a" "b" "c" "d"

 

・ちなみに、col2を無理やりnumericに変えてみると、NAになる。coercionは無理やりデータ型を変えられちゃいました、ということらしく、NAは一応numericらしい。データが飛んでしまった。

> as.numeric(df$col2)
[1] NA NA NA NA
Warning message:
NAs introduced by coercion 

 

・これを何とかするには、データ型によって処理を変える必要が出てくるのかなと思う。is.関数で、そのデータの型が何か調べられるっぽい。

> is.numeric(1.234)
[1] TRUE

> is.numeric("1.234")
[1] FALSE

> is.numeric("a")
[1] FALSE

 

・データ型が混在して入力したベクトルは、例えば以下の例だとcharacterになったりする。ベクトルはデータ型の混在を許容しないので、仕方なしで変えてくれるっぽい。

> test<-c(1,2,"3","d")

> str(test)
 chr [1:4] "1" "2" "3" "d"

 

・では、これをas.numericするとどうなるか。数字にできるものは数字にして、無理っぽいのはNAに強制的に変換してくれる。

> as.numeric(test)
[1]  1  2  3 NA

 

 

おわり。