2 データ整理と要約
第4章は、データを読み込んで、形を整えて、特徴をつかむためのデータを要約する方法を学びます。 Rは最初から用意されている基本関数だけでも十分強力ですが、外部で開発された様々なパッケージを使うと、さらに便利にデータ操作ができます。
はじめに外部のパッケージをインストールしてRの機能を拡張します。 Rでデータを操作する際に超強力なパッケージ群であるtidyverseを読み込みます。 あとで使うExcelファイルを読み込むためのreadxlパッケージも一緒に読み込みます。
テキストでは、install.packages()関数でパッケージをインストールして、library()関数で読み込んでいますが、ここではpacmanパッケージを使って、必要なパッケージを一括でインストールおよび読み込みをします。
まず始めに、pacmanパッケージをインストールします。 この作業はここで1回だけです。以降はこのコードは実行しなくて大丈夫です。
次に、pacmanパッケージのp_load()関数を使って,tidyverseとreadxlパッケージをインストールおよび読み込みします。 ::という記法はパッケージ名を明示的に指定して関数を使う方法ですが、ここではpacmanパッケージのp_load()関数を指定して使っています。
一度pacmanパッケージをインストールすれば、あとはpacman::p_load()関数を使うだけで、指定したパッケージがインストールされていなければ自動的にインストールし、読み込みまで行ってくれます。非常に便利です。
作業ディレクトリ内のdataフォルダに入っている2022idpos.csvというデータを読み込みます。 何を読み込んだのか確認するために,基本関数head()でデータの先頭6行を表示させます。
# A tibble: 6 × 4
id date spent coupon
<dbl> <chr> <dbl> <dbl>
1 12 2019/9/25 14326 1
2 32 2019/9/10 10232 1
3 30 2019/9/9 6881 1
4 29 2019/9/4 6365 0
5 46 2019/9/10 7595 1
6 44 2019/9/14 7858 0
readrパッケージのread_csv()関数は,CSV形式のデータを読み込むための関数です。na = "."という引数は,データ内の.を欠損値として扱うことを指定しています。
読み込んだデータに含まれている変数の名前を確認するため,基本関数names()を使います。
データの詳細を確認するため,tidyverseのdplyrパッケージのglimpse()関数を使います。
Rows: 3,000
Columns: 4
$ id <dbl> 12, 32, 30, 29, 46, 44, 44, 32, 3, 34, 36, 3, 42, 18, 38, 4, 19…
$ date <chr> "2019/9/25", "2019/9/10", "2019/9/9", "2019/9/4", "2019/9/10", …
$ spent <dbl> 14326, 10232, 6881, 6365, 7595, 7858, 9405, 1821, 8375, 1828, 6…
$ coupon <dbl> 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, …
Excelのようにデータを表示させたいなら,基本関数View()を使います。
これで,自分が読み込んだデータがどんなものかを確認できました。
2.0.1 dplyrでデータの要約
tidyverseのdplyrパッケージを使うと,データの要約が簡単にできます。 ここでは,
-
summarise()関数:データの要約統計量を計算する -
mutate()関数:新しい変数を作成する -
filter()関数:条件に合う行を抽出する -
select()関数:特定の変数を抽出する -
arrange()関数:データを並び替える -
rename()関数:変数の名前を変更する
の使い方を学びます。
先ほど読み込んだデータには,
- id:顧客ID
- date:購入日
- spent:購入金額
- coupon:クーポン利用の有無 (1=利用, 0=未利用)
という4変数に3,000の観測値が含まれています。 このデータをdplyrの各関数を使って,以下のような操作をしてみます。
まず,
-
select()関数でspentとcouponを抽出し, -
arrange()関数でspentの降順に並び替え, -
mutate()関数でspentの金額を10倍したspent10という新しい変数を作成し, -
filter()関数でspent10が10000以上の行を抽出し, -
summarise()関数でspent10の平均を計算し, -
rename()関数でspent10をspent_times10に名前変更する,
という一連の操作を行います(この処理に意味はないです。)
# A tibble: 1 × 1
spent_times10
<dbl>
1 82391.
すると,spentを10倍して,1万円以上の買い物の平均値を計算した結果がspent_times10として表示されます。
このように加工したデータをcsvファイルとして保存するには、readrパッケージのwrite_csv()関数を使います。 write_csv()関数は、第1引数に保存したいデータフレームを指定し、第2引数に保存先のファイルパスを指定します。
2.0.2 企業データの処理
MS Excelのファイルを読み込むこともできます。 ここでは,readxlパッケージのread_xlsx()関数を使って,企業データを読み込みます。
Rows: 1,431
Columns: 31
$ fyear <dbl> 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,…
$ legalname <chr> "ハウステンボス株式会社", "ハウステンボス株式会社", "ハウステンボス株式会社", "ハ…
$ ind_en <chr> "Miscellaneous Services", "Miscellaneous Servic…
$ parent <chr> "エイチ・アイ・エス", "エイチ・アイ・エス", "エイチ・アイ・エス", "エイチ・アイ・…
$ fiscal_month <chr> "2011/10", "2012/10", "2013/10", "2014/10", "20…
$ current_liability <dbl> 65509, 76206, 85459, 98384, 122993, 102805, 131…
$ ltloans <dbl> 0, 4781, 23411, 22780, 14319, 77042, 101603, 11…
$ total_liability <dbl> 73428, 96734, 125233, 179036, 194254, 237245, 3…
$ current_assets <dbl> 102810, 111697, 137515, 196789, 212979, 233531,…
$ ppent <dbl> 12383, 40554, 45511, 48704, 60761, 62291, 83001…
$ total_assets <dbl> 139018, 173497, 215913, 281332, 308245, 332385,…
$ net_assets_per_capital <dbl> 65589, 76763, 90680, 102295, 113990, 95139, 111…
$ sales <dbl> 380805, 431483, 479478, 523246, 537456, 523705,…
$ sga <dbl> 61158, 65654, 69953, 80033, 88284, 90769, 98822…
$ operating_profit <dbl> 9407, 11316, 11843, 15906, 19970, 14274, 15915,…
$ net_profit <dbl> 8958, 10881, 11190, 11271, 14025, 1305, 15835, …
$ pnet_profit <dbl> 8300, 9331, 8903, 9050, 10890, 267, 13259, 1106…
$ re <dbl> 47658, 55966, 63664, 71612, 82150, 80988, 92731…
$ adv <dbl> 8565, 9691, 10694, 11665, 12969, 12647, 12371, …
$ labor_cost <dbl> 30624, 31743, 31648, 37240, 40402, 41505, 46583…
$ rd <dbl> 0, 0, 0, 0, 0, 0, 0, 176, 0, 0, 0, 0, 0, 0, 0, …
$ other_sg <dbl> 14962, 17078, 19720, 21648, 24043, 24710, 26067…
$ emp <dbl> 6265, 8310, 9026, 9652, 10143, 10845, 13510, 13…
$ temp <dbl> 1751, 2470, 2750, 3071, 3469, 3535, 3422, 3179,…
$ tempratio <dbl> 0.2184381, 0.2291280, 0.2335258, 0.2413739, 0.2…
$ indgrowth <dbl> -0.0038649153, 0.0780674898, -0.0737512474, 0.0…
$ adint <dbl> 0.02249183, 0.02245975, 0.02230342, 0.02229353,…
$ rdint <dbl> 0.0000000000, 0.0000000000, 0.0000000000, 0.000…
$ mkexp <dbl> 0.16060188, 0.15215895, 0.14589408, 0.15295482,…
$ op <dbl> 0.02470293, 0.02622583, 0.02469978, 0.03039870,…
$ roa <dbl> 0.0597044987, 0.0537819098, 0.0412342008, 0.032…
31変数1,431観測値からならなるデータが読み込まれました。 必要な変数が変わることもあるかと思うので、先に必要な変数名をベクトルvars_listとして保存しておきます。
必要な観測値と変数だけを抽出して、新しい変数を作成し、並び替えて、新しいオブジェクトfirm2018_checkに格納します。
# 必要な変数のリスト
vars_list <- c("legalname", "fyear", "sales", "labor_cost", "emp", "temp")
# コード4-28
firm2018_check <- firmdata |>
filter(fyear == 2018) |> # 2018年のデータを抽出
select(all_of(vars_list)) |> # 必要な変数を選択
mutate(wage = labor_cost / (temp + emp), na.rm = TRUE) |> # 新しい変数wageを作成
# temp + empが0でないチェックが必要かも
arrange(desc(wage)) # wageの降順に並び替え
head(firm2018_check, n = 10) # 先頭10行を表示# A tibble: 10 × 8
legalname fyear sales labor_cost emp temp wage na.rm
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <lgl>
1 株式会社リクルート 2018 2310756 388583 45856 2449 8.04 TRUE
2 株式会社 大丸松坂屋百貨店 2018 459840 62692 6695 3581 6.10 TRUE
3 株式会社 帝国ホテル 2018 58426 17307 1940 998 5.89 TRUE
4 株式会社 髙島屋 2018 912848 83779 7761 8849 5.04 TRUE
5 株式会社コメリ 2018 346862 43991 4646 4777 4.67 TRUE
6 株式会社オートバックスセブン 2018 213840 22139 4171 747 4.50 TRUE
7 株式会社ロイヤルホテル 2018 40884 13115 2049 894 4.46 TRUE
8 オルビス株式会社 2018 248574 28555 4181 2330 4.39 TRUE
9 株式会社ファンケル 2018 122496 15103 1381 2213 4.20 TRUE
10 近畿日本ツーリスト株式会社 2018 411821 38186 6956 2189 4.18 TRUE
all_of()関数は、select()関数内で使用され、変数名のベクトルを指定して、その変数を選択するために使います。これにより、変数名が動的に指定でき、コードの柔軟性が向上します。
2.1 4 データの要約
大規模データをあつかうとき、データそのものを眺めていても特徴をつかむことは難しいので、そのデータを特徴付ける代表値を計算して、そこからデータの特徴をつかみます。 主要な代表値として、平均値(mean)や中央値(median)、散らばり具合を示す分散(variance)や標準偏差(standard deviation)があります。 それぞれ、
を使って計算します。
先ほどのデータfirm2018_checkのsales変数について、代表値を計算してみます。 ここでは、dplyrパッケージのsummarize()関数を使って、平均、中央値、分散、標準偏差を一度に計算します。
2.2 カテゴリ変数の要約
つぎに、データ上は数値や文字列として記録されていても、その数値や文字列がカテゴリーを表す名義尺度(nominal scale)である場合を考えましょう。
例えば、firmdataの中の産業分類を表すind_en変数は、文字列で表現されていますが、その文字列はある観測値が所属するカテゴリーを表しています。 fyear変数は数値ですが、これは年度を表すカテゴリー変数です。
カテゴリ内の観測値の頻度を確認するには基本関数table()を使いますが、ここでは、dplyr::count()関数を使って、ind_en変数の各カテゴリーの頻度を計算し、gtパッケージで表形式で表示します。
| ind_en | 企業数 |
|---|---|
| Air Transportation | 8 |
| Amusement Services | 4 |
| Bakery Products | 1 |
| Communication Services | 2 |
| Cosmetics & Toilet Goods | 3 |
| Department Stores | 7 |
| Foods, NEC | 1 |
| Home & Pre-Fabs | 2 |
| Hotels | 5 |
| Miscellaneous Services | 27 |
| Miscellaneous Wholesales | 2 |
| Motor Vehicles | 4 |
| Musical Instrument | 1 |
| Railroad (Major) | 27 |
| Railroad (Minor) | 2 |
| Real Estate - Sales | 1 |
| Retail Stores, NEC | 35 |
| Supermarket Chains | 14 |
| Trucking | 1 |
教科書のようにtable()関数を使うと簡単に頻度表が作れますが、オブジェクトの型がtable型になってしまい、後で他の処理に使いにくくなるので、data.frame型のまま処理できるdplyr::count()関数を使いました。
広告費を表すadient変数の観測値が全企業の中央値より大きいか否か、でダミー変数ad_dummyを作成し、ind_en変数とad_dummy変数のクロス集計表を作成します。
教科書を先取りしますが、if_else()関数を使ってダミー変数を作成し、count()関数でクロス集計表を作成し、gtパッケージで表形式で表示します。 tidyrパッケージのpivot_wider()関数を使って、クロス集計表が見やすくなるように、縦は産業名、横は広告費が中央値より大きいか否か、で表示するように変形しています。
table()関数を使うと簡単にクロス集計表が作れますが、あえてdata.frame型のまま処理できるdplyr::count()関数を使っています。そのため、データが矩形データのまま扱えるので、後で他の処理に使いやすくなるものの、表にすると見にくくなるので、tidyr::pivot_wider()関数で見やすく変形しています。
| ind_en | 0 | 1 |
|---|---|---|
| Air Transportation | 4 | 4 |
| Amusement Services | 4 | 0 |
| Bakery Products | 0 | 1 |
| Communication Services | 1 | 1 |
| Cosmetics & Toilet Goods | 0 | 3 |
| Department Stores | 0 | 7 |
| Foods, NEC | 0 | 1 |
| Home & Pre-Fabs | 0 | 2 |
| Hotels | 5 | 0 |
| Miscellaneous Services | 17 | 10 |
| Miscellaneous Wholesales | 1 | 1 |
| Motor Vehicles | 0 | 4 |
| Musical Instrument | 0 | 1 |
| Railroad (Major) | 27 | 0 |
| Railroad (Minor) | 2 | 0 |
| Real Estate - Sales | 0 | 1 |
| Retail Stores, NEC | 11 | 24 |
| Supermarket Chains | 2 | 12 |
| Trucking | 1 | 0 |
次に、カテゴリ変数ごとに処理を行いたい場合、例えば、産業分類ごとに売上高の平均値や標準偏差を計算したい場合は、group_by()関数を使って、カテゴリ変数でグループ化してから、summarize()関数で集計します。
| ind_en | obs | sales_m | sales_sd | adint_m | adint_sd |
|---|---|---|---|---|---|
| Air Transportation | 8 | 1,772,786.50 | 305,239.60 | 0.00 | 0.00 |
| Amusement Services | 4 | 298,137.50 | 263,017.35 | 0.00 | 0.00 |
| Bakery Products | 1 | 1,059,442.00 | NA | 0.01 | NA |
| Communication Services | 2 | 547,087.50 | 172,735.58 | 0.02 | 0.03 |
| Cosmetics & Toilet Goods | 3 | 140,669.33 | 100,063.48 | 0.11 | 0.05 |
| Department Stores | 7 | 843,248.29 | 348,818.99 | 0.02 | 0.01 |
| Foods, NEC | 1 | 504,153.00 | NA | 0.02 | NA |
| Home & Pre-Fabs | 2 | 4,143,505.00 | 0.00 | 0.01 | 0.00 |
| Hotels | 5 | 62,134.80 | 58,060.28 | 0.00 | 0.00 |
| Miscellaneous Services | 27 | 311,867.22 | 456,036.57 | 0.01 | 0.02 |
| Miscellaneous Wholesales | 2 | 176,520.00 | 52,778.45 | 0.02 | 0.03 |
| Motor Vehicles | 4 | 5,279,121.75 | 4,233,187.72 | 0.03 | 0.00 |
| Musical Instrument | 1 | 434,373.00 | NA | 0.04 | NA |
| Railroad (Major) | 27 | 1,302,920.52 | 1,037,834.05 | 0.00 | 0.00 |
| Railroad (Minor) | 2 | 260,502.00 | 0.00 | 0.00 | 0.00 |
| Real Estate - Sales | 1 | 1,861,195.00 | NA | 0.01 | NA |
| Retail Stores, NEC | 35 | 571,019.17 | 547,246.68 | 0.02 | 0.03 |
| Supermarket Chains | 14 | 4,335,164.07 | 3,511,346.59 | 0.01 | 0.01 |
| Trucking | 1 | 1,118,094.00 | NA | 0.00 | NA |