こんにちは、usagi-sanです。今回は前回の記事に続いて、データ整形の練習をしていきます。
前回は教育用データセットを扱いやすい形に整えました。
今回はデータフレームのnumeric型のデータやfactor型のデータの操作について学んでいきます。
今回用いるプログラミングコードは以下で ダウンロードできます。
R言語 データ整形numeric型やfactor型 Rスクリプト
ぜひぜひ練習用として、ダウンロードしてみてください 。
データセットを読み込む
では早速、教育用データセットを読み込み、前回の記事と同様に整形後のデータフレームを用意します。
次のコードは前回の記事と同様のものとなります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | #データ整形の方法 df <- read.csv("SSDSE-2020A.csv", fileEncoding = "CP932") for(i in 1:ncol(df)){ ##データ整形の練習として複数の種類の欠損値を代入 for (j in 1:10){ random <- sample(3:nrow(df), 30) #sample()ベクトルの要素をランダムに返す df[random[1:10], i] <- "" df[random[11:20], i] <- "欠損値" df[random[21:30], i] <- "unknown" } } #NA挿入後のデータフレームを保存(ここでは列数が多いので30列まで扱う) write.csv(df, paste0("SSDSE-2020A(NA挿入後).csv"), fileEncoding = "CP932", row.names = F) #準備が整ったので、再度データを読み込む df <- read.csv(paste0("SSDSE-2020A(NA挿入後).csv"), fileEncoding = "CP932") #データの列名としては、1,2行目のみ必要そうなので、2行を組み合わせて列名とする colnames(df) <- paste0(df[1,], "_" ,df[2,]) #念のため、区切り文字として"_"を用いる df <- df[-c(1,2),] #1,2行目を消去する #欠損値を整理する #for文を用いた例 for(i in 1:ncol(df)){ df[,i] <- replace(df[,i], df[,i] == "" | df[,i] == "欠損値" | df[,i] == "unknown", NA) } #apply関数を用いた例 #df<- apply(df, 2, function(x){ # return(replace(x, x == "" | x == "欠損値" | x == "unknown" , NA)) # } #) #数値データの整理 #for文を用いた例 for(i in 4:ncol(df)){ df[,i] <- as.numeric(df[,i]) } #apply関数を用いた例 #df[,4:ncol(df)]<- apply(df[,4:ncol(df)], 2, function(x){ # return(as.numeric(x)) #} #) #因子データの整理 #for文を用いた例 for(i in 1:3){ df[,i] <- as.factor(df[,i]) } #apply関数を用いた例 #df[,1:3]<- apply(df[,1:3], 2, function(x){ # return(as.factor(x)) #} #) |
実行するとdfに整形後のデータフレームが代入されています。
以降、このdfのnumeric型のデータやfactor型のデータの操作を説明します。
numeric型のデータをカテゴリカルデータに
まず、数値データ(numeric型のデータ)をカテゴリカルデータに変更する方法を紹介します。
カテゴリカルデータの例
例えば、次の身長を意味するheightというデータを次で与えます。
1 | height<- c(155,178,158,165,167,172,159,172) |
このデータを~160、160~170、170~という3つのグループに分けると、heightは次のようになります。
1 2 3 | > catHeight [1] ~160 170~ ~160 160~170 160~170 170~ ~160 170~ Levels: ~160 160~170 170~ |
身長というnumeric型のデータを~160、160~170、170~の3つのグループで分けることで3つ水準からなるfactor型のカテゴリカルデータを作成しました。
このような大小関係をもつfactor型のデータに変更できます。
関数cut
カテゴリカルデータを作成する際には、関数cutを用いると便利です。
dfの"2015_総人口"という列に対して、関数cutを使ってみます。
総人口を「0~5000、5000~10000、10000~15000、15000~20000、20000~」という5つのカテゴリに分けたいときには、次を実行します。
1 2 | bar <- cut(df[,"2015_総人口"], breaks = c(0, 5000, 10000, 15000, 20000,1e+10), labels = c("0~5000", "5000~10000", "10000~15000","15000~20000","20000~"), right = FALSE) |
barを参照すると次のように、df[,"2015総人口"]が「0~5000、5000~10000、10000~15000、15000~20000、20000~」を水準としてもつfactor型のデータに変わっていることが分かります。
cutの引数breaksに分割する区間を指定し、labelsに水準の名前を指定します。
また、引数rightは区間の閉区間、開区間の設定を行います。
上を例に挙げて説明すると、
- right=TRUEのとき「0~5000」という区間は(0, 5000]、すなわち「0より大きく5000以下」
- right = FALSEのとき、「0~5000」という区間は[0, 5000)、すなわち「0以上5000未満」
を意味します。
特にこだわりがないのであれば、right = FALSEをお勧めします。
余談にはなりますが、次のようにbreaksを設定すると、10000個の区間を作ることもできます。
1 2 3 4 5 6 7 8 | > tmp <- cut(df[,"2015_総人口"], breaks = 100*(0:10000), right = FALSE) #1万個の区間で分割 > tmp[1:20] [1] <NA> [2.659e+05,2.66e+05) [1.219e+05,1.22e+05) [3.396e+05,3.397e+05) [5] [8.85e+04,8.86e+04) [1.747e+05,1.748e+05) [1.693e+05,1.694e+05) [1.212e+05,1.213e+05) [9] [8800,8900) [8.44e+04,8.45e+04) [3.9e+04,3.91e+04) [2.22e+04,2.23e+04) [13] [1.727e+05,1.728e+05) [3.63e+04,3.64e+04) [2.3e+04,2.31e+04) [1.46e+04,1.47e+04) [17] [1.206e+05,1.207e+05) [1.11e+04,1.12e+04) [2.31e+04,2.32e+04) [1.99e+04,2e+04) 10000 Levels: [0,100) [100,200) [200,300) [300,400) [400,500) [500,600) [600,700) [700,800) ... [9.999e+05,1e+06) |
関数cut(x, breaks, labels = NULL, include.lowest = FALSE, right = TRUE, dig.lab = 3, ordered_result = FALSE, ...)の引数について、以下の表にまとめました。
breaks | カットポイントの数値ベクトル。またはカットする間隔。 |
labels | カテゴリカルデータの各レベルのラベルを指定する |
include.lowest | 閉区間に等しいデータをその区間のカテゴリに含めるか。デフォルトのright=TRUEの場合、左側の閉区間を含めるかどうか。 |
right | 区間を右側で閉じるか、また閉じないか。right = TRUEの場合、左側を閉じる。 |
dig.lab | ラベルを指定していないときに、levelsに使用される数値の桁数 |
ordered_result | 順序付けられた結果を返すかどうか |
データの挿入
barにdf[,"2015_総人口"]のカテゴリカルデータが入っているので、このベクトルをdfに挿入しましょう。
cbind、data.frameを用いてデータフレームにベクトルを挿入できます。他の方法として、関数transformを用いる方法もあります。
次のように実行することでdfの一番右に、ベクトルを挿入することでできます。
注意として、今回用いるデータの列名は"2015_~"などと数字で始まるため、transformを用いると列名の最初にXが加わります。cbindやdata.frameを用いるのが賢明です。
1 2 | df <- cbind(df, 総人口カテゴリカル = bar) #dfにカテゴリカルデータを追加する #df <- transform(df, 総人口カテゴリカル = bar) #transformを用いた例 |
関数transform(x, tag = y)の引数を以下にまとめました。
y | データフレームxに挿入するベクトル。tagを指定すると、tagが列名となる。 |
factor型のデータの操作
dfの都道府県の列を用いてfactorの操作の練習をしてみます。
factorのlevels(水準)を変更したり、levelsをプール(組み合わせ)したり、levelsの順番を変更したりしていきます。
levelsの参照
まず、df[, "年度_都道府県"]を参照してみます。
1 2 3 4 | > df[1:20,"年度_都道府県"] [1] <NA> 北海道 北海道 北海道 北海道 北海道 北海道 北海道 <NA> <NA> 北海道 北海道 北海道 北海道 [15] 北海道 北海道 北海道 北海道 北海道 北海道 47 Levels: 愛知県 愛媛県 茨城県 岡山県 沖縄県 岩手県 岐阜県 宮崎県 宮城県 京都府 熊本県 群馬県 ... 和歌山県 |
levelsは47都道府県であることが分かります。levelsは関数levelsを用いることで次のようにあることができます。
1 2 3 4 5 6 7 | > levels(df[,"年度_都道府県"]) [1] "愛知県" "愛媛県" "茨城県" "岡山県" "沖縄県" "岩手県" "岐阜県" "宮崎県" "宮城県" [10] "京都府" "熊本県" "群馬県" "広島県" "香川県" "高知県" "佐賀県" "埼玉県" "三重県" [19] "山形県" "山口県" "山梨県" "滋賀県" "鹿児島県" "秋田県" "新潟県" "神奈川県" "青森県" [28] "静岡県" "石川県" "千葉県" "大阪府" "大分県" "長崎県" "長野県" "鳥取県" "島根県" [37] "東京都" "徳島県" "栃木県" "奈良県" "富山県" "福井県" "福岡県" "福島県" "兵庫県" [46] "北海道" "和歌山県" |
上の実行例を見ればわかる通り、辞書順(levelsはアルファベット順、またはあいうえお順)になっていることができます。
levelsの数を参照するときには、関数nlevelsを用います。
1 2 | > nlevels(bar) #水準の数 [1] 8 |
levelsの変更
では、levelsを都道府県別から地方別に変更してみましょう。
準備として、次のように各都道府県に対応する地方のリストを定義します。
1 2 3 4 5 6 7 8 9 | #因子の水準を変更してみる hokkaidou <- list(c("北海道"),region = "北海道地方") touhoku <- list(c("青森県", "岩手県", "宮城県", "秋田県", "山形県", "福島県"), region = "東北地方") kantou <- list(c("茨城県", "栃木県","群馬県" , "埼玉県", "千葉県", "東京都", "神奈川県"), region = "関東地方") tyuubu <- list(c("新潟県", "富山県","石川県" ,"福井県" ,"山梨県" ,"長野県" , "岐阜県", "静岡県", "愛知県"), region = "中部地方") kinki <- list(c("三重県", "滋賀県", "京都府", "大阪府", "兵庫県", "奈良県", "和歌山県"), region = "近畿地方") tyuugoku <- list(c("鳥取県", "島根県", "岡山県","広島県" , "山口県"), region = "中国地方") sikoku <- list(c("徳島県", "香川県", "愛媛県", "高知県"), region = "四国地方") kyuusyuu <- list(c("福岡県", "佐賀県", "長崎県", "熊本県", "大分県", "宮崎県", "鹿児島県", "沖縄県"), region = "九州地方") |
各地方のリストの最初の要素にその地方の都道府県を、2つ目の要素に地方の名前を代入しました。
これらのリストをさらにリストlist_regionに格納することで参照が楽になります(fot文を用いるときに楽になります)。
1 | list_region <- list(hokkaidou, touhoku, kantou, tyuubu, kinki, tyuugoku, sikoku, kyuusyuu) |
levelsを変更する用意が整いました。関数replaceを用いて、都道府県に対応する地方に置き換えてみましょう。
1 2 3 4 5 6 7 8 9 | bar <- as.character(df[,"年度_都道府県"]) #character型に変更する for(i in list_region){ for(j in i[[1]]){ bar <- replace(bar, bar == j, i$region) } } bar <- as.factor(bar) |
for文について、iはlist_regionの要素である各都道府県のリストであり、jは各都道府県のリストiの1番目の要素である都道府県のベクトルを指します。都道府県のベクトルi[[1]]の要素jに等しいbarの要素を地方i$regionで置き換えています。
注意として、最初にfactorであるdf[,"年度_都道府県"]をcharacterに変更してます。
factor型のデータにreplaceを用いるとエラーを出すため、注意しましょう。
最後に9行目でcharacter型のbarをfactor形に戻します。
次を実行すると都道府県のlevelsが地方に置き換わっていることが分かります。
1 2 3 4 5 | > bar[1:20] [1] <NA> 北海道地方 北海道地方 北海道地方 北海道地方 北海道地方 北海道地方 北海道地方 <NA> [10] <NA> 北海道地方 北海道地方 北海道地方 北海道地方 北海道地方 北海道地方 北海道地方 北海道地方 [19] 北海道地方 北海道地方 Levels: 関東地方 近畿地方 九州地方 四国地方 中国地方 中部地方 東北地方 北海道地方 |
また、関数levelsで次のようにも、levelsの順序が変更されたことが分かります。
1 2 | > levels(bar) [1] "北海道地方" "東北地方" "関東地方" "中部地方" "近畿地方" "中国地方" "四国地方" "九州地方" |
levelsの順番の変更
今度はlevelsの順序を変更する方法を説明します。
先ほどの地方のfactorのlevelsの順序を見てみると、辞書順に並んでおり、これを「北海道、東北地方、関東地方、. . . 、九州地方」に変更したい時があると思います。
関数factorの引数levelsを指定してあげることでlevelsの変更が可能です。
下のコード2行目のように、levelsのように順序を指定実行します。
1 2 | #水準の順番を変更する bar <- factor(bar, levels = c("北海道地方", "東北地方", "関東地方", "中部地方", "近畿地方", "中国地方", "四国地方", "九州地方")) |
順序が変更されているか確認してみます。
1 2 3 4 5 | > bar[1:20] [1] 北海道地方 北海道地方 北海道地方 北海道地方 北海道地方 北海道地方 <NA> 北海道地方 北海道地方 [10] 北海道地方 北海道地方 北海道地方 北海道地方 <NA> 北海道地方 北海道地方 北海道地方 北海道地方 [19] 北海道地方 北海道地方 Levels: 北海道地方 東北地方 関東地方 中部地方 近畿地方 中国地方 四国地方 九州地方 |
上のようにLevesの項目が「関東地方 近畿地方 九州地方、. . . 」から「北海道地方 東北地方 関東地方、. . . 」に変更されたことが分かります。
最後に列名"地方"として、このfactorの列をdfの一番右に挿入します。
1 2 | df <- cbind(df, 地方 = bar) #df <- transform(df, 地方 = bar) #transformを用いた例 |
水準のプール
統計解析を行う上で、水準をプール(組み合わせる)したい場合があります。
levelsをプールしたいときには、前にも紹介したreplaceを用います。
北海道地方と東北地方をプールを例に説明していきます。
1 2 3 4 | #北海道地方と東北地方をプールする bar <- as.character(bar) bar <- replace(bar, bar == "東北地方" | bar == "北海道地方", "東北地方.北海道地方") bar <- as.factor(bar) |
2行目でfactorをcharacterに変更後、3行目でreplaceを用いて"東北地方"、または"北海道地方"を"東北地方.北海道地方"に置き換えています。最後に4ぎょうめで再びfactorに戻します。
levelsを用いて確認してみると、次のようにlevelsの項目に東北地方と北海道地方をプールしたlevelが加えられたことが分かります。
1 2 3 | > levels(bar) [1] "関東地方" "近畿地方" "九州地方" "四国地方" [5] "中国地方" "中部地方" "東北地方.北海道地方" |
最後にdfの一番右にプール後の列を挿入します。
1 2 | df <- cbind(df, 地方.東北北海道プール = bar) #df <- transform(df, 地方.東北北海道プール = bar) #transformを用いた例 |
levelsの名前の変更
levelsをの変更方法について紹介します。
「男性、女性」を「Male、Female」や「1、0」に変更したい場合などがあります。
次のように関数factorの引数にlevelsとlabelsを指定することで、変更したいlevelsをlabelsの名前に変更することができます。
1 2 | #factorの水準名の変更 bar <- factor(df[,"総人口カテゴリカル"], levels = levels(df[,"総人口カテゴリカル"]), labels = c("とても少ない" ,"少ない" ,"普通" ,"多い" ,"とても多い")) |
上のコードでは、factorは、labelsがlevelsに対応付けられることを利用しています。
barを参照してみると、「0~5000、5000~10000、10000~15000、15000~20000、20000~」というlevelsが「とても少ない、少ない、普通、多い、とても多い」に変更されたことが確認できます。
1 2 3 4 5 | > bar[1:20] [1] <NA> とても多い とても多い <NA> <NA> とても多い とても多い とても多い 少ない [10] とても多い <NA> とても多い とても多い とても多い とても多い 普通 とても多い 普通 [19] とても多い 多い Levels: とても少ない 少ない 普通 多い とても多い |
levelsに大小関係を持たせる
levelsに大小関係を持たせる方法を紹介します。
先ほどのlevels「とても少ない、少ない、普通、多い、とても多い」を「とても少ない<少ない<普通<多い<とても多い」というように大小関係を持たせ、数値のようにふるまいたい場合があると思います。
levelsに大小関係を持たせるときは、関数orderedを用います。
levelsの変更との違いは、大小関係を持たせることで比較演算子「<、>」が使えるようになることです。
まず準備として、先ほどのbarをcharacterに変更しfactorにすることで、levelsを辞書順にします。
1 2 | #注意characterに変更後factorにすると水準が文字コード順に並ぶ bar <- as.factor(as.character(bar)) |
barを参照すると、levelsの順序がおかしくなっていることが分かります。これを関数orderedを用いて、大小関係を持たせていきます。
orderedの引数は、levelsの変更で行った指定方法と同じです。変更後のlevelsを引数levelsに指定します。
1 2 | #factorの水準の大小関係の変更 bar <- ordered(bar, levels = c("とても少ない" ,"少ない" ,"普通" ,"多い" ,"とても多い")) |
上を実行し、以下のようにbarを参照すると、Levelsの項目に大小関係「<」が含まれていることが分かります。
1 2 3 4 5 | > bar[1:20] [1] <NA> とても多い とても多い <NA> <NA> とても多い とても多い とても多い 少ない [10] とても多い <NA> とても多い とても多い とても多い とても多い 普通 とても多い 普通 [19] とても多い 多い Levels: とても少ない < 少ない < 普通 < 多い < とても多い |
大小関係を持たせたことで、次のように比較演算子を用いて論理演算を行うことが可能となりました。
1 2 3 | > bar[1:20] > "普通" [1] NA TRUE TRUE NA NA TRUE TRUE TRUE FALSE TRUE NA TRUE TRUE TRUE TRUE FALSE TRUE [18] FALSE TRUE TRUE |
factorの操作に用いる関数
factorの操作で用いた関数を以下にまとめました。
関数levels
levels(x)の引数を以下にまとめました。
x | levelsを参照したいfactor型のベクトル |
関数nlevels
nlevels(x)の引数を以下にまとめました。
x | levelsの数を参照したいfactor型のベクトル |
関数factor
factor(x, levels, labels = levels, exclude = NA, ordered = is.ordered(x), nmax = NA)の引数を以下にまとめました。
levels | xのlevelsを指定する |
labels | levelsにラベル付けをする |
exclude | levelsから除外するlevels。初期値がNAであり、NAを除外したくないときはexclude = ""とする。 |
ordered | levelsの順序。初期値は辞書順。 |
nmax | levels数の上限 |
関数ordered
ordered(x,...)の引数を以下にまとめました。
x | 順序を指定するfactor型のベクトル |
levels | R document上では、互換性の観点から引数として載っていないが、levelsの順番で順序を指定できる。 |
numeric型とfactor型の注意点
最後にnumeric型とfactor型を操作するときの注意点について、具体例を交えながら説明します。
今回用いたデータを例に、総人口を10000個のlevelsで分割したfactorを用意します。
1 2 | #例として2015年総人口を10000ずつ分割 bar <- cut(df[,"総人口"], breaks = 10000*(0:100), labels = 10000*c(0:99) , right = FALSE) #1万個の区間で分割 |
barを参照すると、次のように10000個で分割されていることが分かります。
1 2 3 4 | > bar[1:20] [1] <NA> 260000 120000 330000 80000 170000 160000 <NA> 0 80000 30000 20000 <NA> 30000 [15] 20000 10000 120000 10000 20000 10000 100 Levels: 0 10000 20000 30000 40000 50000 60000 70000 80000 90000 1e+05 110000 120000 130000 ... 990000 |
このfactorを数値と扱いたため、as.factorを行ってみます。
すると、次のように元のfactorのlevelsの値と異なることが分かります。
1 2 3 | > as.numeric(bar) #factorをnumericにすると数値が水準のindexになります [1] NA 27 13 34 9 18 17 NA 1 9 4 3 NA 4 3 2 13 2 3 2 3 1 3 10 5 2 1 3 3 NA 7 4 NA 6 [35] 5 2 1 1 1 1 1 NA 1 2 NA 1 1 1 1 1 1 1 NA 1 NA 1 1 1 1 1 1 NA 2 1 NA 1 1 1 |
factorをnumericにすると各要素が水準のindexになってしまうことが原因です。
これを解決するには、下のようにas.numericを行う前にas.characterを用います。
1 2 3 | > as.numeric(as.character(bar)) #数値のまま参照できる [1] NA 260000 120000 330000 80000 170000 160000 NA 0 80000 30000 20000 NA 30000 [15] 20000 10000 120000 10000 20000 10000 20000 0 20000 90000 40000 10000 0 20000 |
このようにして、factorの要素をnumericとして扱ことが可能となります。
まとめ
numeric型やfactor型の操作やnumeric型、character型、factor型にそれぞれ変換する方法を学びました。
今回紹介した内容で、ほとんどのデータ整形に対応できると思います。
ぜひここで得た内容を役立ててください。