データフレームの内容を確認

概要

講義: 20 分
演習: 10 分
質問
  • データフレームはどのように操作すればよいですか?

目標
  • 行や列を追加したり削除出来るようになりましょう。

  • NAが入っている行の削除が出来るようになりましょう。

  • 2つのデータフレームをマージできるようになりましょう。

  • factor(ファクター)について理解しましょう。

  • factorcharacter(文字型)に変換、またはその逆の変換が出来るようになりましょう。

  • データフレームの列のサイズやクラスなどの基本的なプロパティ、および名前や最初の数行の表示が出来るようになりましょう。

これでRの基礎を全て見たことになります。このレッスンで、Rの基本的なデータ型、データ構造を習いました。 これからすることの全ては、これらの道具を用いた操作となります。 しかし大抵の場合、主役はデータフレーム(CSVファイルから情報を読み込み作成した表)です。 このレッスンでは、データフレームを使ってどう作業していくかについて更に学んでいきましょう。

データフレームに行と列を追加する

既に学んだとおり、データフレームの列はベクトルですから、列にあるデータには一貫性があります。 ですので、新しい列を加えたい場合は、新しいベクトルを作ることから始めることになります:

age <- c(2, 3, 5)
cats
    coat weight likes_string
1 calico    2.1            1
2  black    5.0            0
3  tabby    3.2            1

そして、これを以下を使って列に加えます:

cbind(cats, age)
    coat weight likes_string age
1 calico    2.1            1   2
2  black    5.0            0   3
3  tabby    3.2            1   5

もし、データフレームの行の数と一致しない年齢のベクトルを追加しようとすると、失敗します:

age <- c(2, 3, 5, 12)
cbind(cats, age)
Error in data.frame(..., check.names = FALSE): arguments imply differing number of rows: 3, 4
age <- c(2, 3)
cbind(cats, age)
Error in data.frame(..., check.names = FALSE): arguments imply differing number of rows: 3, 2

なぜダメだったのでしょうか?もちろん、Rは新しい列のひとつの要素を、表の中にある全ての行について参照したがるものです。

nrow(cats)
[1] 3
length(age)
[1] 2

ですから、そうするために、 nrow(cats) = length(age) である必要があるのです。猫の内容を新しいデータフレームで上書きしましょう。

age <- c(2, 3, 5)
cats <- cbind(cats, age)

新しい行を加えてみてはどうでしょうか?既に知っているとおり、データフレームの行はリストです:

newRow <- list("tortoiseshell", 3.3, TRUE, 9)
cats <- rbind(cats, newRow)
Warning in `[<-.factor`(`*tmp*`, ri, value = "tortoiseshell"): invalid
factor level, NA generated

順序なし因数

ここで、もうひとつ気を付けなければいけないことは、順序なし因子型(factor)の中で、それぞれの値に水準(level) と呼ばれるものが付されているということです。つまり、この例では、順序なし因子型である「coat(毛皮)」には、「black(黒)」、「calico(三毛)」、「tabby(ぶち)」の3つの水準(level) があり、Rは、この水準のいずれかに一致する値でなければ、受け付けないというわけです。新しい値を加えようとすると、それはNAになります。

この注意は、順序なし因子型である「coat(毛皮)」に、「tortoiseshell(錆)」をちゃんと加えられなかった ことを伝えているのです。しかし、3.3 (数値型)、TRUE(論理型)、9(数値型)については、 順序なし因子型ではないため、weightlikes_stringageにそれぞれ正しく加えられています。 「tortoiseshell」の猫について、coatをちゃんと加えるために、「tortoiseshell」をこの順序なし因子の水準(level)に加えましょう:

levels(cats$coat)
[1] "black"  "calico" "tabby" 
levels(cats$coat) <- c(levels(cats$coat), "tortoiseshell")
cats <- rbind(cats, list("tortoiseshell", 3.3, TRUE, 9))

あるいは、順序なし因子型を文字型のベクトルに変えることもできます。 すると、順序なし因子型の便利な分類を失いますが、順序なし因子の水準を 管理する必要なく、列に好きな語句を追加できます:

str(cats)
'data.frame':\t5 obs. of  4 variables:
 $ coat        : Factor w/ 4 levels "black","calico",..: 2 1 3 NA 4
 $ weight      : num  2.1 5 3.2 3.3 3.3
 $ likes_string: int  1 0 1 1 1
 $ age         : num  2 3 5 9 9
