2  データ整理と要約

第4章は、データを読み込んで、形を整えて、特徴をつかむためのデータを要約する方法を学びます。 Rは最初から用意されている基本関数だけでも十分強力ですが、外部で開発された様々なパッケージを使うと、さらに便利にデータ操作ができます。

はじめに外部のパッケージをインストールしてRの機能を拡張します。 Rでデータを操作する際に超強力なパッケージ群であるtidyverseを読み込みます。 あとで使うExcelファイルを読み込むためのreadxlパッケージも一緒に読み込みます。

テキストでは、install.packages()関数でパッケージをインストールして、library()関数で読み込んでいますが、ここではpacmanパッケージを使って、必要なパッケージを一括でインストールおよび読み込みをします

まず始めに、pacmanパッケージをインストールします。 この作業はここで1回だけです。以降はこのコードは実行しなくて大丈夫です。

# first time only
install.packages("pacman")

次に、pacmanパッケージのp_load()関数を使って,tidyversereadxlパッケージをインストールおよび読み込みします。 ::という記法はパッケージ名を明示的に指定して関数を使う方法ですが、ここではpacmanパッケージのp_load()関数を指定して使っています。

pacman::p_load(tidyverse, readxl, gt, gtExtras)

一度pacmanパッケージをインストールすれば、あとはpacman::p_load()関数を使うだけで、指定したパッケージがインストールされていなければ自動的にインストールし、読み込みまで行ってくれます。非常に便利です。

作業ディレクトリ内のdataフォルダに入っている2022idpos.csvというデータを読み込みます。 何を読み込んだのか確認するために,基本関数head()でデータの先頭6行を表示させます。

idpos <- readr::read_csv("data/2022idpos.csv", na = ".")
idpos |> head()
# 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()を使います。

idpos |> names()
[1] "id"     "date"   "spent"  "coupon"

データの詳細を確認するため,tidyversedplyrパッケージのglimpse()関数を使います。

idpos |> 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()を使います。

View(idpos)

これで,自分が読み込んだデータがどんなものかを確認できました。

2.0.1 dplyrでデータの要約

tidyversedplyrパッケージを使うと,データの要約が簡単にできます。 ここでは,

  • summarise()関数:データの要約統計量を計算する
  • mutate()関数:新しい変数を作成する
  • filter()関数:条件に合う行を抽出する
  • select()関数:特定の変数を抽出する
  • arrange()関数:データを並び替える
  • rename()関数:変数の名前を変更する

の使い方を学びます。

先ほど読み込んだデータには,

  • id:顧客ID
  • date:購入日
  • spent:購入金額
  • coupon:クーポン利用の有無 (1=利用, 0=未利用)

という4変数に3,000の観測値が含まれています。 このデータをdplyrの各関数を使って,以下のような操作をしてみます。

まず,

  1. select()関数でspentcouponを抽出し,
  2. arrange()関数でspentの降順に並び替え,
  3. mutate()関数でspentの金額を10倍したspent10という新しい変数を作成し,
  4. filter()関数でspent10が10000以上の行を抽出し,
  5. summarise()関数でspent10の平均を計算し,
  6. rename()関数でspent10spent_times10に名前変更する,

という一連の操作を行います(この処理に意味はないです。)

# コード4-11(見本コード)
idpos |>
  select(spent, coupon) |>
  arrange(desc(spent)) |>
  mutate(spent10 = spent * 10) |>
  filter(spent10 >= 10000) |>
  summarise(mean_spent10 = mean(spent10)) |>
  rename(spent_times10 = mean_spent10)
# A tibble: 1 × 1
  spent_times10
          <dbl>
1        82391.

すると,spentを10倍して,1万円以上の買い物の平均値を計算した結果がspent_times10として表示されます。

このように加工したデータをcsvファイルとして保存するには、readrパッケージのwrite_csv()関数を使います。 write_csv()関数は、第1引数に保存したいデータフレームを指定し、第2引数に保存先のファイルパスを指定します。

readr::write_csv(idpos, path = "data/new_data.csv")  

2.0.2 企業データの処理

MS Excelのファイルを読み込むこともできます。 ここでは,readxlパッケージのread_xlsx()関数を使って,企業データを読み込みます。

firmdata <- readxl::read_xlsx("data/MktRes_firmdata.xlsx")
firmdata |> glimpse()
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_checksales変数について、代表値を計算してみます。 ここでは、dplyrパッケージのsummarize()関数を使って、平均、中央値、分散、標準偏差を一度に計算します。

firm2018_check |> 
  summarize(
    mean_sales   = mean(sales),
    median_sales = median(sales),
    var_sales    = var(sales),
    sd_sales     = sd(sales)
  )
# A tibble: 1 × 4
  mean_sales median_sales var_sales sd_sales
       <dbl>        <dbl>     <dbl>    <dbl>
1   1242425.       526675   3.69e12 1921062.

2.2 カテゴリ変数の要約

つぎに、データ上は数値や文字列として記録されていても、その数値や文字列がカテゴリーを表す名義尺度(nominal scale)である場合を考えましょう。

例えば、firmdataの中の産業分類を表すind_en変数は、文字列で表現されていますが、その文字列はある観測値が所属するカテゴリーを表しています。 fyear変数は数値ですが、これは年度を表すカテゴリー変数です。

カテゴリ内の観測値の頻度を確認するには基本関数table()を使いますが、ここでは、dplyr::count()関数を使って、ind_en変数の各カテゴリーの頻度を計算し、gtパッケージで表形式で表示します。

firm2018 <- firmdata |>
  filter(fyear == 2018) # 2018年のデータを抽出
firm2018 |>
  dplyr::count(ind_en, name = "企業数") |>
  gt::gt() |>
  gtExtras::gt_theme_538()
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()関数で見やすく変形しています。

firm2018 <- firm2018 |>
  mutate(
    ad_dummy = if_else(adint > median(adint, na.rm = TRUE), 1L, 0L)
    )
firm2018 %>%
  count(ind_en, ad_dummy) |>
  tidyr::pivot_wider(names_from = ad_dummy, values_from = n, values_fill = 0) |>
  gt::gt() |>
  gtExtras::gt_theme_538()
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()関数で集計します。

# コード4-39
firm2018 |>
  group_by(ind_en) |>
  summarize(
      obs = n(),
      sales_m = mean(sales),
      sales_sd = sd(sales),
      adint_m = mean(adint),
      adint_sd = sd(adint)
      ) |>
  gt::gt() |>
  gt::fmt_number(
    columns = c(sales_m, sales_sd, adint_m, adint_sd),
    decimals = 2
  ) |>
  gtExtras::gt_theme_538()
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
Back to top