R言語を1年ぐらい使っていて身についたちょっとしたTips

  • 11
    いいね
  • 0
    コメント

この記事はR Advent Calendar 2016の13日目の記事です。

さすがR言語のカレンダーだけあって、皆さん数学的な記事やデータサイエンスに関するレベルの高い記事ばかりで勉強になります。
そんな中、本日はゆるっとした内容となっていますので、力を抜いてご覧ください。

今年1年、R言語を触っていて「あれ、こういう時どう書くんだろう」とか「こういう機能ってあるんだろうか」と思って調べ、役に立ったものを列挙します。

factor型とdroplevels

あまり型を気にせずに集計を行っているとたまにこういうことに出くわしました。

#データ
raw <-data.frame("name"=c("A","B","C","B","C"),
                 "value"=c(80,130,90,120,110))
#いくつあるか確認
table(raw$name)
>A B C 
>1 2 2 

#valueが85以上のデータを集計したい
sub <- raw[85<raw$value,]
# sub <- subset(sub, 85<value) #subsetでも可

table(sub$name)
>A B C 
>0 2 2 
# Aはないはずなのに集計すると出てくる。

このようにデータを絞り込んでも元のFactor型の情報が保持されていて、集計やグラフを作ると出てくることがあります。
そんなときはdroplevels関数を使うと綺麗にしてくれます。

sub <- droplevels(sub)
table(sub$name)
>B C 
>2 2

CSVを型指定で読み込む

csvファイルを読み込むときに、明示的に各データの型を指定して読み込むにはcolclassesで指定できます。NAを指定するとその列だけ自動判定になります。

data1 <- read.csv("data/sample.csv")
str(data1)
>'data.frame':  5 obs. of  3 variables:
> $ date : Factor w/ 2 levels "2016-12-13","2016-12-14": 1 1 1 2 2
> $ name : Factor w/ 3 levels "A","B","C": 1 2 3 2 3
> $ value: int  80 130 90 120 110

data2 <- read.csv("data/sample.csv",colClasses = c("Date","character",NA))
str(data2)
>'data.frame':  5 obs. of  3 variables:
> $ date : Date, format: "2016-12-13" "2016-12-13" "2016-12-13" ...
> $ name : chr  "A" "B" "C" "B" ...
> $ value: int  80 130 90 120 110

おまけ。
rep関数でNAをリピートすることもできるので、列数が多いファイルで
colClasses = c("Date",rep(NA,10),"integer")
のように使うこともできて楽です。

apply関数の第4引数

apply関数群全般ですが、データの指定とどのように処理するかの関数を渡します。
この時、外部のデータを参照したい場合があります。わかりにくいのでソースをどうぞ。

raw <-data.frame("種別"=c("A","B","C","B","C"),
                 "値段"=c(80,130,90,120,110))
消費税<-1.08
# 各商品に消費税をかけてから合計したい!
tapply(raw$`値段`,raw$`種別`,function(v){sum(v)})
>  A   B   C 
> 80 250 200 

こういった場合、tapplyの第4引数以降に指定したものが、集計関数の第2引数以降に入ってきます。これもわかりにくいのでソースで。

tapply(raw$`値段`,raw$`種別`,function(v,tax){sum(v*tax)},消費税)
>    A     B     C 
> 86.4 270.0 216.0

実はRならこういう書き方でもできてしまうんですが、集計関数内から直接外部を見に行くのは不自然ですね。

# できてしまうが、あまりおすすめできない
tapply(raw$`値段`,raw$`種別`,function(v){sum(v*消費税)})

自作関数以外でも同じなので、meanやmaxなどの計算でNAが含まれるデータに対してNAを除外した計算などもna.rm=Tを追加することで楽に対応できます。

raw <-data.frame("種別"=c("A","B","C","B","C"),
                 "値段"=c(80,130,90,NA,110))
tapply(raw$`値段`,raw$`種別`,mean)
>  A   B   C 
> 80  NA 100 
tapply(raw$`値段`,raw$`種別`,mean,na.rm=T)
>  A   B   C 
> 80 130 100

warningメッセージの抑制

R MarkdownなどでRの出力からドキュメントを作ろうとしたとき、warningのメッセージを非表示にしたい場合がありました。
optionを指定することで可能です。

options(warn=-1)

library(・・・)
# 読み込んだよーというメッセージがでなくなる

他にも、出力時の折り返し文字列数を変えるoptions(width=xx)や数値の表示桁数を変えるoptions(digits=xx)などがあります。
便利。

永続代入

できるだけグローバル変数は使わないほうがよいですが、どうしても必要になった場合、<<-演算子を使うと関数内からグローバル変数へアクセスできます。

raw <-data.frame("種別"=c("A","B","C","B","C"),
                 "値段"=c(80,130,90,NA,110))

func <- function(){
  #raw[raw$`種別`=="B",c("値段")] <- 999 #これでは無理
  raw[raw$`種別`=="B",c("値段")] <<- 999
}
func()
raw
>  種別 値段
>1    A   80
>2    B  999
>3    C   90
>4    B  999
>5    C  110

Trueの数

何かの条件を満たす要素の数を数えるときはsumで大丈夫

raw <- c(0,1,2,3,3,4,2,0)
2 < raw
> [1] FALSE FALSE FALSE  TRUE  TRUE  TRUE FALSE FALSE
sum(2<raw)
> [1] 3

繰り返し項目の判定

要素の重複を除去したい場合はuniqueですが、重複が発生している場所を確認したい場合はduplicatedが便利です。

raw <- c(0,1,2,3,3,4,2,0)
duplicated(raw)
>[1] FALSE FALSE FALSE FALSE  TRUE FALSE  TRUE  TRUE

raw[!duplicated(raw)]
>[1] 0 1 2 3 4

オーバーライド

変数へ値を代入するイメージで関数のオーバーライドも可能です。
演算子をオーバーライドすることも可能です。
使い所は…いつだろう…。

5 + 3
> 8

`+` <- `-`

5 + 3
> 2

data.frameからxts形式へ変換

時系列情報を扱うのに便利なxtsパッケージがあります。
csvファイルやdata.frameからその時系列形式であるxts型に変換する方法をよく忘れるのでメモです。
csvから読み込む場合はread.zooを使うと便利で、data.frameから変換する場合はorder.byを指定するのがポイントです。

#CSVファイルから読む場合
raw.xts <- as.xts(read.zoo("sample.csv",header=T,sep=","))

raw.xts
>           name value
>2016-12-13 "A"  " 80"
>2016-12-13 "B"  "130"
>2016-12-13 "C"  " 90"
>2016-12-14 "B"  "120"
>2016-12-14 "C"  "110"

#元ファイルの日付項目をDate型指定で読み込んでおきます
raw <- read.csv("sample.csv",header=T,colClasses = c("Date",NA,NA))

## order.byで日付項目を指定
as.xts(raw[,-1], order.by=raw[,1])
>           name value
>2016-12-13 "A"  " 80"
>2016-12-13 "B"  "130"
>2016-12-13 "C"  " 90"
>2016-12-14 "B"  "120"
>2016-12-14 "C"  "110"

まだまだあった気はするんですが、ひとまず思いついたものを列挙してみました。
Rカレンダーを書いているみなさまには常識的なところかもしれませんが、こういう細かいとこをすぐ忘れてしまうので。

この投稿は R Advent Calendar 201613日目の記事です。