cats$coat <- as.character(cats$coat)
str(cats)
'data.frame':\t5 obs. of  4 variables:
 $ coat        : chr  "calico" "black" "tabby" NA ...
 $ weight      : num  2.1 5 3.2 3.3 3.3
 $ likes_string: int  1 0 1 1 1
 $ age         : num  2 3 5 9 9

チャレンジ1

人の1年は、猫の7年と同じだと想像してみましょう。

  1. cats$ageを7倍にした、human_age(人の年齢)というベクトルを作りましょう
  2. human_ageを順序なし因子型に変換しましょう
  3. human_age を、as.numeric() 関数を用いて、数値型に戻しましょう。そして、もともとの年に戻すために7で割りましょう。何が起こったか説明して下さい。

チャレンジ1の解答

  1. human_age <- cats$age * 7
  2. human_age <- factor(human_age). as.factor(human_age) でもうまくいきます
  3. as.numeric(human_age) からは、1 2 3 4 4 が返ってきます。なぜなら、順序なし文字型は、それぞれが、ラベル(ここでは、28、35、56、63)に紐づけられた整数型(ここでは、1:4)として蓄積されているからです。順序なし文字型を数値型に変換する際は、ラベルの値ではなく、裏にある整数に変換されるのです。もとの数が欲しい場合は、human_ageを文字ベクトルにしてから、数値ベクトルにする必要があります(なぜこれでうまくいくのでしょう?)。この問題は、csvファイルの中で数だけを持つはずの列のどこかにうっかり文字を入れてしまい、データを読み込むときstringsAsFactors=FALSEを設定し忘れてしまったときなどに起こります。

行の削除

Rのデータフレームに行列を加える方法は分かりましたが、猫データフレームに”tortoiseshell”を加える最初の試みで、 うっかり不要な行を加えてしまいました。

cats
           coat weight likes_string age
1        calico    2.1            1   2
2         black    5.0            0   3
3         tabby    3.2            1   5
4          <NA>    3.3            1   9
5 tortoiseshell    3.3            1   9

この問題の行を除くようにデータフレームに頼みましょう:

cats[-4, ]
           coat weight likes_string age
1        calico    2.1            1   2
2         black    5.0            0   3
3         tabby    3.2            1   5
5 tortoiseshell    3.3            1   9

コンマの後に何もないのは、4番目の行の全部を削除して欲しいということを示している点に留意しましょう。

注意:ベクトルの中に行番号を入れれば、新しく追加した行を両方削除することもできす:cats[c(-4,-5), ]

または、NAの値がある全ての行を除くこともできます:

na.omit(cats)
           coat weight likes_string age
1        calico    2.1            1   2
2         black    5.0            0   3
3         tabby    3.2            1   5
5 tortoiseshell    3.3            1   9

変更した内容を今後も使えるように、この結果を再度cats に入れましょう:

cats <- na.omit(cats)

列の削除

データフレームから列を削除することもできます。列「age」を削除したい場合、どうすれば削除できるのでしょう。この場合、要素番号か見出しを使う2つの方法があります。

cats[,-4]
           coat weight likes_string
1        calico    2.1            1
2         black    5.0            0
3         tabby    3.2            1
5 tortoiseshell    3.3            1

全ての行を持っていたいということを示すため、コンマの前に何も入っていない点に留意しましょう。

または、要素番号の名前を使って列を削除することもできます:

drop <- names(cats) %in% c("age")
cats[,!drop]
           coat weight likes_string
1        calico    2.1            1
2         black    5.0            0
3         tabby    3.2            1
5 tortoiseshell    3.3            1

データフレームへの追加

データフレームにデータを加えるときに覚えておくべき重要なことは、列はベクトルで、行はリスト であることです。 2つのデータフレームを rbind を使ってくっつけることもできます:

cats <- rbind(cats, cats)
cats
            coat weight likes_string age
1         calico    2.1            1   2
2          black    5.0            0   3
3          tabby    3.2            1   5
5  tortoiseshell    3.3            1   9
11        calico    2.1            1   2
21         black    5.0            0   3
31         tabby    3.2            1   5
51 tortoiseshell    3.3            1   9

しかし、いまや行の名前は必要以上に複雑です。この列名は削除することができます。削除すると、Rは自動的に連続する名称をつけます。

rownames(cats) <- NULL
cats
           coat weight likes_string age
1        calico    2.1            1   2
2         black    5.0            0   3
3         tabby    3.2            1   5
4 tortoiseshell    3.3            1   9
5        calico    2.1            1   2
6         black    5.0            0   3
7         tabby    3.2            1   5
8 tortoiseshell    3.3            1   9

