Exploring Data Frames
Overview
Teaching: 20 min
Exercises: 10 minQuestions
How can I manipulate a data frame?
Objectives
Add and remove rows or columns.
Remove rows with
NA
values.Append two data frames.
Understand what a
factor
is.Convert a
factor
to acharacter
vector and vice versa.Display basic properties of data frames including size and class of the columns, names, and first few rows.
これで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(数値型)については、 順序なし因子型ではないため、weight、likes_string、ageにそれぞれ正しく加えられています。 「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年と同じだと想像してみましょう。
cats$age
を7倍にした、human_age(人の年齢)
というベクトルを作りましょうhuman_age
を順序なし因子型に変換しましょうhuman_age
を、as.numeric()
関数を用いて、数値型に戻しましょう。そして、もともとの年に戻すために7で割りましょう。何が起こったか説明して下さい。チャレンジ1の解答
human_age <- cats$age * 7
human_age <- factor(human_age)
.as.factor(human_age)
でもうまくいきます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
が表示する全てのことを説明するために、colnames
やdim
などの出力関数を使いましょう。 理解できないところがあれば、近くの人と話し合ってみましょう。チャレンジ5の解答
gapminder
というオブジェクトは、データフレームで、
country
とcontinent
という順序なし因子型、year
という整数型のベクトル、pop
、lifeExp
、gdpPercap
という数値型のベクトルの行を持っています。
Key Points
Use
cbind()
to add a new column to a data frame.Use
rbind()
to add a new row to a data frame.Remove rows from a data frame.
Use
na.omit()
to remove rows from a data frame withNA
values.Use
levels()
andas.character()
to explore and manipulate factors.Use
str()
,nrow()
,ncol()
,dim()
,colnames()
,rownames()
,head()
, andtypeof()
to understand the structure of a data frame.Read in a csv file using
read.csv()
.Understand what
length()
of a data frame represents.