データフレームの作成と編集
例えば、Excelのワークシート上に図1のようなデータが保存されていたとします。このデータをRに取り込み、それからデータフレームを作成することを考えます。その場合、方法としては2つあります。1つはCSV(カンマで区切られたデータ)形式のファイルとして保存しなおし、それを取り込むという方法です。もう1つはRのコンソールウィンドウ上で直接データを打ち込んで作成するという方法です。
図1 Excelに保存されているデータ
扱うデータの量が多い場合はCSVファイルを作成し、それを読み込む方が良いでしょう。そうではなく、今回のように少量のデータであれば直接コマンドしてデータフレームを作成した方が速いかもしれません。
CSVファイルの読み込みによる方法 read.table()を使う
まずはCSVファイルの作り方から説明します。Excelのワークシート上に入力されているデータ範囲(A1:D8)をコピーして、それをメモ帳などのテキストエディタにペーストします。そうしたら、[名前をつけて保存]し、適当なファイル名(ここではsample_data.csvとします)をつけて保存するだけです。このとき、[ファイルの種類]を[全てのファイル]にしておいて、拡張子(.csv)を付け忘れないようにしましょう。
CSVファイルが用意できたならば、今度はRのread.table()という関数を用いてデータを読み込みます。ここではsample_data.csvがDドライブに保存されていたとします。
# データの読み込み > dat <- read.table("D://sample_data.csv", header=TRUE) > dat # datの中身を表示 気温 天候 曜日 売上 1 20 曇 日 86 2 21 曇 月 107 3 17 曇 火 111 4 16 晴 水 86 5 19 曇 木 109 6 14 晴 金 91 7 22 雨 土 99
read.table()の引数であるheaderをFALSEにすると、先頭にあるラベル(列名)が1行目のデータとして読み込まれてしまいます。なお、デフォルト(最初の状態)でFALSEになっていますので、何も指定しない場合は自動的にheader=FALSEになります。
# headerを省略した場合(FALSEとした場合) > dat2 <- read.table("D://sample_data.csv") > dat2 V1 V2 V3 V4 1 気温 天候 曜日 売上 2 20 曇 日 86 3 21 曇 月 107 4 17 曇 火 111 5 16 晴 水 86 6 19 曇 木 109 7 14 晴 金 91 8 22 雨 土 99
直接コマンドして作る方法 data.frame()を使う
いくつか方法はありますが、私がやりやすいと思う方法を1つ紹介します。
# それぞれのデータベクトルを作る > Temperature <- c(20, 21, 17, 16, 19, 14, 22) > Weather <- c("曇", "曇", "曇", "晴", "曇", "晴", "雨") > Week <- c("日", "月", "火", "水", "木", "金", "土") > Sales <- c(86, 107, 111, 86, 109, 91, 99) # データフレームの作成 > dat3 <- data.frame(TEMPERATURE = Temperature, WEATHER = Weather, + WEEK = Week, SALES = Sales) # dat3の中身を表示 > dat3 TEMPERATURE WEATHER WEEK SALES 1 20 曇 日 86 2 21 曇 月 107 3 17 曇 火 111 4 16 晴 水 86 5 19 曇 木 109 6 14 晴 金 91 7 22 雨 土 99
ところで、コンピュータ上で日本語を使うのはあまり好ましくありません。ファイル名も極力、半角英数で入力されたものにしましょう。R上であっても、なるべく日本語は使わないようにしましょう。
# names()で変数名を表示できます > names(dat) [1] "気温" "天候" "曜日" "売上" # 新たな名前をつけます > names(dat) <- c("TEMPERATURE", "WEATHER", "WEEK", "SALES") # 再びnames()を使用 > names(dat) [1] "TEMPERATURE" "WEATHER" "WEEK" "SALES" # 中身を表示して確認 > dat TEMPERATURE WEATHER WEEK SALES 1 20 曇 日 86 2 21 曇 月 107 3 17 曇 火 111 4 16 晴 水 86 5 19 曇 木 109 6 14 晴 金 91 7 22 雨 土 99
ちなみに、names(dat)とするとdatの列名が表示されますが、これはcolnames(dat)としても同じ結果が得られます。また、rownames(dat)とすると、行名が表示されることになります。つまり、
> rownames(dat) <- c("", "", ..., "")
とすることによって、列名の変更と同じように、行名の変更ができるわけです。
なお、今回用いたsample_data.csvは行名がなかったため、読み込んだ際には行名が1から7という数字が自動的に割り振られていました。もし行名が最初からついているデータであった場合は、read.table()の引数にrow.names=1と指定してあげれば良いのです。
> dat <- read.table(D://sample_data.csv, header=TRUE, row.names=1)
■ データフレームの中身の置換 *ややこしいのここは別に読まなくても良いです
ところで、WEATHERとWEEKは文字型です。データフレームを作成する際に、文字型のデータベクトルは因子として扱われます。因子(factor)には水準(level)があり、例えば、WEATHERの水準は
> dat$WEEK [1] 日 月 火 水 木 金 土 Levels: 火 金 月 水 土 日 木
とされています。水準1(Levels 1)は火、水準2は金のようになっているわけです。私たちの感覚だと、日、月、火、水、木、金、土の順番にそれぞれ水準1、水準2、、、のように割り当てられるのが普通ですが、R側では文字コードによって優先順位が変わるので、このようになっています。
もし漢字(全角)ではなくて英語(半角)にしたい(置き換えたい)場合は次のような関数を定義しておくと便利です。
myfunc <- function(a, myn){ mydata <- c() n <- length(a) n2 <- length(myn) for(i in 1:n){ for(j in 1:n2){ if(a[i] == levels(a)[j]) mydata[i] <- myn[j] } } mydata <- as.factor(mydata) }
使い方は次の通りです。
# データフレームdatにおけるWEEKの中身を確認 > dat$WEEK [1] 日 月 火 水 木 金 土 Levels: 火 金 月 水 土 日 木 # 英語に置き換える > chikan <- myfunc(dat$WEEK, c("Tue", "Fri", "Mon", "Wed", "Sat", "Sun", "Thu")) # データフレームdatのWEEKにchikanのデータを挿入 > dat$WEEK <- chikan > dat # 中身の確認 TEMPERATURE WEATHER WEEK SALES 1 20 曇 Sun 86 2 21 曇 Mon 107 3 17 曇 Tue 111 4 16 晴 Wed 86 5 19 曇 Thu 109 6 14 晴 Fri 91 7 22 雨 Sat 99
ここで改めてWEEKの水準がどうなっているかを見てみましょう。
> dat$WEEK [1] Sun Mon Tue Wed Thu Fri Sat Levels: Fri Mon Sat Sun Thu Tue Wed
すると、このようにSun, Mon, Tue, ...という順番ではなくて、やはりR側が勝手に水準の順番を解釈してしまいます。別に、水準の順番は解析においては関係ないので問題はありませんが、少ししっくり来ない感じがあります。
そういう場合は先ほど使ったmyfunc()を使って、次のようにします。
> chikan2 <- myfunc(dat$WEEK, c(6, 2, 7, 1, 5, 3, 4)) > dat$WEEK <- chikan2 > dat TEMPERATURE WEATHER WEEK SALES 1 20 曇 1 86 2 21 曇 2 107 3 17 曇 3 111 4 16 晴 4 86 5 19 曇 5 109 6 14 晴 6 91 7 22 雨 7 99 > dat$WEEK [1] 1 2 3 4 5 6 7 Levels: 1 2 3 4 5 6 7
実はデータフレームを作成する際に、因子の扱いにはいくつかややこしいことがあります。ですから、Excelなどに原データを入力する際に、最初から数値を割り当てて入力してしまい、それを(Rでデータフレームを作成するときに)as.factor()を用いて因子として指定してやった方がいいでしょう(→補足1)。
最後にWEATHERも数値を割り当てておきます。
> dat$WEATHER [1] 曇 曇 曇 晴 曇 晴 雨 Levels: 雨 晴 曇 > chikan3 <- myfunc(dat$WEATHER, c(3, 1, 2)) > dat$WEATHER <- chikan3 > dat$WEATHER [1] 2 2 2 1 2 1 3 Levels: 1 2 3
すると、結局はこうなりました。
> dat TEMPERATURE WEATHER WEEK SALES 1 20 2 1 86 2 21 2 2 107 3 17 2 3 111 4 16 1 4 86 5 19 2 5 109 6 14 1 6 91 7 22 3 7 99
補足1
どこかのデータベースから引っ張ってきたデータとかだと、今回のような曜日のデータ(月火水木金土日)を自分で数値に置換しなければなりません(もちろん、そのまま使っても良いですが)。その場合は置換機能をもったテキストエディタを使えばラクに作業を行うことができます。
例えば、TeraPadが便利です。まずExcelなどからコピペした1次データがあります(図2)。
図2 1次データ
これが用意できたら、[Ctrl] + [R]をタイプするか、[検索] -> [置換]の順にクリックしてください。すると、図3のようなウィンドウが開かれます。[検索する文字列]に「曇」を入力し、[置換後の文字列]に「2」を入力して、[すべて置換]をクリックします。
図3 TeraPadの置換機能
すると、「曇」が「2」に置換されます(図4)。
図4 置換後のデータ
しかし、実はこのようなことをしなくてもfactor()という関数を上手く使うことによって、上手にカテゴリカル型のデータを扱うことができます。
# 男性と女性を表すデータベクトル > x <- c("男性", "男性", "女性", "男性", "女性") # この時点では文字型のベクトル > x [1] "男性" "男性" "女性" "男性" "女性" # カテゴリカル型のデータにする > y <- factor(x, level=c("男性", "女性")) > y [1] 男性 男性 女性 男性 女性 Levels: 男性 女性 # 男性はMに、女性はFに置換 > z <- factor(y, level=c("男性", "女性"), label=c("M", "F")) > z [1] M M F M F Levels: M F # 文字を数値に置換することもできる。逆ももちろんできる。 > xx <- factor(z, level=c("F", "M"), label=c(1, 2)) > xx [1] 2 2 1 2 1 Levels: 1 2 # ちなみにas.factor()には引数にlevelsやlabelsはない。 > yy <- as.factor(x) # 中身を見るとやはり文字コード順になっている > yy [1] 男性 男性 女性 男性 女性 Levels: 女性 男性
データフレームを作成して、それからある行のみのデータ(あるいは列のデータ)、特定の条件を満たす行(あるいは列)データを抽出したいこともあるでしょう。例えば、天候が晴れで、かつ売上が80万円以上のデータとかですね。
データフレームを扱う場合、attach()を使ってパスを通しておくと便利です。
# パスを通す > attach(dat3)
データフレームにパスを通すことによって、そのデータフレームに含まれる変数へ直接アクセスすることができます。
> TEMPERATURE [1] 20 21 17 16 19 14 22 > WEEK [1] 日 月 火 水 木 金 土 Levels: 火 金 月 水 土 日 木
パスを解除するためにはdetach()を使います。
# パスを解除 > detach(dat3) # パスが通っていないので、変数TEMPERATUREは存在しないことになっている > TEMPERATURE エラー: オブジェクト "TEMPERATURE" は存在しません # WEEKも同様。 > WEEK エラー: オブジェクト "WEEK" は存在しません
いくつかの例を紹介しましょう。
# 例1 > dat3[dat3$WEATHER == "晴", ] TEMPERATURE WEATHER WEEK SALES 4 16 晴 水 86 6 14 晴 金 91 # 例2 > dat3[dat3$SALES >= 100, ] TEMPERATURE WEATHER WEEK SALES 2 21 曇 月 107 3 17 曇 火 111 5 19 曇 木 109 # 例3 > dat3[dat3$WEATHER == "曇" & dat3$TEMPERATURE >= 20, ] TEMPERATURE WEATHER WEEK SALES 1 20 曇 日 86 2 21 曇 月 107 # 例4 > dat3$SALES[dat3$SALES >= 90] [1] 107 111 109 91 99 # 例5 > dat3$SALES[dat3$SALES >= 90 & dat3$WEATHER == "雨"] [1] 99
もしパスを通した状態であれば、[ ]のなかで
> dat3[WEATHER=="晴", ] TEMPERATURE WEATHER WEEK SALES 4 16 晴 水 86 6 14 晴 金 91
とするだけでよいのです。わざわざdat3$WEATHERとする必要はないわけです。
ちなみに、&は「かつ」を意味しており、|は「または」という意味です。A & Bならば「AかつB」でAとBの両方ということになります。A | Bならば「AまたはB」で、AとBのどちらか一方ということになります。
行、列の削除
ある特定の列(変数)を削除するのは簡単です。
> my.data TEMPERATURE WEATHER WEEK SALES 1 20 曇 日 86 2 21 曇 月 107 3 17 曇 火 111 4 16 晴 水 86 5 19 曇 木 109 6 14 晴 金 91 7 22 雨 土 99 # 特定の列にNULLを代入する > my.data[, 2] <- NULL # NULLが代入された変数が削除されている > my.data TEMPERATURE WEEK SALES 1 20 日 86 2 21 月 107 3 17 火 111 4 16 水 86 5 19 木 109 6 14 金 91 7 22 土 99
行を削除するためには、たぶん、ちょっとの工夫が必要です。
> my.data TEMPERATURE WEATHER WEEK SALES 1 20 曇 日 86 2 21 曇 月 107 3 17 曇 火 111 4 16 晴 水 86 5 19 曇 木 109 6 14 晴 金 91 7 22 雨 土 99 # 削除したい行の任意の場所にNAを代入 > my.data[3, 1] <- NA # NAを含む行を削除する関数na.omit()を使う # 3行目"とされていた"データが削除されている > (my.data2 <- na.omit(my.data)) TEMPERATURE WEATHER WEEK SALES 1 20 曇 日 86 2 21 曇 月 107 4 16 晴 水 86 5 19 曇 木 109 6 14 晴 金 91 7 22 雨 土 99 # ラベルと行数が異なっていることに注意 > my.data2[3, ] TEMPERATURE WEATHER WEEK SALES 4 16 晴 水 86
行、列の追加
これも列は簡単にできるのですが、新たな行を追加する場合はけっこうな手間がかかります。
列は新たな変数をベクトルとして作り出し、それを既に作成されているデータフレームとcbind()を使って結合すればよいのです。
> new <- 1:7 > new [1] 1 2 3 4 5 6 7 > cbind(my.data2, new) TEMPERATURE WEATHER WEEK SALES new 1 20 曇 日 86 1 2 21 曇 月 107 2 3 NA 曇 火 111 3 4 16 晴 水 86 4 5 19 曇 木 109 5 6 14 晴 金 91 6 7 22 雨 土 99 7
行の場合も1行分のデータベクトルを作成して、それをrbind()で結合させればよいのですが、、、
> new.col <- c(10, "雨", "日", 80) > (a <- rbind(my.data2, new.col)) TEMPERATURE WEATHER WEEK SALES 1 20 曇 日 86 2 21 曇 月 107 3 <NA> 曇 火 111 4 16 晴 水 86 5 19 曇 木 109 6 14 晴 金 91 7 22 雨 土 99 8 10 雨 日 80 # 変数の中身を確認してみると # 文字列になってしまっている > a$TEMPERATURE [1] "20" "21" NA "16" "19" "14" "22" "10"
基本的にベクトル内のデータ型は文字が含まれていれば、すべて文字列としてデータベクトルが作成されます。それなので、データフレームと結合した際に、全ての変数が文字型のデータになってしまったというわけです。
この場合は、as.numeric()を使って数値データに変換してあげる必要があります。
> a$TEMPERATURE [1] 20 21 NA 16 19 14 22 10 # 型の確認 > is.numeric(a$TEMPERATURE) [1] TRUE
ちなみに、型を検査するための関数、型を変換するための関数があります。numeric型であるかを判定するas.numeric()と変換するためのis.numeric()、文字型であるかを判定するas.character()とそれに変換するためのis.character()、そして因子型に対応するis.factorとas.factor()があります。通常はこの3つを覚えておけば、とくに問題はありません。