チャレンジ2

新しいデータフレームを、以下の構文で、Rで正しく作成できます:

df <- data.frame(id = c("a", "b", "c"),
                 x = 1:3,
                 y = c(TRUE, TRUE, FALSE),
                 stringsAsFactors = FALSE)

自分で、以下の情報を持つデータフレームを作ってみましょう:

  • first name
  • last name
  • lucky number

そして rbind を使って、隣に座っている人の入力したものを加えましょう 最後に cbind を使って、「コーヒーブレイクの時間ですか?」という質問への各自の回答を加えましょう

チャレンジ2の解答

df <- data.frame(first = c("Grace"),
                 last = c("Hopper"),
                 lucky_number = c(0),
                 stringsAsFactors = FALSE)
df <- rbind(df, list("Marie", "Curie", 238) )
df <- cbind(df, coffeetime = c(TRUE,TRUE))

現実的な例

これまで、猫のデータフレームで基本的なデータ操作を見てきました。 ここからは、もっと現実的なデータセットで学んだスキルを試してみましょう。 前にダウンロードした gapminder のデータセットを読み込みましょう:

gapminder <- read.csv("data/gapminder_data.csv")

いろいろなヒント

  • もう一つの遭遇するかもしれないファイルの種類は、タブで分けられた値のファイル(.tsv)です。タブを値を分けるものとして指定するためには、"\\t" またはread.delim()を使いましょう。

  • ファイルは、download.file 関数を使って、インターネットから直接、自分のコンピューターの お好みのローカルフォルダへダウンロードすることもできます。 read.csv 関数は、ダウンロードされたファイルをダウンロードされた場所から読み込む形で実行されます。例えば、

download.file("https://raw.githubusercontent.com/swcarpentry/r-novice-gapminder/gh-pages/_episodes_rmd/data/gapminder_data.csv", destfile = "data/gapminder_data.csv")
gapminder <- read.csv("data/gapminder_data.csv")
  • または、read.csv のファイルパスをウェブアドレスに置き換えることで、インターネットからRへ直接読み込むこともできます。こうすると、csvファイルのローカルコピーが、最初に自分のコンピューターに保存されないことに留意する必要があります。例えば、
gapminder <- read.csv("https://raw.githubusercontent.com/swcarpentry/r-novice-gapminder/gh-pages/_episodes_rmd/data/gapminder_data.csv")
  • エクセルのシートからテキスト形式に変換することなく、直接読み込むことも、 readxl パッケージで可能です。

gapminderを少し見てみましょう。いつも、まずしなければいけないことは、 データがどうなっているかをstrで見てみることです:

str(gapminder)
'data.frame':\t1704 obs. of  6 variables:
 $ country  : Factor w/ 142 levels "Afghanistan",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ year     : int  1952 1957 1962 1967 1972 1977 1982 1987 1992 1997 ...
 $ pop      : num  8425333 9240934 10267083 11537966 13079460 ...
 $ continent: Factor w/ 5 levels "Africa","Americas",..: 3 3 3 3 3 3 3 3 3 3 ...
 $ lifeExp  : num  28.8 30.3 32 34 36.1 ...
 $ gdpPercap: num  779 821 853 836 740 ...

データフレームのそれぞれの列を typeof 関数で調べることもできます:

typeof(gapminder$year)
[1] "integer"
typeof(gapminder$country)
[1] "integer"
str(gapminder$country)
 Factor w/ 142 levels "Afghanistan",..: 1 1 1 1 1 1 1 1 1 1 ...

データフレームの次元について見てみることもできます。 str(gapminder) が、gapminderには、6変数について1704の観測値があると言っていたことを念頭に置き、 以下から何が出てくると思いますか?それはなぜですか?

length(gapminder)
[1] 6

予想としては、データフレームの長さは行数(1704)だと思うものですが、実はそうではありません。 データフレームは、ベクトルと順序なし因子型のリストであるということを思い出しましょう:

typeof(gapminder)
[1] "list"

lengthが、6と返ってくるのは、gapminderは、6つの列のリストから成っているからです。 データセットで、行と列の数を知るためには、こうしてみましょう:

nrow(gapminder)
[1] 1704
ncol(gapminder)
[1] 6

または、両方を同時に:

dim(gapminder)
[1] 1704    6

全ての列のタイトルを知りたいと思うことも多いと思うので、後で聞いてみましょう:

colnames(gapminder)
[1] "country"   "year"      "pop"       "continent" "lifeExp"   "gdpPercap"

