「RユーザーのためのRStudio[実践]入門」読了メモ
はじめに
学習書籍読了メモ第一弾。今回は下記の本でRStudioの使い方を学んだ。書籍の簡単な内容紹介と個人的に有益となったポイントを記していこうと思う。ちなみに僕のR言語の練度は4ヶ月ほどRStudioを触り、CSV読み込み、簡単なデータ整形、簡単なグラフのプロットをネットを見ながら行える初心者後半といったところ。
学習所要日数は以下の通り。
- 学習開始日:2020/12/21
- 学習終了日:2021/01/02
- 学習所要時間:13時間
目次
- 第1章 RStudioの基礎:RStudioの設定、使い方、csv読み込み
- 第2章 スクレイピングによるデータ収集:WEBページからのデータ取得、パイプ演算子
- 第3章 dplyr/tidyrrによるデータ前処理:データの抽出、結合、集計
- 第4章 ggplotによるデータ可視化:散布図、グループ化、パネル分割
- 第5章 R Markdownによるレポート作成:マークダウンの基礎、RMarkdownの設定
第1章 RStudioの基礎
データフレーム
データフレームの中身の確認で「Filter」をクリックすることで列内の分布を大まかに把握可能。
スニペット機能
あらかじめ文字列を登録しておくことで頭文字のみを入力して残りを補完してくれる機能。自分オリジナルのスニペットは[Global Options]で登録可能。初心者のうちは必要なさそうだが、いつか使いこなしたい。
Global Options
RStudioの設定。こだわらない人にとってはデフォルトのままでいいと思う。個人的に変えたところは、保管機能を開始する文字数を3から2に、きれいなコードを書くためにコード診断に関するDiagnosticsタブの項目をすべて有効にした。あとは外観を白だと目が疲れるため黒に。
CSVの読み込み。
基本的にはread_csv()を使用すればよいみたい。引数で読み込む型の指定や欠損値の指定、読み飛ばしなど色々設定できる。
library(tidyverse)
read_csv("csv/Sales.csv", col_types = 'ccT',skip = 3,na = c('','NA'))
関数のヘルプ等を見る。
help(read_csv)
vignette("dplyr")
第2章 スクレイピングによるデータ収集
現状スクレイピングによるデータ収集を行う予定は無いので、この章はさらっと流そうと思う。
タイトルの抽出
パイプ演算子で処理を連結。ショートカットキー[Ctrl + shift + M]で %>% を挿入。
library(rvest)
#urlを変数に格納
kabu_url <- 'https://kabutan.jp/stock/kabuka?code=0000'
#urlの読み込み → title要素を抽出 → 文字列に変換
title2 <- read_html(kabu_url) %>%
html_node(xpath = '/html/head/title') %>%
html_text()
Chromeの開発ツール表示
[F12]でChromeの開発ツールを開き、XPathをコピーしhtml_node(xpath = ここに張り付け)。今回株価を取得するために書籍の図2.5では開発ツール内の「< table class='stock・・・」の部分のxpathをコピーするということのようだが、見当たらず断念した。
第3章 dplyr/tidyrによるデータ処理
この章ではデータの抽出、集計、結合の際に使用する関数を一通り知ることができた。この賞を終えたころにはデータの操作に加え、パイプライン処理にも慣れるであろう。
横型のデータと縦型のデータ
- gather()
- spread()
以下のような列数の多いデータは人間にとって見やすく情報が瞬時わかる。
しかしプログラム上で処理する際に好まれるデータは以下のように列数が少ない縦長のデータ。
横から縦に変換するのがgather(),縦から横に変換するのがspread()
library(tidyverse)
# 縦データに変換
gather(score,key = 'subject',value = 'score',2:5)
score.gather <- gather(score,key = 'subject',value = 'score',-1) ## 上と同じ
# 横データに変換
spread(score.gather,key = subject,value = score)
data.frame型とtibble型
見た目は同じ表形式のデータ。多少挙動に違いあり。
df <- data.frame(x = 1:5)
df[,'x']
tib <- tibble(x = 1:5)
tib[,'x']
データの操作
- filter() : 行の抽出
- arrange() : 並び替え
- select() : 列の抽出
- rename() : 列名の変更
- mutate() : 列の追加
- transrate() : 列の追加及び抽出
mpg %>% select(manufacturer,model,displ,year,cyl) %>% #列の抽出
filter(manufacturer == 'audi' & cyl >= 6) %>% # 行の抽出
mutate(century = ceiling(year / 100)) # 新しい列の作成
# 行の抽出
filter(mpg,manufacturer == 'audi') # 条件一致
filter(mpg,manufacturer == 'audi' & cyl >= 6) # 条件一致AND
filter(mpg,manufacturer == 'audi' | cyl >= 6) # 条件一致OR
filter(mpg,!(manufacturer == 'audi' | cyl >= 6)) # NOT条件一致OR
# データの並び替え
arrange(mpg,cty,hwy) # 昇順
arrange(mpg,-cty) # 降順
arrange(mpg,manufacturer) # 文字列も可能
arrange(mpg,-manufacrurer) # 文字列だとerror
arrange(mpg,desc(manufacturer)) # 文字列の降順
# 列の抽出
select(mpg,model,trans) # 指定した列
select(mpg,manufacturer:year) # 指定した列の間の区間すべて
select(mpg,-manufacturer) # 指定した列以外
select(mpg,MODEL = model,TRANS = trans) # 列名を変えて抽出
select(mpg,starts_with("c")) # 指定文字で始まる列すべて
# 列名の変更
rename(mpg,MODEL = model,TRANS = trans) # 指定した列
rename(mpg,aaa = ends_with("l")) # 指定した文字で終わる列
# 列の追加
mutate(mpg,cty.6 = if_else(cty >= 6,'up','down')) # 条件の真偽で追加する列内の要素を指定
mutate(mpg,cty = if_else(cty >= 6,'up','down')) # 既存の列に上書き
# 列の追加および抽出
transmute(mpg,cty.6 = if_else(cty >= 6,'up','down'),year)
データの集計、グルーピング
- summarise() :
- group_by() :
# 集計
summarise(mpg,displ.max = max(displ))
# グループ化
mpg.group <- group_by(mpg,manufacturer,year) # 指定の列でグルーピング
mpg.group
transmute(mpg.group,displ.rank = rank(displ,ties.method = 'max')) # グループ毎にランク付け
filter(mpg.group,n() >= 20) # データ数が20以上あるグループを抽出
summarise(mpg.group,max(displ)) # グループごとに最大値
ungroup(mpg.group) # グルーピング解除
ウィンドウ関数
rank(c(1,20,3)) #大きさの順位を返す
lag(1:10) # 1づつずらして表示。ずらした部分はNA
lag(1:10,n = 3)# nづつずらして表示。ずらした部分はNA
uriage <- tibble(day = c(1,1,2,2,3,3,4,4),
store = c('a','b','a','b','a','b','a','b'),
sales = c(100,500,200,500,400,500,800,500))
# 一つ前の値を引く
uriage %>% group_by(store) %>%
mutate(sales.diff = sales - lag(sales))
# 平均値を引く
uriage %>%
group_by(store) %>%
mutate(sales.err = sales - mean(sales))
文字列を列名として使用したい場合
mutate(mpg,cyl2 = sqrt('cyl')) #error
col <- as.name('cyl')
mutate(mpg,cyl2 = sqrt(!!col))
複数の列をまとめて操作
横型のデータを複数列まとめて操作したい場合は、一度縦型に変換してから操作を行い横型に戻すと良い。
set.seed(1)
d <- tibble(id = 1:10,
test1 = runif(10,max = 100),
test2 = runif(10,max = 100),
test3 = runif(10,max = 100),
test4 = runif(10,max = 100),
)
d.gather <- gather(d,key = 'test',value = 'value',test1:test4)
d.gather
mutate(d.gather,value = round(value))
d.gather %>%
group_by(test) %>%
summarise(value.avg = mean(value))
d.gather %>%
mutate(value = round(value)) %>%
spread(key = test,value = value)
d %>%
gather(key = 'test',value = 'value',-id) %>%
filter(value > 50) %>%
spread(key = test,value = value)
横型データに対して複数列まとめて操作する関数もある。
# 列全体に対し丸める
d %>% mutate_all(round)
# 数値データのみに対し丸める
mpg %>% mutate_if(is.numeric,round)
# 指定した列のみに対し丸める
d %>% mutate_at(c('test1','test2','test3','test4'),round)
# 指定した列以外に対し丸める
d %>% mutate_at(vars(-id),round)
データの結合
tenki <- tibble(day = c(1,2,3,4),
rain = c(F,F,T,F))
# 両方に共通して存在する行で結合
inner_join(uriage,tenki,by = "day")
tenki2 <- tibble(day2 = c(1,2,3,4),
rain = c(F,F,T,F))
# 名前の異なる列どうしを結合
inner_join(uriage,tenki2,by = c('day' = 'day2'))
tenki3 <- tibble(DAY = c(1,1,2,2,3),
store = c('a','b','a','b','b'),
rain = c(F,F,T,F,T))
# 左側のデータにないデータは欠落
inner_join(uriage,tenki3,by = c('day' = 'DAY',"store"))
# 左側にないデータはNAとし残す
res <- left_join(uriage,tenki3,by = c('day' = 'DAY',"store"))
# NAをFALSEで置き換える
mutate(res,rain = replace_na(rain,F))
# 上に同じ
replace_na(res,list(rain = F))
# NAを一つ前の値で置き換える
res %>%
group_by(store) %>%
arrange(day) %>%
fill(rain) %>%
ungroup()
データの絞り込み
tenki4 <- tibble(day = c(2,2,3),
store = c('a','b','a'),
rain = c(F,F,T))
# 両方に共通する列のみ抽出
semi_join(uriage,tenki4,by = c('day','store'))
第4章 ggplot2によるデータ可視化
ggplotの基礎
- ggplot()
- geom_histogram()
- geom_density()
- geom_point()
- geom_smooth()
# tidyverseの中にgplot2も含まれる
library(tidyverse)
# ヒストグラム
ggplot() + geom_histogram(data = mpg,mapping = aes(x = displ))
# 分布曲線
ggplot() + geom_density(data = mpg,mapping = aes(x = displ))
# 散布図と近似曲線
ggplot() + geom_point(data = mpg,mapping = aes(x = displ,y = cty)) +
geom_smooth(data = mpg,mapping = aes(x = displ,y = cty),method = 'lm')
# 上に同じ。記述量を減らした書き方
ggplot(data = mpg,mapping = aes(x = displ,y = cty)) +
geom_point() +
geom_smooth(method = 'lm')
mpg1999 <- filter(mpg,year == 1999)
mpg2008 <- filter(mpg,year == 2008)
# 複数のデータフレームから一つの図を作成
ggplot(mapping = aes(x = displ,y = cty)) +
geom_point(data = mpg1999) +
geom_point(data = mpg2008)
グループ化
- group = ,colour =
# cylでグループ化して色分け
ggplot(data = mpg,mapping = aes(x = displ,y = cty,group = cyl,colour = cyl)) +
geom_point()
# cylを離散値として扱う
ggplot(data = mpg,mapping = aes(x = displ,y = cty,group = factor(cyl),colour = factor(cyl))) +
geom_point()
# cylグループ毎に近似曲線
ggplot(data = mpg,mapping = aes(x = displ,y = cty)) +
geom_point() +
geom_smooth(mapping = aes(group = factor(cyl),colour = factor(cyl)),method = 'lm')
# グループ毎に点の形を変える
ggplot(data = mpg,mapping = aes(x = displ,y = cty)) +
geom_point(mapping = aes(colour = factor(cyl),shape = factor(cyl)))
# グループ毎に近似曲線の色とタイプを変える
ggplot(data = mpg,mapping = aes(x = displ,y = cty)) +
geom_point() +
geom_smooth(mapping = aes(colour = factor(year),linetype = factor(year)),method = 'lm')
# 色(colour),近似曲線の誤差範囲を表示するか否か(se)
ggplot(data = mpg,mapping = aes(x = displ,y = cty)) +
geom_point(colour = 'chocolate',shape = 35,size = 10) +
geom_smooth(method = 'lm',linetype = 'dashed',se = FALSE)
# 形状(shape),大きさ(size),透明度 (alpha)を変更
ggplot(data = mpg,mapping = aes(x = displ,y = cty,colour = factor(cyl))) +
geom_point(shape = 17,size = 4,alpha = 0.4)
図にデータの追加と注釈の追加
- annotate()
# 図にデータと注釈を追加
add.x <- c(2.5,3,3.5)
add.y <- c(25,27.5,30)
ggplot(data = mpg,mapping = aes(x = displ,y = cty)) +
geom_point() +
annotate(geom = 'point',x = add.x,y = add.y,colour = 'red') +
annotate(geom = 'text',x = c(5,5),y = c(30,25),label = c('abcd','xyz'))
複数のパネルを表示
- facet_wrap()
- facet_grid()
# 複数のパネルを表示
ggplot(data = mpg,mapping = aes(x = displ,y = cty)) +
geom_point() +
facet_wrap(~ cyl)
# 縦に分割
ggplot(data = mpg,mapping = aes(x = displ,y = cty)) +
geom_point() +
facet_grid(.~ cyl)
# 横に分割
ggplot(data = mpg,mapping = aes(x = displ,y = cty)) +
geom_point() +
facet_grid(cyl ~.)
# 2つ以上の変数を用いて分割
ggplot(data = mpg,mapping = aes(x = displ,y = cty)) +
geom_point() +
facet_wrap(year ~ cyl)
ggplot(data = mpg,mapping = aes(x = displ,y = cty)) +
geom_point() +
facet_grid(year ~ cyl)
集計結果をグラフ表示
- stat_summary
- stat = ,fun.y =
# 集計結果をグラフ表示
mean.cty <- mpg %>%
group_by(class) %>%
summarise(cty = mean(cty))
ggplot(data = mean.cty,mapping = aes(x = class,y = cty)) +
geom_bar(stat = 'identity')
# 集計と描画を同時に行う
ggplot(data = mpg,mapping = aes(x = class,y = cty)) +
geom_bar(stat = 'summary',fun.y = 'mean')
# 上に同じ
ggplot(data = mpg,mapping = aes(x = class,y = cty)) +
stat_summary(geom = 'bar',fun.y = 'mean')
# 平均値と最大値、最小値を算出し描画
ggplot(data = mpg,mapping = aes(x = class,y = cty)) +
stat_summary(geom = 'pointrange',fun.y = 'mean',fun.ymax = 'max',fun.ymin = 'min')
# 平均値と平均値±標準偏差
mean_se(mpg$cty)
# 棒グラフと標準誤差を描画
ggplot(data = mpg,mapping = aes(x = class,y = cty)) +
stat_summary(geom = 'bar',fun.y = 'mean',fill = 'grey') +
stat_summary(geom = 'pointrange',fun.data = 'mean_se')
# 「group = 1」をつけないとエラーになる
ggplot(data = mpg,mapping = aes(x = factor(year),y = displ)) +
stat_summary(fun.y = 'mean',geom = 'line',group = 1)
# 集計棒グラフ+散布図を色分け表示
ggplot(data = mpg,mapping = aes(x = class,y = cty)) +
stat_summary(geom = 'bar',fun.y = 'mean') +
geom_point(mapping = aes(colour = class),show.legend = F)
# バイオリングラフ+散布図を色分け表示
ggplot(data = mpg,mapping = aes(x = class,y = cty)) +
geom_violin(scale = 'count') +
geom_point(mapping = aes(colour = class),show.legend = F)
# 散布図の重なりを防ぐ
ggplot(data = mpg,mapping = aes(x = class,y = cty)) +
stat_summary(geom = 'bar',fun.y = 'mean') +
geom_point(mapping = aes(colour = class),position = position_jitter(),show.legend = F)
# 水平方向のみ散らばりを与える
ggplot(data = mpg,mapping = aes(x = class,y = cty)) +
stat_summary(geom = 'bar',fun.y = 'mean') +
geom_jitter(mapping = aes(colour = class),width = 0.4,height = 0,show.legend = F)
配置指定
- position =
# position = 'identity' 配置指定なし
ggplot(data = mpg,mapping = aes(x = class,y = cty,fill = factor(year))) +
stat_summary(geom = 'bar',fun.y = 'mean') +
stat_summary(fun.data = 'mean_se')
# position = 'stack' 積み上げ
ggplot(data = mpg,mapping = aes(x = class,y = cty,fill = factor(year))) +
geom_bar(stat = 'summary',fun.y = 'mean') +
stat_summary(fun.data = 'mean_se')
#position = 'dodge' 水平方向に並べる
ggplot(data = mpg,mapping = aes(x = class,y = cty,fill = factor(year))) +
stat_summary(geom = 'bar',fun.y = 'mean',position = position_dodge()) +
stat_summary(fun.data = 'mean_se',position = position_dodge(width = 0.9))
軸の変更
- xlim()
- ylim()
- coord_cartesian()
- coord_flip()
ggplot(data = mpg,mapping = aes(x = displ,y = cty)) +
geom_point() +
geom_vline(xintercept = 4) +
geom_hline(yintercept = 15) +
geom_smooth(method = 'lm',se = F)
# 軸の変更
ggplot(data = mpg,mapping = aes(x = displ,y = cty)) +
geom_point() +
xlim(1.5,4.5) +
ylim(10,35) +
geom_vline(xintercept = 4) +
geom_hline(yintercept = 15) +
geom_smooth(method = 'lm',se = F)
# 軸の拡大
ggplot(data = mpg,mapping = aes(x = displ,y = cty)) +
geom_point() +
coord_cartesian(xlim = c(1.5,4.5),ylim = c(10,35)) +
geom_vline(xintercept = 4) +
geom_hline(yintercept = 15) +
geom_smooth(method = 'lm',se = F)
ggplot(data = mpg,mapping = aes(x = drv,y = cty)) +
geom_violin() +
stat_summary(fun.data = 'mean_se')
# 軸の反転
ggplot(data = mpg,mapping = aes(x = drv,y = cty)) +
geom_violin() +
stat_summary(fun.data = 'mean_se') +
coord_flip()
図の保存
- ggsave()
g <- ggplot(data = mpg,mapping = aes(x = class,y = cty)) +
stat_summary(geom = 'bar',fun.y = 'mean')
# dpi = 解像度,height = 高さ,width = 幅,units = 単位,
ggsave(plot = g,filename = 'cty_class.png',dpi = 300,height = 20,width = 20,units = 'cm')
テーマの変更
- theme_classic()
- theme_bw()
- theme()
# テーマの変更
ggplot(data = mpg,mapping = aes(x = displ,y = cty)) +
theme_classic() +
geom_point()
# メモリ線あり
ggplot(data = mpg,mapping = aes(x = displ,y = cty)) +
theme_bw() +
geom_point()
# 文字サイズとフォントを変更
ggplot(data = mpg,mapping = aes(x = displ,y = cty)) +
theme_classic(base_size = 25,base_family = 'serif') +
geom_point()
# X軸のラベルの回転
ggplot(data = mpg,mapping = aes(x = class,y = cty)) +
geom_boxplot() +
theme(axis.text.x = element_text(angle = 45,hjust = 1))
# デフォルトのテーマの上書き
theme_set(theme_bw() + theme(axis.text.x = element_text(angle = 45,hjust = 1)))
ggplot(data = mpg,mapping = aes(x = class,y = cty)) +
geom_boxplot()
配色の変更
- scale_fill_manual()
- scale_fill_grey()
- scale_fill_brewer()
- scale_fill_viridis()
- scale_fill_colorblind()
# デフォルトの配色
ggplot(data = mpg,mapping = aes(x = drv,y = cty,fill = drv)) +
geom_boxplot()
# 手動で配色を変更
ggplot(data = mpg,mapping = aes(x = drv,y = cty,fill = drv)) +
geom_boxplot() +
scale_fill_manual(values = c('4' = 'black','f' = 'grey','r' = '#ffffff'))
# 半自動で色を変更
ggplot(data = mpg,mapping = aes(x = drv,y = cty,fill = drv)) +
geom_boxplot() +
scale_fill_grey(start = 0.4,end = 0.9)
ggplot(data = mpg,mapping = aes(x = drv,y = cty,fill = drv)) +
geom_boxplot() +
scale_fill_grey(start = 1,end = 0)
install.packages('RColorBrewer')
library(RColorBrewer)
display.brewer.all()
# 配色の選択
ggplot(data = mpg,mapping = aes(x = drv,y = cty,fill = drv)) +
geom_boxplot() +
scale_fill_brewer(palette = 'Greys')
ggplot(data = mpg,mapping = aes(x = drv,y = cty,fill = drv)) +
geom_boxplot() +
scale_fill_brewer(palette = 'Paired')
# Pythonのmatplotlobの色を使用できる
install.packages('viridis')
library(viridis)
ggplot(data = mpg,mapping = aes(x = drv,y = cty,fill = drv)) +
geom_boxplot() +
scale_colour_viridis()
ggplot(data = mpg,mapping = aes(x = drv,y = cty,fill = drv)) +
geom_boxplot() +
scale_fill_viridis(discrete = T)
install.packages("ggthemes")
library(ggthemes)
ggplot(data = mpg,mapping = aes(x = drv,y = cty,fill = drv)) +
geom_boxplot() +
scale_fill_colorblind()
ラベルの変更
- labs()
ggplot(data = mpg,mapping = aes(x = displ,y = cty,group = factor(cyl),colour = factor(cyl))) +
geom_point() +
labs(title = 'エンジンの大きさと燃費の関係',subtitle = "1999,2008",caption = '出典xxx',x = 'エンジンの大きさ(L)',y = '燃費(mpg)',colour = 'シリンダー数')
GUIベースでggplotオブジェクトを作成
- ggplot_shiny()
install.packages("ggplotgui")
library(ggplotgui)
ggplot_shiny(mpg)
複数のグラフをパネルで並べる
- ggarrange()
install.packages("ggpubr")
library(ggpubr)
g1 <- ggplot(data = mpg,mapping = aes(x = displ,y = cty)) +
theme_classic() +
geom_point(colour = 'seagreen')
g2 <- ggplot(data = mpg,mapping = aes(x = displ,y = hwy)) +
theme_classic() +
geom_point(colour = "lightskyblue")
# パネルを横に並べる。(hjust:ラベルの位置)
ggarrange(g1,g2,labels = c('市街地','高速道路'),ncol = 2,hjust = -1.5)
# パネルを縦に並べる
ggarrange(g1,g2,labels = c('市街地','高速道路'),nrow = 2,hjust = -3)
表示される順番を変更
- fct_inorder()
# 表示される順番を変更
weight_change <- data.frame(time = c('pre','post_2days','post_10days','post_20days'),
weight = c(60,60,57,55))
# アルファベット順に表示されてしまう
g1 <- ggplot(data = weight_change,mapping = aes(x = time,y = weight)) +
geom_point() +
geom_line(group = 1)
# 表示される順番を固定
weight_change$time <- fct_inorder(weight_change$time)
g2 <- ggplot(data = weight_change,mapping = aes(x = time,y = weight)) +
geom_point() +
geom_line(group = 1)
ggarrange(g1,g2)
第4章 RMarkdownによるレポート生成
見出し
行頭に# がある行は、見出しとして判断される。
# 見出し1
## 見出し2
### 見出し3
#### 見出し4
改行
空行もしくは文末に半角スペース2つで改行。
空行
空行
半角スペースなし
半角スペースなし
半角スペースあり
半角スペースあり
箇条書き
- 箇条書き1
- 箇条書き2
- 箇条書き2-1
- 箇条書き2ー1-1
1. 数字つき箇条書き
1. 数字
2. 数字付き箇条書き
リンク
[Rリンク](https://www.r-project.org/)
画像ファイル
![](キャプチャ1.PNG)
引用
> 引用分
> 引用文2
強調文
この文では*aa*が斜体になります
この文では**aa**が斜体になります
数式
数式$\theta=0.25$
$$
数式ブロック\theta=0.25
$$
表
担当課所|執筆者
--------|------
第1章、第2章|松村
第3章|白石
Rインラインチャンク
`r `
data.frameの表示方法
df <- head(iris)
knitr::kable(df)
htmlwidgets系パッケージ
library(plotly)
plot_ly(iris,x = ~Species,y = ~Sepal.Length,type = "box")
ドキュメントの設定
---
title: "Untitled"
author: "齋藤"
date: "2021/01/02"
output:
html_document:
toc: true
toc_float: true
---
まとめ
読了してみての感想として、この書籍はこれからR言語でデータ分析を始める初心者にとっては、一冊持っておいて損は無いと思う。非常に難易度が低く、データの読み込み、整形、可視化、レポート作成の基礎がコンパクトにまとめられていてそれほど勉強時間もかからない。自分は読みながら、コードを写経し実行させ、学習内容をまとめるといった作業を13時間ほどかけて行った。
既にRでデータ分析を行っている中級者にとってはわざわざ買うほどのものではないかもしれないが、3章のdplyrによるデータ整形や4章のggplotによる可視化の部分は新しい発見があるかもしれない。ちなみに自分はあった。
dplyrやggplotの関数を当たり前に使いこなせる上級者にとってはこの本は必要ないであろう。
Discussion