こんにちは。産婦人科医のtommyです。(Twitter:@obgyntommy)
本記事ではRを用いたデータフレームの計算チュートリアルを行います。
対象としては、Rに全く触れたことのない方やRの学習初心者の方向けになります。
非常に簡単な四則演算の内容になりますが、RStudioを用意して実際に手を動かしてみてください。
RStudioを手軽にセットアップするにはRStudio Cloudが便利です。詳しい内容は以下を参照してください。
R Studio Cloudの使い方
続きを見る
R Studio Cloud で学習される方はこちらを参照してください。
また、RStudio Cloudではなくても、Rのインストール方法やRStudioの使い方については以下の記事を参照してください。
Rのインストールの方法とRStudioの使い方
続きを見る
では早速みていきましょう。
分析を行う際には、データを構造データと非構造データの2種類に大きく分けます。
構造データは行・列からなるテーブル形式で表すことのできるもの、非構造データは音声やテキスト、画像など、テーブル形式では表すのが難しいものを指します。構造データのなかには実に様々な保存形式がありますが、最も一般的なのがエクセル(.xlsx)やCSVでしょう。こうしたデータをRで読み込むとき、たいていはデータフレームのオブジェクトとして読み込みます。
つまり、データフレームの操作方法を知っていれば、大方の構造データは分析を少なからず始めることができるようになります。
1. データフレームの読み込み方
1-1. read.csvとread.csv2の違い
CSVファイルが手元にある時、Rに読み込む方法はread.csv()とread.csv2()の2種類のうちいずれかを使うことができます。この2つはほとんど同じ関数ですが、CSV内のデータの区切りがカンマかセミコロンかによってどちらを使うかを決める必要があります。カンマ区切りの場合はread.csv()、セミコロン区切りの場合はread.csv2()を使います。また小数点にピリオドが使われている場合はread.csv()、カンマが使われている場合はread.csv2()になります。どうしてこのような違いが生まれるかというと、例えばフランスなど、国によって小数点にカンマを使う場合があるためです。
どちらを使えばよいかわからない場合、CSVファイルをお手元のテキストエディタで直接開いてみましょう。
以下のように区切りがカンマ、小数点にピリオドが使われている場合、read.csv()でOKです。
Number,Letter,Animal
1.1,A,Alligator
2.1,A,Anteater
3.1,B,Bison
4.1,Z,Zebra
以下のように区切りがセミコロン、小数点にカンマが使われている場合、read.csv2()を使って開きます。
Number,Letter,Animal
1,1;A;Alligator
2,1;A;Anteater
3,1;B;Bison
4,1;Z;Zebra
他にもread.csv()とread.csv2()には関数にわたすことのできる引数がいくつかあります。うち大事なのはheaderです。
CSVファイルがヘッダー(読み込んだときにカラム名となる行)を含んでいる場合、header=TRUE(初期設定ではTRUEになっています)、そうでない場合はFALSEに設定します。
以下、自分の手元にCSVファイルを用意して、read.csv()かread.csv2()を使って読み込む練習をしてみましょう。
{r}
# PATHは自分自身のCSVファイルのPATHに変えてください。
PATH <- "data/Presidential Election 2020/president_county.csv"
data <- read.csv(file=PATH, header=TRUE)
head(data) # head()は最初の6行だけを表示します
1-2. エクセルファイルの読み込み方
エクセルファイルを読み込むにはread_excel()という関数を使う必要があります。ただread_excel()はRStudioにはじめから付いてくる関数ではなく、tidyverseというパッケージグループの中の、readxlというパッケージ内に入っている関数です。ですのでreadxlというパッケージをまずインストールする必要があります。パッケージのインストールの仕方は別の回で詳しく説明しますので、今回はとりあえず下のコードを実行してください。
{r}
# install.packages("tidyverse")
install.packages("readxl")
trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/readxl_1.3.1.tgz'
Content type 'application/x-gzip' length 1646895 bytes (1.6 MB)
==================================================
downloaded 1.6 MB
The downloaded binary packages are in
/var/folders/h1/yk7v45ss33z86wql6b7zvtch0000gp/T//RtmpuS8JuZ/downloaded_packages
次に、インストールしたreadxlパッケージを呼び出します。
{r}
library(readxl)
これでようやくread_excel()関数が使えるようになります。使い方はread.csvとほとんど同じで、エクセルファイルがおいてあるパスを引数として渡すだけです。自分の手元にエクセルファイルを用意して、読み込む練習をしてみましょう。
{r}
# PATHは自分自身のxlsxファイルのPATHに変えてください。
PATH_EXCEL <- "data/westnile.xlsx"
read_excel(PATH_EXCEL, n_max = 10)
2. データフレームの情報を取得する
さて、ここからはRStudioにはじめからついてくるデータセット、"iris"を使ってデータフレームの練習をしていきましょう。"iris"とは、1936年にR. A. FISHERらが投稿した「THE USE OF MULTIPLE MEASUREMENTS IN TAXONOMIC PROBLEMS(分類学の問題に対する多重測定の使用)」という論文に掲載されているデータで、3種のアヤメ(setosa, versicolor, virginica)のがくの長さ(Sepal.length)と幅(Sepal.Width)、花びらの長さ(Petal.Length)と幅(Petal.width)を50サンプルについて調べたデータです。irisデータは単純にirisと打てば呼び出すことができます。呼び出したときには既にデータフレームになっています。
{r}
data("iris")
iris
2-1. 大きさを把握する
データフレームを呼び出す時、その行数と列数を把握するのは非常に重要な作業です。大きさを把握することで、計算が重くなりそうなデータかどうかがわかります。また列の追加・削除をするときにも、元のデータフレームの大きさと比べることでその作業がきちんと実行できたかどうかを確認できます。
大きさを把握するにはdim()関数を使います。dim()関数は行、列の順番に値を返します。
{r}
dim(iris)
[1] 150 5
説明した通り、変数はがくの長さと幅、花びらの長さと幅、アヤメの種類の5つなので列数は5です。3種類のアヤメのそれぞれに50サンプル集めたので、行数は全部で150となっています。
2-2. 最大・最小、平均値など要約を出力する
summary()関数を使うと、データフレームに収納されているすべての変数で最大値、最小値、平均値などを一度に計算することができます。
{r}
summary(iris)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
Min. :4.300 Min. :2.000 Min. :1.000 Min. :0.100 setosa :50
1st Qu.:5.100 1st Qu.:2.800 1st Qu.:1.600 1st Qu.:0.300 versicolor:50
Median :5.800 Median :3.000 Median :4.350 Median :1.300 virginica :50
Mean :5.843 Mean :3.057 Mean :3.758 Mean :1.199
3rd Qu.:6.400 3rd Qu.:3.300 3rd Qu.:5.100 3rd Qu.:1.800
Max. :7.900 Max. :4.400 Max. :6.900 Max. :2.500
上から
+ Min:平均値
+ 1st Qu.:第一四分位数
+ Median:中央値
+ Mean:平均値
+ 3rd Qu.:第三四分位数
+ Max.:最大値
となっています。
このsummaryでは標準偏差などの情報を得ることはできません。標準偏差を出すにはsd()を使いますが、これは一度に複数の変数に対して使うことができない関数です。そのためirisの後ろにドルサイン($)を付けて、カラム名を指定したうえで使う必要があります。
{r}
sd(iris$Sepal.Length)
[1] 0.8280661
ただし、apply()関数と組み合わせて使うことで、sd()関数を複数のカラムにいっぺんに適用することも可能です。apply()関数が取る引数は次の4つです。
+ X: FUNで定めた関数を適用するデータフレームやマトリックス
+ FUN: Xに適用する関数
+ MARGIN: FUNで定めた関数を適用する方向。行ごとの場合は1, 列ごとの場合は2
+ na.rm: 欠損値を取り除くかどうか。TRUEもしくはFALSE
{r}
apply(X = iris[-5], FUN = sd, MARGIN = 2, na.rm = TRUE)
Sepal.Length Sepal.Width Petal.Length Petal.Width
0.8280661 0.4358663 1.7652982 0.7622377
irisの後ろに[-5]としているのは、speciesカラムをFUNの適用から取り除くためです。speciesはカテゴリカル変数なので、標準偏差は出せません。
2-3. カラム名を出力する
カラム名だけを出力したい場合はcolnames()を使います。
{r}
colnames(iris)
[1] "Sepal.Length" "Sepal.Width" "Petal.Length" "Petal.Width" "Species"
3. データフレームの変形
3-1. 行・列の並び替え
カラムの並び替えをするには、まず並べたい順番に入れ替えたカラム名をベクトルでつくります。
{r}
col_order <- c("Species", "Petal.Width", "Sepal.Length",
"Sepal.Width", "Petal.Length")
次に、そのベクトルを[,]を使ってもとのデータフレームに適用します。ベクトルをカンマの右側に入れるのがポイントです。
{r}
iris_new <- iris[, col_order]
iris_new
3-2. データフレームの一部を切り出す
データフレームの一部を切り出すにはいくつか方法があります。
ドルサインを使う。これはカラムをひとつだけ取り出す際に使えます。
{r}
head(iris$Sepal.Length)
[1] 4.6 5.0 5.4 4.6 5.0 4.4
[]を使う。取り出したいカラムのインデックスを数字で表し、カギカッコの中に入れます。
{r}
iris[1]
コロン:を使うと複数の連続したカラムを取り出すことができます。下では1~3列目までを取り出しています。
{r}
iris[1:3]
マイナスサインをつけると、そのカラムを除いたほか全てを取り出すことができます。
{r}
iris[-5]
カギカッコは[行,列]のようにコンマの左側に行、右側に列の情報を入れることでより自由自在に切り出しができるようになります。
1行だけ取り出す
{r}
iris[3,]
連続した複数行を取り出す
{r}
iris[3:5,]
{r}
iris[3:5, 2:3]
取り出したい複数のカラムが連続していないときは、インデックスをベクトルに入れてカンマの右側に置きます。
{r}
iris[,c(1,5)]
カラムの名前を入れても同じです。
{r}
iris[,c("Sepal.Length", "Sepal.Width")]
セル内の値を元にデータフレームの一部を切り出すこともできます。それには条件演算子(==, =>, =<など)を使います。下の例ではSpeciesカラムの値がsetosaの行のみを取り出しています。
{r}
iris[iris$Species == "setosa", ]
3-3. 指定したカラムを削除する、新しいカラムを追加する
NULLに置き換えてしまうことで、指定したカラムを削除することができます。
{r}
col_species <- iris$Species #Speciesのカラムを一旦避難させておく
iris$Species <- NULL
iris
新しくカラムを追加するには、ドルサインで名前を指定して、ベクトルを代入すればOKです。
{r}
iris$Species <- col_species
iris
またはcbind()関数を使って新しいカラムを追加できます。
{r}
cbind(iris, col_species)
3-4. 指定した行を削除する、新しい行を追加する
行を削除するには、[,]のカンマの左側にマイナスサイン付きで消したい行のインデックスをベクトルに入れて指定します。
{r}
# 1から3行目までを削除
new_rows <- iris[c(1:3),] # 消したい行を避難
iris <- iris[-c(1:3),]
iris
新しく行を追加するには、rbind()を使ってもとのデータフレームと追加したい行をまとめます。
{r}
iris <- rbind(iris, new_rows)
tail(iris)
3-5. 行・列どうしの四則演算
異なるカラムどうしの四則演算も単純に演算子を使ってできます。
{r}
# +サインを-, *, /などで置き換えてみてください。
head(iris$Sepal.Length + iris$Sepal.Width)
[1] 7.7 8.6 9.3 8.0 8.4 7.3
ここで便利な関数rowSums()を紹介します。例えば合計したいカラムが100本ある場合、それらをいちいちドルサインと+記号を使って書いていくのはとても面倒です。そんな時、rowSums()を使えば()内に入れたデータフレームのすべてのカラムを合計してくれます。
以下では、Speciesを除いたすべてのirisのカラムを足し合わせる計算をしています。Speciesは文字列のカラムであるため、取り除かないとこの計算ができません。
{r}
head(rowSums(iris[, -5]))
4 5 6 7 8 9
9.4 10.2 11.4 9.7 10.1 8.9
複数の行どうしを足し合わせる場合には、colSums()という関数を使うことができます。以下ではirisのすべての行を足し合わせる計算をしています。ここでもやはりSpeciesの文字列カラムを取り除く必要があります。
{r}
colSums(iris[, -5])
Sepal.Length Sepal.Width Petal.Length Petal.Width
876.5 458.6 563.7 179.9
irisの1行目と3行目だけを足し合わせたい場合は、以下のように計算できます。
{r}
colSums(iris[c(1, 3), -5])
Sepal.Length Sepal.Width Petal.Length Petal.Width
10.0 7.0 3.2 0.6
3-6. 複数のデータフレームを統合する
データ分析を進めているうち、複数のデータフレームをひとつにまとめたい場合が多く出てきます。例えば頭痛持ちの患者のデータを分析しているうち、頭痛の有無と天候の関係について相関を調べたくなるかもしれません。そうすると天候データを別のところから引っ張ってきて、頭痛のデータにくっつける必要があります。このようにデータフレームの統合は分析を進めるうえで非常に頻度の高いオペレーションのひとつです。
ではデータフレームを統合する練習をしていきましょう。練習のためにまずダミーのデータフレームartistとmusicを2つ作ります。artistは歌手の名前と国が入ったデータフレーム、musicは歌手名と曲名が入ったデータフレームです。
まずartistデータフレームをつくります。大きさは5×2です。
{r}
artist <- data.frame(
surname = c("Beatles","Queen","Mariah Carey","Soda Stereo","Southern All Stars"),
nationality = c("UK","UK","US","Argentina","Japan"),
stringsAsFactors=FALSE)
print(dim(artist))
artist
[1] 5 2
次にmusicデータフレームをつくります。大きさは8×2です。
{r}
music <- data.frame(
surname = c("Beatles",
"Queen",
"Queen",
"Mariah Carey",
"Mariah Carey",
"Beatles",
"Soda Stereo",
"Stromae"),
title = c("Let It Be",
"Somebody to Love",
"Bohemian Rhapsody",
"All I Want for Christmas Is You",
"Fantasy",
"Yellow Submarine",
"Musica Ligera",
"Tous les memes"),
stringsAsFactors=FALSE)
print(dim(music))
music
[1] 8 2
この2つのデータフレームを統合して、surname, nationality, titleの3つのカラムを持つデータフレームをつくってみましょう。最も簡単なやり方は、merge()関数を使うことです。
merge()関数にわたす引数は:
- x: 元のデータフレーム
- y: くっつけたい情報が入っているデータフレーム
- by.x: 統合に使うxに含まれるカラム
- by.y: 統合に使うyに含まれるカラム
です。artistとmusicにはsurnameというカラム名が共通しているので、2つを横方向にくっつけることができます。
データフレームの統合には大きく分けてInner join、Left Join, Outer Joinの3種類があります。
3-6-1. Inner Join
Inner Joinとは、2つのデータフレームから共通した行だけを統合するオペレーションのことをいいます。
Inner Joinのやり方は次のとおりです。
{r}
m1 <- merge(artist, music, by.x = "surname")
m1
では出力されたm1の大きさを調べてみましょう。
{r}
dim(m1)
[1] 7 3
大きさは7×3となっています。ここで、artistの中に入っていたsurnameの種類と、m1の中のsurname
い含まれる値の種類の数を比べてみましょう。unique()関数を使うことで個別値を調べることができます。
{r}
unique(artist$surname)
unique(m1$surname)
[1] "Beatles" "Queen" "Mariah Carey" "Soda Stereo"
[5] "Southern All Stars"
[1] "Beatles" "Mariah Carey" "Queen" "Soda Stereo"
すると上のように、artistの中に入っていた"Southern All Stars"がm1では落とされていることがわかります。これはmusicのsurnameカラムの中に"Southern All Stars"がなかったために起こった結果です。
同じように、musicのsurnameに含まれていた"Stromae"が、統合後のデータフレームにはないこともわかります。これはartistのsurnameには一致する"Stromae"の値がなかったからです。
{r}
unique(music$surname)
unique(m1$surname)
[1] "Beatles" "Queen" "Mariah Carey" "Soda Stereo" "Stromae"
[1] "Beatles" "Mariah Carey" "Queen" "Soda Stereo"
このように、Inner Joinでは双方のデータフレームに共通する行だけが統合されます。
3-6-2. Outer Join
Inner Joinの反対がOuter Joinで、これは双方のデータフレームに含まれるすべての行を統合後もキープします。
Outer Joinをするには、merge()関数にall=TRUEという引数を渡します。
{r}
m2 <- merge(artist, music, by.x="surname", all=TRUE)
m2
統合結果を見るとわかるように、surnameが"Southern All Stars"の行はtitleが空欄になっています。これはmusicデータフレームの中に"Southern All Stars"に該当する曲がなかったためですが、all=TRUEとしてOuter Joinをしているのでこの行は落とされずにキープされていることがわかります。
同じく、surnameが"Stromae"の行はnationalityが空欄になっています。これはartistデータフレームの中に"Stromae"に該当するnationalityがなかったためです。
3-6-3. Left Join
最後にLeft Joinです。Left Joinは2つのデータフレームを自分から見て右と左に置いた時に、左側のデータフレームからはすべての行をキープし、右側のデータフレームからは左側にマッチするものだけを統合するオペレーションのことを指します。
Left Joinをするにはall.x=TRUEの引数を渡します。「x(左側)のデータセットからすべての行を持ってくる」という意味です。ここではx=artist, y=musicとしているので、artistが左側、musicが右側になります。
{r}
m3 <- merge(artist, music, by.x="surname", all.x=TRUE)
m3
ご覧の通り、surnameが"Southern All Stars"の行だけtitleが空欄になっている一方。Inner Joinではキープされていたsurname="Stromae"は落とされています。
3-6-4. カラム名が共通していない場合
さて、ここまでartistとmusicの両方に同じ名前のカラム(surname)がある場合を想定して統合の練習をしてきました。ただ実際には、別のところから引っ張ってきたデータにも同じ名前のカラムがあることはまれです。統合の前にカラム名を同じに揃える作業をやっても良いのですが、その手間を省いていきなりmerge()関数を使うこともできます。
練習のために、まずmusicのsurnameカラムをnameという名前に変更してしまいましょう。
{r}
colnames(music)[colnames(music) == 'surname'] <- 'name'
music
ここで先程と同じようなmerge()の使い方をしてもエラーが出てきてしまいます。なぜならsurnameカラムがmusicにないからです。
{r}
merge(artist, music, by.x="surname")
Error in merge.data.frame(artist, music, by.x = "surname") : 'by.x' and 'by.y' specify different numbers of columns
そんなときはby.yの引数を渡して、y側のカラム名も指定しましょう。
{r}
m4 <- merge(artist, music, by.x="surname", by.y="name")
m4
結果として、x側のカラム名surnameが残り、y側のnameカラムは落とされました。
今回はデータフレームの操作方法について少し詳しめに見てみました。これでもまだ基本の範囲内ですが、非常によく使う操作方法を説明したので、手元のデータセットでたくさん練習して使いこなせるようになってください。データフレームはdplyrなどのパッケージを使うことでより複雑なオペレーションができるようになります。それはまた別の回で詳しく解説します!