この段階で、Rが伝える構造が自分の直感や予想と合っているかを自問することが大切です。 それぞれの列の基本的なデータ型は、思った通りのデータ型になってますか?もしなっていないのなら、今後、予想外の事態を引き起こさないように、 今の時点で、問題を解決しておく必要があります。そのためには、これまでに学んだ、Rがどのようにデータを解釈するか、 そしてデータを記録する際の厳格な整合性の重要性といった知識を活かしましょう。

データ型と構造に満足することができたら、データを詳しく見始めることができます。 最初のいくつかの行を見てみましょう:

head(gapminder)
      country year      pop continent lifeExp gdpPercap
1 Afghanistan 1952  8425333      Asia  28.801  779.4453
2 Afghanistan 1957  9240934      Asia  30.332  820.8530
3 Afghanistan 1962 10267083      Asia  31.997  853.1007
4 Afghanistan 1967 11537966      Asia  34.020  836.1971
5 Afghanistan 1972 13079460      Asia  36.088  739.9811
6 Afghanistan 1977 14880372      Asia  38.438  786.1134

チャレンジ3

最後と中間のいくつかの行を確認するのは良い習慣です。どうしたらこれらの行を見ることができるのでしょう?

ちょうど真ん中にあるものを探すことは、難しくはありません。しかし、いくつかの行を無作為に尋ねることもできます。これはどうコードにすればよいでしょうか。

チャレンジ3の解答

最後のいくつかの行を調べるのは、比較的簡単です。Rには既に関数があるからです:

tail(gapminder)
tail(gapminder, n = 15)

すっきりするために(人によっては、もやっとするかもしれませんが)、いくつかの任意の行については、どうでしょうか。

ヒント:これには、いくつか方法があります。

ここでの解答は、関数を入れ子にしたもの(つまり、他の関数に、因数として渡される関数)です。新しい概念のように聞こえるかもしれませんが、実はもう使っています。 my_dataframe[rows, cols] は、(範囲または名前が付いた列を例えば聞いていたかもしれないとしても)質問した行と列の数と共にデータフレームを画面に表示させます。もし、データフレームに、いくつ行があるか知らない場合、どう最後の行を得るのでしょうか。Rには、そのための関数があります。では(疑似ランダム)サンプルを得るにはどうすればよいでしょうか。Rには、そのための関数もあります。

gapminder[sample(nrow(gapminder), 5), ]

分析を再現可能にするためには、後で使えるようにコードをスクリプトファイルに置く必要があります。

チャレンジ4

まず、file -> new file -> R scriptへ行き、gapminder データセットを 読み込むR scriptを書きましょう。それを、 scripts/ ディレクトリに置き、 バージョン管理に追加しましょう。

そのスクリプトを source 関数を使って走らせましょう。ファイルパスを引数にしましょう (または、RStudioで「source」ボタンを押しましょう).

チャレンジ4の解答

scripts/load-gapminder.Rの内容:

download.file("https://raw.githubusercontent.com/swcarpentry/r-novice-gapminder/gh-pages/_episodes_rmd/data/gapminder_data.csv", destfile = "data/gapminder_data.csv")
gapminder <- read.csv(file = "data/gapminder_data.csv")

gapminder 変数にデータを読み込みスクリプトを走らせるためには:

source(file = "scripts/load-gapminder.R")

チャレンジ5

str(gapminder) の結果を再び読みましょう。 今度は、順序なし因数、リスト、ベクトルについて学んだことを使いましょう。 gapminder が何を意味するか str が表示する全てのことを説明するために、 colnamesdim などの出力関数を使いましょう。 理解できないところがあれば、近くの人と話し合ってみましょう。

チャレンジ5の解答

gapminder というオブジェクトは、データフレームで、

  • countrycontinent という順序なし因子型、
  • year という整数型のベクトル、
  • poplifeExpgdpPercap という数値型のベクトルの行を持っています。

まとめ

  • 新しい列をデータフレームに追加するときは cbind() を使用する。

  • 新しい行をデータフレームに追加するときは rbind() を使用する。

  • データフレームから行を削除するとき

  • 値が NA の行をデータフレームから削除するときは na.omit() を使用する。

  • ファクターの内容を確認、および操作するときは levels() および as.character() を使用する。

  • データフレームの構造を理解したいときは str(), nrow(), ncol(), dim(), colnames(), rownames(), head(), および typeof() を使用する。

  • csv ファイルを読み込むときは read.csv() を使用する。

  • データフレームの length() の出力内容を理解する。