Data_Handling(Basic) - Rによる超一般的なデータハンドルの話 -
自己紹介
分析屋の堀井です。 今回はTidyverseというRを語るうえで欠かせないパッケージ群と基礎的なデータハンドリングについて説明します。 前回の「Rmarkdowのすゝめ」ではデータの可視化をやるといいましたが、その前段階であるデータハンドリングについて軽く触れることにしました。
普段はライフサイエンス部で機械学習・深層学習を用いて人による判断の自動化を行っています。
弊社の技術ブログでは勝手にRを解説するシリーズを投稿して、社内にRの良さを布教することを画策しています。
▼前回の記事はこちら
Tidyverseの準備
TidyverseとはR界の神Hadley Wickhamらによって開発された、 「基礎となる設計哲学、文法、およびデータ構造を共有する」Rパッケージ群です。
余談ですが、日本では「羽鳥」という愛称で親しまれており、Rを使っていると色々なところで名前を見たり聞いたりすることになります。世界中に信者がおり、「羽鳥教」なんて呼ばれていたり…
Tidyverseは主に以下のパッケージで構成されています。
ggplot2: グラフ作成のためのパッケージ
dplyr: データハンドリングのためのパッケージ
tidyr: tidy dataの作成・操作のためのパッケージ
- tidy data:整然データreadr: 様々な形式のデータを読み込むためのパッケージ
purrr: ループ処理やlist処理を簡潔に行うためのパッケージ
tibble: tibbleというデータ構造を作成・操作するためのパッケージ
- tibble:Dataframeの進化版)stringr: 文字列を操作のためのパッケージ
forcats: factor型のデータ操作のためのパッケージ
magrittr: パイプ演算子の機能を提供するパッケージ
- パイプ演算子:(%>%):左辺の出力を右辺の関数の第1引数にするための記号
- R4.1では(|>)という書き方のパイプ演算子が開発された。多くの書籍では(%>%)が使われているので、基本的には(%>%)でコードは書いていきます。
Tidyverseのインストールはinstall.packages("tidyverse")で行います。
R MarkdownでのRコードの実行方法
R MarkdownでのRコードを実行する場合、以下のようなchunkというブロックにコードを書く必要があります。
チャンクはctrl + alt + iでショートカット入力が可能です。
試しにハローワールドしましょう。以下のように入力しましょう。
R Markdownでコード実行すると以下のようにHTMLに出力されます。 基本的には「コード」「出力結果」が表示されます。
R Markdownでのコードの実行方法がわかったので、実際にデータハンドリングをしてみましょう。
データハンドリング
csvデータの読み込み
まずはデータがないと始まりませんね。
今回はカリフォルニアの住宅価格のデータセットを使用します。
様々なオープンデータを紹介しているサイトの「housez.zip」をcsv形式にしたものを使用します。
Tidyverseを読み込み、readrパッケージのread_csv()でデータを読み込みましょう。
まずは、library("tidyverse")でTidyverseを読み込みます。
library("tidyverse")
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.1 ✔ readr 2.1.4
## ✔ forcats 1.0.0 ✔ stringr 1.5.0
## ✔ ggplot2 3.4.2 ✔ tibble 3.2.1
## ✔ lubridate 1.9.2 ✔ tidyr 1.3.0
## ✔ purrr 1.0.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
そして、csvデータを読み込みます。
df <- read_csv("data/California_Housing.csv")
## Rows: 20640 Columns: 9
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## dbl (9): MedInc, HouseAge, AveRooms, AveBedrms, Population, AveOccup, Latitu...
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
ここで少しチャンクについて補足をします。
チャンクにはオプションがあり、出力方法を制御できます。
例えば、library("tidyverse")やdf <- read_csv("data/California_Housing.csv")を実行した際に、
以下のようなメッセージが出てきました。
これを出力しないようにしましょう。
以下のようにチャンクオプションを設定します。
また、以下のようなコードを実行するとR Markdown全体にチャンクオプションを設定できます。
ちなみに、include=FALSEは実行するがHTML等には結果を出力しないという設定です。
データのざっくりとした確認
本題に戻りまして、
読み込んだデータの中身を見てみましょう。
簡単にデータ量や変数の数を確認する場合はglimpse()を使用します。
glimpse(df)
## Rows: 20,640
## Columns: 9
## $ MedInc <dbl> 8.3252, 8.3014, 7.2574, 5.6431, 3.8462, 4.0368, 3.6591, 3.1…
## $ HouseAge <dbl> 41, 21, 52, 52, 52, 52, 52, 52, 42, 52, 52, 52, 52, 52, 52,…
## $ AveRooms <dbl> 6.984127, 6.238137, 8.288136, 5.817352, 6.281853, 4.761658,…
## $ AveBedrms <dbl> 1.0238095, 0.9718805, 1.0734463, 1.0730594, 1.0810811, 1.10…
## $ Population <dbl> 322, 2401, 496, 558, 565, 413, 1094, 1157, 1206, 1551, 910,…
## $ AveOccup <dbl> 2.555556, 2.109842, 2.802260, 2.547945, 2.181467, 2.139896,…
## $ Latitude <dbl> 37.88, 37.86, 37.85, 37.85, 37.85, 37.85, 37.84, 37.84, 37.…
## $ Longitude <dbl> -122.23, -122.22, -122.24, -122.25, -122.25, -122.25, -122.…
## $ target <dbl> 4.526, 3.585, 3.521, 3.413, 3.422, 2.697, 2.992, 2.414, 2.2…
このデータセットは9変数あり、20640行のレコードがあるとわかりました。
もっと詳しくデータセットの概要を見てみましょう
summary()で各変数の最小値、第一四分位数、中央値、平均値、第三四分位数、最大値を確認できます。
summary(df)
## MedInc HouseAge AveRooms AveBedrms
## Min. : 0.4999 Min. : 1.00 Min. : 0.8461 Min. : 0.3333
## 1st Qu.: 2.5634 1st Qu.:18.00 1st Qu.: 4.4407 1st Qu.: 1.0061
## Median : 3.5348 Median :29.00 Median : 5.2291 Median : 1.0488
## Mean : 3.8707 Mean :28.64 Mean : 5.4290 Mean : 1.0967
## 3rd Qu.: 4.7432 3rd Qu.:37.00 3rd Qu.: 6.0524 3rd Qu.: 1.0995
## Max. :15.0001 Max. :52.00 Max. :141.9091 Max. :34.0667
## Population AveOccup Latitude Longitude
## Min. : 3 Min. : 0.6923 Min. :32.54 Min. :-124.3
## 1st Qu.: 787 1st Qu.: 2.4297 1st Qu.:33.93 1st Qu.:-121.8
## Median : 1166 Median : 2.8181 Median :34.26 Median :-118.5
## Mean : 1425 Mean : 3.0707 Mean :35.63 Mean :-119.6
## 3rd Qu.: 1725 3rd Qu.: 3.2823 3rd Qu.:37.71 3rd Qu.:-118.0
## Max. :35682 Max. :1243.3333 Max. :41.95 Max. :-114.3
## target
## Min. :0.150
## 1st Qu.:1.196
## Median :1.797
## Mean :2.069
## 3rd Qu.:2.647
## Max. :5.000
ちなみに、このデータセットの各変数は以下のような意味を持っています。
データの抽出(列)
データの確認ができたらデータを抽出してみましょう。
まずは{dplyr}のselect()を使って列を絞ります。
参考程度にパイプ演算子を使っていないコードと使っているコードを併記してみます。
# %>% を使わない方法
select(df, MedInc, HouseAge, AveOccup)
## # A tibble: 20,640 × 3
## MedInc HouseAge AveOccup
## <dbl> <dbl> <dbl>
## 1 8.33 41 2.56
## 2 8.30 21 2.11
## 3 7.26 52 2.80
## 4 5.64 52 2.55
## 5 3.85 52 2.18
## 6 4.04 52 2.14
## 7 3.66 52 2.13
## 8 3.12 52 1.79
## 9 2.08 42 2.03
## 10 3.69 52 2.17
## # ℹ 20,630 more rows
# %>% を使う方法
df %>%
select(MedInc, HouseAge, AveOccup)
## # A tibble: 20,640 × 3
## MedInc HouseAge AveOccup
## <dbl> <dbl> <dbl>
## 1 8.33 41 2.56
## 2 8.30 21 2.11
## 3 7.26 52 2.80
## 4 5.64 52 2.55
## 5 3.85 52 2.18
## 6 4.04 52 2.14
## 7 3.66 52 2.13
## 8 3.12 52 1.79
## 9 2.08 42 2.03
## 10 3.69 52 2.17
## # ℹ 20,630 more rows
パイプ演算子を使うとselect()の中身が変数名のみなので、見やすくなったと思います。
もっと処理が増えるとより効果的に可読性が上がります。
数行のデータ抽出
最初の数行を抽出したい場合はslice_head()を使用します。 n=で抜き出す行数を選びます
df %>%
slice_head(n=5)
## # A tibble: 5 × 9
## MedInc HouseAge AveRooms AveBedrms Population AveOccup Latitude Longitude
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 8.33 41 6.98 1.02 322 2.56 37.9 -122.
## 2 8.30 21 6.24 0.972 2401 2.11 37.9 -122.
## 3 7.26 52 8.29 1.07 496 2.80 37.8 -122.
## 4 5.64 52 5.82 1.07 558 2.55 37.8 -122.
## 5 3.85 52 6.28 1.08 565 2.18 37.8 -122.
## # ℹ 1 more variable: target <dbl>
最後の数行を抽出したい場合はslice_tail()を使用します。 n=で抜き出す行数を選びます
df %>%
slice_tail(n=5)
## # A tibble: 5 × 9
## MedInc HouseAge AveRooms AveBedrms Population AveOccup Latitude Longitude
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1.56 25 5.05 1.13 845 2.56 39.5 -121.
## 2 2.56 18 6.11 1.32 356 3.12 39.5 -121.
## 3 1.7 17 5.21 1.12 1007 2.33 39.4 -121.
## 4 1.87 18 5.33 1.17 741 2.12 39.4 -121.
## 5 2.39 16 5.25 1.16 1387 2.62 39.4 -121.
## # ℹ 1 more variable: target <dbl>
指定した変数の大きい順に数行を抽出することもできます。 slice_max()を使用します。
df %>%
slice_max(AveOccup, n=3)
## # A tibble: 3 × 9
## MedInc HouseAge AveRooms AveBedrms Population AveOccup Latitude Longitude
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 10.2 45 3.17 0.833 7460 1243. 38.3 -122.
## 2 5.52 36 5.14 1.14 4198 600. 40.4 -121.
## 3 4.26 46 9.08 1.31 6532 502. 35.3 -121.
## # ℹ 1 more variable: target <dbl>
小さい順もできます slice_min()を使用します。
df %>%
slice_min(AveOccup, n=3)
## # A tibble: 3 × 9
## MedInc HouseAge AveRooms AveBedrms Population AveOccup Latitude Longitude
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2.59 8 28.6 5.49 27 0.692 39.8 -121
## 2 0.536 16 4.5 1.5 3 0.75 34.0 -118.
## 3 0.682 17 2.37 0.990 198 0.971 37.6 -121
## # ℹ 1 more variable: target <dbl>
無作為に数行を出力なんてこともできます。 slice_sample()を使用します。
df %>%
slice_sample(n=10)
## # A tibble: 10 × 9
## MedInc HouseAge AveRooms AveBedrms Population AveOccup Latitude Longitude
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2.76 16 3.31 0.971 1925 5.12 33.7 -118.
## 2 2.08 34 4.22 0.965 661 3.87 33.9 -118.
## 3 4.38 26 4.72 1.20 1098 2.03 34.2 -118.
## 4 1.47 44 5.87 1.22 931 3.51 34.0 -118.
## 5 3.19 16 4.39 0.981 1386 2.18 38.5 -122.
## 6 4.88 20 6.48 1.10 862 3.48 32.8 -116.
## 7 1.60 29 4.56 1.07 1112 2.02 38.0 -120.
## 8 3.89 19 4.97 1.03 1126 2.89 34.1 -118.
## 9 5.10 13 7.08 1.13 1075 3.07 37.9 -122.
## 10 3.31 33 5.47 0.950 1113 2.30 41.7 -123.
## # ℹ 1 more variable: target <dbl>
条件による行の抽出
上記で説明したような行の抽出方法は解析で使用するデータの抽出というよりは特徴を掴むための抽出です。
次は使用頻度の高い条件による抽出を紹介します。
例えば、築年数5年以下の住宅を抜き出してみます。
df %>%
filter(HouseAge <= 5)
出力
## # A tibble: 559 × 9
## MedInc HouseAge AveRooms AveBedrms Population AveOccup Latitude Longitude
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2.56 2 2.77 0.754 94 1.65 37.8 -122.
## 2 7.61 5 6.86 1.06 7427 2.73 37.7 -122.
## 3 7.06 5 7.02 0.912 1738 3.39 37.7 -122.
## 4 3.66 5 5.16 1.21 814 2.48 37.7 -122.
## 5 5.49 5 5.20 1.01 840 2.09 37.7 -122.
## 6 3.28 5 4.78 1.18 2240 2.83 37.6 -122.
## 7 2.88 2 5.62 1.98 240 1.61 37.6 -122.
## 8 3.57 5 4.69 1.27 648 2.64 37.5 -122.
## 9 6.08 3 5.09 1.04 7205 2.62 37.5 -122.
## 10 4.14 4 4.47 1.18 1606 1.92 37.6 -122.
## # ℹ 549 more rows
## # ℹ 1 more variable: target <dbl>
次は複数条件で抽出してみましょう。
築年数が5から10の住宅を抜き出します。
df %>%
filter(HouseAge >= 5 & HouseAge <= 10)
## # A tibble: 1,254 × 9
## MedInc HouseAge AveRooms AveBedrms Population AveOccup Latitude Longitude
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 0.76 10 2.65 1.05 546 1.65 37.8 -122.
## 2 0.972 10 2.69 1.08 125 3.21 37.8 -122.
## 3 2.17 10 5.03 1.18 228 2.28 37.9 -122.
## 4 7.61 5 6.86 1.06 7427 2.73 37.7 -122.
## 5 7.06 5 7.02 0.912 1738 3.39 37.7 -122.
## 6 3.66 5 5.16 1.21 814 2.48 37.7 -122.
## 7 3.34 10 4.60 1.24 877 1.81 37.7 -122.
## 8 5.49 5 5.20 1.01 840 2.09 37.7 -122.
## 9 2.40 10 4.72 1.19 1140 2.42 37.7 -122.
## 10 3.90 6 4.27 1.03 1189 2.08 37.6 -122.
## # ℹ 1,244 more rows
## # ℹ 1 more variable: target <dbl>
次は複数の変数で抽出してみましょう。
築年数5年以下かつその物件がある地域の世帯年収の中央値が30000ドル以下の物件を抽出します。
df %>%
filter(HouseAge <= 5 & MedInc <= 3)
## # A tibble: 61 × 9
## MedInc HouseAge AveRooms AveBedrms Population AveOccup Latitude Longitude
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2.56 2 2.77 0.754 94 1.65 37.8 -122.
## 2 2.88 2 5.62 1.98 240 1.61 37.6 -122.
## 3 2.12 3 3.39 1.01 769 1.67 37.7 -122.
## 4 2.96 2 4.48 1.01 1606 2.30 38.0 -122.
## 5 1.81 5 3.00 0.934 623 1.72 36.8 -120.
## 6 2.29 5 4.11 1.06 2505 4.37 36.6 -119.
## 7 2.80 5 4.95 1.13 1767 3.30 32.8 -116.
## 8 2.7 5 8.86 2.05 41 1.95 33.4 -116.
## 9 1.62 4 3 0.5 8 1.33 35.2 -118.
## 10 2.38 4 1 1 6 3 35.2 -118.
## # ℹ 51 more rows
## # ℹ 1 more variable: target <dbl>
行と列の抽出
最後に行と列を同時に抽出します。
築年数と物件がある地域の世帯年収の中央値を抜き出し、条件を付けて行を抽出しています。
df %>%
select(HouseAge, MedInc) %>%
filter(HouseAge <= 5 & MedInc <= 3)
## # A tibble: 61 × 2
## HouseAge MedInc
## <dbl> <dbl>
## 1 2 2.56
## 2 2 2.88
## 3 3 2.12
## 4 2 2.96
## 5 5 1.81
## 6 5 2.29
## 7 5 2.80
## 8 5 2.7
## 9 4 1.62
## 10 4 2.38
## # ℹ 51 more rows
まとめ
今回はTidyverseと超基礎的なデータハンドリングついて簡単に説明しました。
普通の業務では欠損値の補完やOne-Hotエンコーディングのような特徴量エンジニアリングが前処理として必要なことがほとんどだと思います。 上記のようなこともタイミングを見てお話したいと思います。
次回は実際にオープンソースのデータを用いてデータ可視化の説明をしたいと思います。
ここまでお読みいただき、ありがとうございました!
この記事が少しでも参考になりましたら「スキ」を押していただけると幸いです!
株式会社分析屋について
ホームページはこちら。
noteでの会社紹介記事はこちら。
【データ分析で日本を豊かに】
分析屋はシステム分野・ライフサイエンス分野・マーケティング分野の知見を生かし、多種多様な分野の企業様のデータ分析のご支援をさせていただいております。 「あなたの問題解決をする」をモットーに、お客様の抱える課題にあわせた解析・分析手法を用いて、問題解決へのお手伝いをいたします!
【マーケティング】
マーケティング戦略上の目的に向けて、各種のデータ統合及び加工ならびにPDCAサイクル運用全般を支援や高度なデータ分析技術により複雑な課題解決に向けての分析サービスを提供いたします。
【システム】
アプリケーション開発やデータベース構築、WEBサイト構築、運用保守業務などお客様の問題やご要望に沿ってご支援いたします。
【ライフサイエンス】
機械学習や各種アルゴリズムなどの解析アルゴリズム開発サービスを提供いたします。過去には医療系のバイタルデータを扱った解析が主でしたが、今後はそれらで培った経験・技術を工業など他の分野の企業様の問題解決にも役立てていく方針です。
【SES】
SESサービスも行っております。