Rの基本的なデータ構造であるデータフレームと、基礎的な入出力とグラフィックスについて学びます
学習項目です
列1のラベル | 列2のラベル | … | 列mのラベル | |
行1のラベル | 要素1-1 | 要素1-2 | … | 要素1-m |
行2のラベル | 要素2-1 | 要素2-2 | … | 要素2-m |
… | … | … | … | … |
行nのラベル | 要素n-1 | 要素n-2 | … | 要素n-m |
Rに組み込みの変数iris(統計学者のFisherによるアヤメのデータ)の値は、調査した3種類のアヤメの萼片(Sepal)の長さと幅、花弁(Petal)の長さと幅、それにアヤメの種類(setosa, versicolor, virginicaの3種)についての150個のデータで、個々のアヤメが行、それぞれの特徴量が列で表されたデータフレームである。
ここで、関数
> head(iris) Sepal.Length Sepal.Width Petal.Length Petal.Width Species 1 5.1 3.5 1.4 0.2 setosa 2 4.9 3.0 1.4 0.2 setosa 3 4.7 3.2 1.3 0.2 setosa 4 4.6 3.1 1.5 0.2 setosa 5 5.0 3.6 1.4 0.2 setosa 6 5.4 3.9 1.7 0.4 setosa注意すべきは、データフレームは2次元構造なので、各列の要素数は同じでな ければならないことである。また数値型データ以外は「因子型(factor)」に強制的に変換されることにも注意しよう。
> Namae <- c("太郎", "花子", "次郎", "直美") > Math <- c(80, 90, 50, 60) > English <- c(60, 80, 70, 90) > Seiseki <- data.frame(namae=Namae, math=Math, eng=English) > Seiseki namae math eng 1 太郎 80 60 2 花子 90 80 3 次郎 50 70 4 直美 60 90 > class(Seiseki) [1] "data.frame"上記では関数
> Seiseki$math [1] 80 90 50 60 > class(Seiseki$math) [1] "numeric" > Seiseki$namae [1] 太郎 花子 次郎 直美 Levels: 花子 次郎 太郎 直美 > class(Seiseki$namae) [1] "factor" > class(Namae) [1] "character"この例でわかるように、文字列型であったnamae列のデータはデータフレームでは自動的に「因子(factor)型」に変換されている。因子型のデータを表示するときには、
列の名前は次のように colnames関数で取り出すことができる。
> colnames(Seiseki) [1] "namae" "math" "eng"
このデータフレームに新たに行のラベルをつけることができる。
行にはデフォルトとして数の名前が付けられており、
それを rownames関数で取り出すことができるが、
以下に示すように、行のラベル付けにも
> rownames(Seiseki) [1] "1" "2" "3" "4" # デフォルトでは行の番号が行ラベル(文字型) > rownames(Seiseki)<-c("Taro","Hanako","Ziro","Naomi") # 行にラベルを付ける > Seiseki namae math eng Taro 太郎 80 60 Hanako 花子 90 80 Ziro 次郎 50 70 Naomi 直美 60 90データフレームの要素は、従来の「n行目のデータ」「m列目のデータ」「n行m列のデータ」という、行と列の番号による指定によってアクセスできる(もっともあまりお薦めしない)。 それに加えて、次のようにラベルを用いたアクセスもできる:
> Seiseki[,2] # 2列目、つまり数学データ [1] 80 90 50 60 > Seiseki$math # ラベルmathによるアクセス [1] 80 90 50 60 > Seiseki[3,2] # 3行2列目のデータ, つまりZiroのmath [1] 50 > Seiseki$math[3] # math列の3行目 [1] 50 # 3行2列目のデータ, つまりZiroのmathに等しい上で見たように、列ラベルによっていろいろなアクセスができるが、実は行ラベルはほとんど使い道がない。実際、特定の行を取り出すには、行番号による方法に加えて、次のように列ラベルを用いてアクセスするしかない:
> Seiseki[1,] # 1行目、つまりTaroデータ namae math eng Taro 太郎 80 60 > Seiseki[Seiseki$namae=="太郎",] # namaeが"太郎"の行データを取り出す namae math eng Taro 太郎 80 60 > subset(Seiseki,namae=="太郎") # 関数subsetを用いて上記と同じことを行う namae math eng Taro 太郎 80 60 > Seiseki[Seiseki$math<=60,] namae math eng Ziro 次郎 50 70 Naomi 直美 60 90 > subset(Seiseki,math<=60) # 関数subsetを用いて上記と同じことを行う namae math eng Ziro 次郎 50 70 Naomi 直美 60 90行列がある場合、それを用いてデータフレームを作ることもできる(Namae, Math, EnglishはデータフレームSeisekiを作るときに定義した変数)。
> Seiseki.matrix <- matrix(c(c(1,2,3,4), Math, English), 4, 3) # 4行3列の行列 > ( Seiseki2 <- data.frame(Seiseki.matrix) ) # 行列からデータフレームを作る X1 X2 X3 # X1, X2, X2はデフォルトの列ラベル 1 1 80 60 2 2 90 80 3 3 50 70 4 4 60 90 > Seiseki2$X2 # X2がラベルとして働くことの確認 [1] 80 90 50 60 > names(Seiseki2)<-c("namae","math","eng") # 関数namesで列ラベルを与える > Seiseki2 namae math eng 1 1 80 60 2 2 90 80 3 3 50 70 4 4 60 90 > Seiseki2$namae <- c("太郎","花子","次郎", "直美") # namae列にSeisekiと同様の値をセット [1] "太郎" "花子" "次郎" "直美" > Seiseki2 # 確認 namae math eng 1 太郎 80 60 2 花子 90 80 3 次郎 50 70 4 直美 60 90 # 最終的にSeisekiデータと同じになった[なぜnamae列だけ別に値を与えたのか?]
> Seiseki.matrix.try <- matrix(c(Namae,Math,English),4)しかし、その値をみてみると、すべて文字型に変換されている。これは、行列がの要素がすべて「同じ型」でなければならないという制約から、自動的に変換されたからである。
> Seiseki.matrix.try [,1] [,2] [,3] [1,] "太郎" "80" "60" [2,] "花子" "90" "80" [3,] "次郎" "50" "70" [4,] "直美" "60" "90" > ( Seiseki.try <- data.frame(Seiseki.matrix.try) ) X1 X2 X3 1 太郎 80 60 2 花子 90 80 3 次郎 50 70 4 直美 60 90 # 一見良さそうに思える > class(Seiseki.try$X3) # 確認 [1] "factor" > class(Seiseki$eng) # 比較 [1] "numeric" # 型が違う! > Seiseki.try$X2 <- as.numeric(Seiseki.try$X2) # as.numericで数値型に変換してみる > Seiseki.try # 確認 X1 X2 X3 1 太郎 3 60 2 花子 4 80 3 次郎 1 70 4 直美 2 90 # X2の値が期待したものと違う上記のように、一つでも文字型の値があると全体が因子型に変換されるため、一見同じものに見えても、実際は異なるデータとなってしまう。これがnames列に最初、数値型の値を与えておいて、後で文字型の値を代入した理由である。
A組: 52 86 41 64 33 19 65 35 48 15 B組: 42 59 32 54 35 45 35 55 49 41これからデータフレームを作り、変数Buturiに代入せよ。ただし、列の名前をそれぞれAとBとする。そして、A組の平均値と不偏分散、B組の平均値と不偏分散を求めるコード(プログラム)を書き、値を求めよ。
[課題1-1の答え]
> ( Buturi <- data.frame(A=c(52, 86, 41, 64, 33, 19, 65, 35, 48, 15), B=c(42, 59, 32, 54, 35, 45, 35, 55, 49, 41)) ) A B 1 52 42 2 86 59 3 41 32 4 64 54 5 33 35 6 19 45 7 65 35 8 35 55 9 48 49 10 15 41 > paste("A組の平均=",mean(Buturi$A),"A組の不偏分散=", var(Buturi$A)) [1] "A組の平均= 45.8 A組の不偏分散= 481.066666666667" > paste("B組の平均=",mean(Buturi$B),"B組の不偏分散=", var(Buturi$B)) [1] "B組の平均= 44.7 B組の不偏分散= 87.3444444444444"
[課題1-2の答え]
> length(Buturi[Buturi$A < 60,]$A) [1] 7 > Buturi[Buturi$A < 60,]$A [1] 52 41 33 19 35 48 15
> Data <- data.frame(A=Buturi$A, B=Buturi$B, course=rep(c("Eng","Tech"),5)) > Data A B course 1 52 42 Eng 2 86 59 Tech 3 41 32 Eng 4 64 54 Tech 5 33 35 Eng 6 19 45 Tech 7 65 35 Eng 8 35 55 Tech 9 48 49 Eng 10 15 41 Tech > eng <- Data[Data$course=="Eng",] > eng A B course 1 52 42 Eng 3 41 32 Eng 5 33 35 Eng 7 65 35 Eng 9 48 49 Eng > paste("平均=",mean(eng$A),"不偏分散=",var(eng$A)) [1] "平均= 47.8 不偏分散= 144.7"
cat関数はいくつでも引数をとることができ、その値を文字列として表示する。
引数の間はデフォルトでは1個のスペースが入るが、
デフォルトでは改行を行わないので、改行を行いたい場合は
> cat("This is a test.") This is a test.> # 改行が起きない > cat("This is another","test.\n") This is another test. # 改行する、anotherとtestの間に空白がはいる > i <- 10; cat("\ti =",i,"\tOK\n") i = 10 OK # iの前にタブ、10とOKの間にタブ
> inStr <- readline() これはRへの入力テストです。 > inStr [1] "これはRへの入力テストです。" > inStr <- readline("入力をどうぞ> ") 入力をどうぞ> 1 2 3 > inStr [1] "1 2 3"
Rでは文字列を扱う関数が用意されているものの、文字列データよりも数値型データの方が扱う対象としては主流である。上記でみてわかるように、数を入力しても
[文字列をベクトルにする方法]
> inStr <- "1 2 3 45 67 890" > strsplit(inStr," ") # strsplitの引数の" "によりそれにマッチしたところで「文字列を切る」 [[1]] [1] "1" "2" "3" "45" "67" "890" # 結果は文字列のリスト > unlist(strsplit(inStr," ")) # 関数unlistによりベクトルに変換 [1] "1" "2" "3" "45" "67" "890" > as.numeric(unlist(strsplit(inStr," "))) # as.numericにより文字列型のベクトルを数値型に変換 [1] 1 2 3 45 67 890
> inData <- scan() 1: 10 20 30 -50 -20 0 # プロンプトは現在何番目の要素を入力しているかを示す番号 7: # 改行、コントロールD,コントロールZなどで、入力終了 Read 6 items # 6個入力 > inData [1] 10 20 30 -50 -20 0 > dame <- scan() 1: 1 2 a b 以下にエラー scan(file, what, nmax, sep, dec, quote, skip, nlines, na.strings, : scan() 関数は 'a real' を期待したのに、得られたのは 'a' でした
Rでも「コネクション」という概念により上記のことを行うこともできるが、(1)から(3)まで一遍に「入出力コマンド」でやってしまうこともできるようになっている。
関数
> ( lines <- readLines("Rintro-03.html",3) ) # このページのソースを対象として、3行読み込む [1] "<html>" [2] "<head>" [3] " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=EUC-JP\">"「入力」の項で書いたように、Rではこのような文字列の入力は実際にはあまり使わないであろう。これに対して、ファイルからデータを読み込み「データフレーム」として記憶するのに使われる関数がよく使われる。 関数
CSVファイルの例としては、次にあげるtest.csv(区切り文字としてタブが使用されている)やtestC.csv(区切り文字はコンマ)があげられる。
[CSVファイルとは]
読み込み対象のファイルの第1行目は列の名前が書いてあるヘッダーとしてみなされるのがデフォルトである。ただし、それには条件があり、第1行目「だけ」他の行と項目数が1個少なくなければならない。そうでなければ(つまり、他の行と同じ個数の項目があった場合)、
引数名
################# test.csvの内容: (要素の区切りはタブ)####################### height weight gender Taro 180 80 male Hanako 160 45 female Ziro 170 65 male ############### testC.csv2の内容: (要素の区切りはコンマ) #################### height,weight,gender Taro,180,80,male Hanako,160,45,female Ziro,170,65,male
> (inData <- read.table("test.csv") ) height weight gender Taro 180 80 male Hanako 160 45 female Ziro 170 65 male > class(inData) [1] "data.frame" # データフレームになっている > rownames(inData) # 行の名前 [1] "Taro" "Hanako" "Ziro" > colnames(inData) # 列の名前 [1] "height" "weight" "gender" > dim(inData) # 次元 [1] 3 3 > ( inDataX <- read.table("test.csv",col.names=c("身長","体重","性別")) ) # col.namesを指定 身長 体重 性別 Taro 180 80 male Hanako 160 45 female Ziro 170 65 male > ( xxx <- read.csv("test.csv") ) # read.csvを使ってみる---不適切な使用であることが分かる height.weight.gender # read.csvの区切り文字(,)がないのですべて一つの項目とみなされるため失敗 1 Taro\t180\t80\tmale 2 Hanako\t160\t45\tfemale 3 Ziro\t170\t65\tmale > ( xxx <- read.csv("test.csv",sep="") ) # sep="" (空白を意味)を指定して読み込む height weight gender # ちゃんと読み込める Taro 180 80 male Hanako 160 45 female Ziro 170 65 male > ( inDataC <- read.csv("testC.csv") ) # 区切り文字がコンマのデータを読む height weight gender Taro 180 80 male Hanako 160 45 female Ziro 170 65 male今度は、ファイルにデータを書出す関数を紹介する。そのひとつは既に見た関数
> one <- 1:10 # 1から10までの数値のベクトル > cat(one,file="sample.txt") # 改行をしないでsample.txtに出力 > readLines("sample.txt") # sample.txtから読み込み [1] "1 2 3 4 5 6 7 8 9 10" 警告メッセージ: # エラーにはならないが「警告」が出る In readLines("sample.txt") : 'sample.txt' で不完全な最終行が見つかりました > cat(one,"\n",file="sample.txt") # 改行をつけてsample.txtに書き込む > readLines("sample.txt") # sample.txtから読み込み [1] "1 2 3 4 5 6 7 8 9 10" # 問題なく「文字列」として読み込める > cat(inData,"sample.txt") # 前の例題のデータフレームinDataをcatで書き込もうとする 以下にエラー cat(list(...), file, sep, fill, labels, append) : # エラーになる。listもdata.frameもcatできない 引数 1 (タイプ 'list') は 'cat' で取り扱えません > ( x <- matrix(1:12,3,4) ) # 行列をつくる [,1] [,2] [,3] [,4] [1,] 1 4 7 10 [2,] 2 5 8 11 [3,] 3 6 9 12 > cat(x,file="sample.txt") # 行列をcatでファイルに出力 > readLines("sample.txt") # sample.txtを読み込む [1] "1 2 3 4 5 6 7 8 9 10 11 12" # 行列がベクトルに変換され、書き込まれていたことがわかる 警告メッセージ: In readLines("sample.data") : 'sample.data' で不完全な最終行が見つかりましたファイルからの入力のところで書いたように、Rでは文字列をファイルに書き込んだり、ファイルから文字列を読み込むことはあまりない。やはりいちばん多い用途は、データフレームをファイルに書き込んだり、ファイルから読み込むことである。読み込みはread.tableなどでできることは既に見た。この反対の書き込みを行うのは、write.tableやwrite.csv、write.csv2関数である。
> write.table(inData,"sample.csv") # データフレームinDataをsample.csvに書き込む > read.table("sample.csv") height weight gender Taro 180 80 male Hanako 160 45 female Ziro 170 65 male > write.table(matrix(11;22,3,4),"sample.csv") # 3x4の行列をsample.csvに書き込む > read.table("sample.csv") V1 V2 V3 V4 1 11 14 17 20 2 12 15 18 21 3 13 16 19 22[Excelファイルを読み込む方法]
しかし時にはExcelが提供する形そのままのファイル(90年代では拡張子がxls、2000年代ではxlsx)をRに取り込みたいと思うかもしれない。
そのためには、
それらの準備ができたなら、次のようにすればExcelのファイルを読み込むことができる。ただし、最初の
> library(gdata) # gdataライブラリの読み込み。ない場合はgdataパッケージをインストールする > installXLSXsupport(perl="C:/Perl64/bin/perl.exe") # もしもgdataライブラリを使うのが初めてならこれが必要 > ( x <- read.xls("sample.xlsx") ) # sample.xlsxからデータを入力する。かなり時間がかかる X V1 V2 V3 V4 1 1 1 4 7 10 2 2 2 5 8 11 3 3 3 6 9 12 > ( x <- read.xls("sample.xlsx", sheet=2) ) # sample.xlsxの2枚目のシートの読み込み
Rで作業して作られた関数や変数の名前は、関数
> ls() [1] "English" "indData" "inDataC" "lines" [5] "Math" "Name" "one" "Seiseki" [9] "x" "xxx"作業スペース(Rで作業してきた変数や関数などのライブラリも含めた情報)をすべて保存するには関数save.imageを、特定の変数や関数を保存するには関数saveを用いる。また復元には関数loadを用いる:
> save(x,y,inData,Seiseki,file="save.data") # x, y, inData, Seisekiをsave.dataファイルに保存 > rm(x, y, inData, Seiseki) # x, y, inData, Seisekiを作業スペースから消去 > class(x) # xがあるかどうかをチェック エラー: オブジェクト 'x' がありません > load("save.data") # データの復元 > x # xの値の表示 [,1] [,2] [,3] [,4] [1,] 1 4 7 10 [2,] 2 5 8 11 [3,] 3 6 9 12 > save.image("save.data") # 作業スペースの保存 #---------- いったんRを停止し、再度立ち上げ --------------# > load("save.data") # 作業スペースの復元 > x # 復元されたxの値の表示 [,1] [,2] [,3] [,4] [1,] 1 4 7 10 [2,] 2 5 8 11 [3,] 3 6 9 12
A組: 52 86 41 64 33 19 65 35 48 15 B組: 42 59 32 54 35 45 35 55 49 41課題1-1では列の名前をそれぞれAとBとし、変数Buturiにこのデータフレームを代入した。 このことを前提として、この課題では次のことを行え:
「ファイル」メニューから「ディレクトリの変更」を選び、 コンピュータ演習室ではHドライブ(またはその下のどこかのフォルダ)を選択して、OKをクリックするなお、
Buturi <- data.frame(A=c(52, 86, 41, 64, 33, 19, 65, 35, 48, 15), B=c(42, 59, 32, 54, 35, 45, 35, 55, 49, 41))これをbuturi.csvというファイルに書き込むには次のようにする(注意:どのフォルダーに書き込むか、確認しておくこと。 それには「Rの基礎(4)」で紹介する
write.table(Buturi,"buturi.csv")Buturiという変数が消えたかどうかは、
Buturi <- read.table("buturi.csv")なお、save関数を用いて作られたファイルの中身は read.table では読み込めない(読み込んでも、無意味なデータが得られる)。save関数によって作られたファイルはload関数を用いて、データを読み込むこと。
demo(graphics) # graphicsのデモ demo(image) # 画像のデモ demo(plotmath) # 数式描画のデモ example(barplot) # 棒グラフbarplotの使用例これらは実行しながらソースコードも表示する。気に入ったものがあれば、そのコードを真似すればよい。
表示されるグラフィックスは、Windowsシステムの場合、「ファイル」メニューから「クリップボードにコピー」が可能である。これにより、WordやPowerPointなどの文書に貼り付けできる。また、同じく「ファイル」メニューから「別名で保存」を選ぶと、PDFやPostScript、PNG などの画像ファイルを作ることができる。
plot(数値ベクトル) plot(横軸の値となるベクトル, 縦軸の値となるベクトル) plot(データフレーム)plotはデフォルトでは「点」(デフォルトでは○)で該当するデータを表示する。これをなめらかに「線」でつなぐにはtype="l"というオプションを用いる。また、○ではなく他の文字や記号で表示するにはpch="+"のようなオプションを用いる。
plot(sin(seq(0,2*pi,0.1))) # 0から2πまで0.1刻みで表示 plot(sin(seq(0,2*pi,0.001))) # 0から2πまで0.001刻みで表示 plot(sin(seq(0,2*pi,0.1)),type="l") # 線で結ぶ A <- c(52, 86, 41, 64, 33, 19, 65, 35, 48, 15) B <- c(42, 59, 32, 54, 35, 45, 35, 55, 49, 41) plot(A, B, pch="*") # AとBの関係を表す散布図 plot(iris) # irisデータを用いてplotには色を指定するcol引数などいろいろなオプションがある。マニュアルなどでplotのオプションについて調べてほしい。
(2) Rの関数
curve(dnorm(x), -3, 3) # dnorm はガウス関数。 表示対象とする関数の式にxを書くのが重要この例に従い、定義域(
(3) このままでは、描画したグラフには、原点を通りx軸とy軸にそれぞれ平行な線が引かれないままである。そこで、
次のように
abline(h=0, lty=2) # h=0は水平線(x軸)) を0の位置から引くことを意味する abline(v=0, lty=2) # v=0は垂直線(y軸)を0の位置から引くことを意味する
まず、ファイルをHドライブ(など、自分に割り当てられたフォルダ)にセーブする。
次に、Rの「ファイル」タブから「ディレクトリの変更」を選び、ファイルを置いたフォルダを指定する。
これは、Rに対し、あなたがおいたファイルの場所を教えるためである
(この作業は、自宅のコンピュータでも必要であろう)。
最後に、解説で述べたread.csv関数を用いて、Rにデータを取り込む。
以下はdataの最初の数行を表示したものである: (注意: このように表示されない場合は、関 数もしくは区切り記号の指定が間違っている。 秀丸エディタやTeraPadなどで読み込み対象のファイルを開き、 データどデータがどのような記号で句切られているかを確認すること。 可能性がある区切り記号は、スペース、タブ、コンマ、 セミコロンの4種類であり、それによって使う関数を選べ)
> head(data) height weight sex year 1 79.6 11.0 M 2001 2 90.1 13.3 M 2001 3 96.8 14.9 M 2001 4 103.9 16.5 M 2001 5 109.6 18.7 M 2001 6 116.6 21.9 M 2001(2)dataから、sexの値が"M"(男性を意味する)のものを取り出し変数maleの値とせよ。同 様に、sexの値が"F"(女性を意味する)のものを取り出し、変数femaleの値とせよ。
以下はmaleとfemale、それぞれの最初の数行を表示したものである:
> head(male) height weight sex year 1 79.6 11.0 M 2001 2 90.1 13.3 M 2001 3 96.8 14.9 M 2001 4 103.9 16.5 M 2001 5 109.6 18.7 M 2001 6 116.6 21.9 M 2001 > head(female) height weight sex year 63 79.0 10.1 F 2001 64 87.6 12.3 F 2001 65 95.2 14.1 F 2001 66 103.1 16.8 F 2001 67 109.5 18.7 F 2001 68 114.0 20.4 F 2001[データフレームから特定の条件を満たす要素を取り出す]
(3)maleの各行に対し、weight列の値をx座標、height列の値をy座標とする点を青色で表示せよ。ここで表示には
(4)femaleの各行に対し、weight列の値をx座標、height列の値をy座標とする点を赤色で表示せよ。ここで表示には
(5)(4)を実行させると、x軸やy軸の表示が微妙にずれていることに気づくだろう。これはそれぞれのグラフで横軸と縦軸の調整を別々に行っているからである。
これを防ぐには、
(6)2つのグラフはともに○でプロットされるため、色がついていても判別が難しい。
そこで「基本的なグラフィックス」節で述べた
(7)得られた図は、まだまだ工夫が必要かもしれない。どのような図がよいか、 またそれにはどうしたらできるだろうか、考えを述べよ。