R データフレーム 使い方 考え方

R

Rによるデータフレームの計算チュートリアル

こんにちは。産婦人科医のtommyです。(Twitter:@obgyntommy

 

本記事ではRを用いたデータフレームの計算チュートリアルを行います。

 

対象としては、Rに全く触れたことのない方やRの学習初心者の方向けになります。

 

非常に簡単な四則演算の内容になりますが、RStudioを用意して実際に手を動かしてみてください。

 

RStudioを手軽にセットアップするにはRStudio Cloudが便利です。詳しい内容は以下を参照してください。

 

R Studio Cloud 使い方
R Studio Cloudの使い方

続きを見る

 

R Studio Cloud で学習される方はこちらを参照してください。

 

また、RStudio Cloudではなくても、Rのインストール方法やRStudioの使い方については以下の記事を参照してください。

 

R インストール方法 R studio 使い方
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などのパッケージを使うことでより複雑なオペレーションができるようになります。それはまた別の回で詳しく解説します!


-R

Copyright© Tommy blog  , 2024 All Rights Reserved.