見出し画像

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サービスも行っております。


この記事が参加している募集