5 利益分布アプローチによる利益マネジメントの実態分析

2024-05-10


1 join系関数(教科書コラム5.2)

1.1 二つのデータフレームの結合

  • 実証会計・ファイナンスでは,財務データと株式データの結合を典型例として,二つのデータフレームを結合する場面にしばしば遭遇する.
  • dplyrにはfull_join()関数を始め,結合を行うために用いるjoin系関数が用意されている.
  • 以下では,株価データAとDPSデータBの結合を通じて,それぞれのjoin系関数の返り値を確認していこう.
Code
# tidyverseの読み込み
pacman::p_load(tidyverse) 

# 例に用いる二つのデータフレームを作成
A = tibble(firm_ID = c(1, 2),
           stock_price = c(120, 500)) # 株価データが格納されたAを作成
B = tibble(firm_ID = c(1, 3),
           DPS = c(5, 10)) # DPSデータが格納されたBを作成
  • 上のコードでは,tibble()関数を使って二つのデータフレームを手動で作成している.

1.2 完全外部結合full_join()関数

1.3 内部結合inner_join()関数

Code
# inner_join()関数による欠損値の処理
A %>% inner_join(B, by = "firm_ID") 
# A tibble: 1 × 3
  firm_ID stock_price   DPS
    <dbl>       <dbl> <dbl>
1       1         120     5

1.4 左結合left_join()関数

Code
# left_join()関数による欠損値の処理
A %>% left_join(B, by = "firm_ID") 
# A tibble: 2 × 3
  firm_ID stock_price   DPS
    <dbl>       <dbl> <dbl>
1       1         120     5
2       2         500    NA

2 分布の不連続性(教科書コラム5.3)

2.1 成績評価チート

2.2 その他の例

  • 大相撲の勝ち星
  • 食べログ3.6問題

3 分析の準備

3.1 財務データの読み込み

  • 会計利益の分布に果たして歪みがあるかを検証を進めるため,まずは分析に利用する財務データを読み込むことから始めよう.

目標

  • 目標1: simulation_dataフォルダにあるfinancial_data.csvをデータフレームfinancial_dataとして読み込んでみよう.
  • 目標2: その後,head()関数により,financial_dataの冒頭6行を表示し,どのようなデータが収録されているか確認してみよう.

3.2 フォルダ構造

  • 現在地がcodesである場合,読み込みたいfinancial_data.csvが格納されているsimulation_dataフォルダにアクセスするには,一個上の階層に一度戻る必要があり,それは..により実現可能である.
  • あとは,simulation_dataフォルダに移動 (/simulation_data)し,/financial_data.csvで目的のファイルにアクセス可能である.

3.3 目標を達成するためのコード例

Code
# 財務データの読み込み
financial_data <- read_csv("../simulation_data/financial_data.csv") 
Rows: 22855 Columns: 7
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
dbl  (6): firm_ID, year, macc, X, TA, CFO
date (1): fiscal_year_end

ℹ 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.
Code
# head()関数を用いて冒頭6行の結果のみ表示
head(financial_data) 
# A tibble: 6 × 7
  firm_ID fiscal_year_end  year  macc     X     TA   CFO
    <dbl> <date>          <dbl> <dbl> <dbl>  <dbl> <dbl>
1       8 2004-07-01       2004    12  3125 204270  3319
2       8 2005-07-01       2005    12  3973 197259  4571
3       8 2006-07-01       2006    12  4117 203422  3820
4       8 2007-07-01       2007    12  4277 217386  2367
5       8 2008-03-01       2008     8  4743 218038  5591
6       8 2009-03-01       2009    12  5445 222643  3604
  • fiscal_year_end: 決算年月 (YYYY-MM-DD形式)
  • macc: 決算月数
  • X: 当期純利益(百万円)
  • TA: 資産合計(百万円)
  • CFO: 営業活動によるキャッシュフロー(百万円)

3.4 当期純利益Xを基準化

  • 分析にあたっては,当期純利益Xそのものの分布ではなく,各企業の規模を統制して各観測値を横並びで比較可能にしたScaled Earnings (SE)の分布を考えよう.

    \[ \underbrace{SE_{i,t}}_{\textbf{企業$i$の年度$t$のScaled Earnings}} = \frac{\overbrace{X_{i,t}}^{\textbf{企業$i$の年度$t$の当期純利益}}}{\text{各企業の規模の代理変数}} \]

  • この分析では,簡便的に各企業の発行する株式の時価総額 (株価 \(\times\) 発行済株式数)を規模の代理変数と捉え,分析を進めて行こう.

3.5 株式データの読み込み

  • Scaled Earningsのデフレータとなる時価総額を得るため,simulation_dataフォルダにあるstock_data.csvstock_dataとして読み込んでみよう.
Code
# 株式データの読み込み
stock_data <- read_csv("../simulation_data/stock_data.csv") 
Rows: 22852 Columns: 4
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
dbl (4): firm_ID, year, stock_price, shares_outstanding

ℹ 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.
Code
head(stock_data)
# A tibble: 6 × 4
  firm_ID  year stock_price shares_outstanding
    <dbl> <dbl>       <dbl>              <dbl>
1       8  2007         412          117800776
2       8  2008         514          117800776
3       8  2009         743          117800776
4       8  2010         718          117800776
5       8  2011        1015          117800776
6       8  2012         958          117800776
  • stock_price: 株価
  • shares_outstanding: 発行済株式数

3.6 stock_dataに時価総額ME列の追加

Code
# mutate()関数を使ってME列の追加
stock_data <- stock_data %>% 
  mutate(ME = (stock_price * shares_outstanding) / 1e6) # MEを百万円単位で計算 

3.7 単位確認の重要性

  • 先のコードで登場した1e6は科学技術分野で一般的に用いられる科学的表記と呼ばれる表記法(教科書157頁)であり,\(1 \times 10^6 (= 1,000,000)\)と等しい.
  • 会計・ファイナンス研究で頻用される財務データは,一般的に百万円単位でデータが収録されているため,MEを計算する際,財務データと単位を揃えることを目的として1e6で除している.
  • (stock_price * shares_outstanding) / 1000000とはしないこと!

3.8 財務データと株式データの結合

3.9 パイプ演算子を繋げてSE列も追加

Code
# 財務データと株式データの結合し,SE列も追加
financial_data <- financial_data %>% 
  left_join(stock_data, by = c("firm_ID", "year")) %>% 
  mutate(SE = if_else(macc == 12, X / ME, NA)) 
    # 決算月数が12ヶ月ではないものは欠損値に
  • 上のコードでは,dplyrif_else()関数を使って決算月数が12ヶ月の場合はSEを計算し,そうでなければ欠損値NAになるように工夫している.

4 利益分布アプローチによる分析

4.1 サンプルの確定

  • データの前処理が完了すれば,分析対象を確定させ,新たなデータフレームsampleを利用して分析を進めて行こう.

目標

  • ここでは,SEが欠損値となっている観測値を除外(\(=\) SEが計算可能な観測値のみを抽出)し,分析対象となるデータフレームをsampleとして定義してみよう.

4.2 目標を達成するためのコード例

Code
# 分析に利用する観測値のみのデータフレームsampleを作成
sample <- financial_data %>% 
  drop_na(SE) # SEが欠損値のものを削除 
  • drop_na()の使い方は,ここを参照.

4.3 集計作業の実践 — summarize()関数を使ってみよう

目標

  • データフレームsampleを用いて,年度ごとにSEの平均値を算出し,それをMean列と名付けよう.
  • こうして出来たyear列とMean列から成るデータフレームをtable_1として定義しよう.

4.4 目標を達成するためのコード例

Code
# 年度ごとにSEの平均値を計算
table_1 <- sample %>% # 集計結果をtable_1として定義
  group_by(year) %>% # 年度でグループ化
  summarize(Mean = mean(SE)) # 平均値をMeanと命名

head(table_1) # 内容の確認
# A tibble: 6 × 2
   year    Mean
  <dbl>   <dbl>
1  2005  0.0144
2  2006  0.0126
3  2007 -0.0109
4  2008  0.0337
5  2009  0.0749
6  2010  0.0378
  • 2行目: 以下の処理により作成されるデータフレームをtable_1として定義.
  • 3行目: 年度yearでグループ化.
  • 4行目: summarize関数を適用し,SEの平均値をmean(SE)により計算し,それをMeanと命名.

4.5 更に一歩進んで,年度ごとに基本統計量を集計

Code
# 年度ごとに基本統計量を集計
table_1 <- sample %>% 
  group_by(year) %>% # 年度毎にグループ化
  summarize(N = n(), # 観測値数
            Mean = mean(SE), # 平均値
            SD = sd(SE), # 標準偏差
            Q1 = quantile(SE, 0.25), # 第1四分位
            Median = median(SE), # 中央値
            Q3 = quantile(SE ,0.75)) # 第3四分位

4.6 quantile()関数の使い方

  • 分位点を求めるにはquantile()関数を用いる.この関数は第一引数に入力データ(数値ベクトル),第二引数に求めたい分位点の値をパーセントでなく小数表示で代入する(教科書185頁).
  • 例えば,SEの第1四分位を求めたいならば,quantile(SE, 0.25)とすれば良い.

4.7 出力結果のアレンジ — 桁数の調整

Code
# 年度ごとに基本統計量を集計(平均値のみ桁数調整)
table_1 <- sample %>% 
  group_by(year) %>% # 年度毎にグループ化
  summarize(N = n(), # 観測値数
            Mean = round(mean(SE), 3), # 平均値
            SD = sd(SE), # 標準偏差
            Q1 = quantile(SE, 0.25), # 第1四分位
            Median = median(SE), # 中央値
            Q3 = quantile(SE, 0.75)) # 第3四分位

4.8 write_csv()関数を使ったデータフレームの出力

Code
# table_1の結果を出力
write_csv(table_1, "../tables/table_1.csv")
  • readrwrite_csv()関数を使って,先に作成したデータフレームtable_1tablesフォルダに出力しよう.
  • write_csv()関数の第一引数はデータフレーム名を入力し,第二引数でファイル名を指定する(教科書184頁)
  • 現在地がcodesであることを前提にすれば,出力したいtablesフォルダへ移動するには,一個上の階層に一度戻る必要があり,それは..により実現可能である.
  • あとは,tablesフォルダに移動 (/tables)し,table_1.csvという名前で出力 (/table_1.csv)すれば良いので,write_csv()関数の第二引数は,"../tables/table_1.csv"と指定する.

4.9 Scaled Earnings (SE)のヒストグラム

  • 締めくくりとして,ggplot2を利用し,Scaled Earnings (SE)のヒストグラムを描画し,利益マネジメントの実態を明らかにしていこう.

目標

  • データフレームsample内のSEについて,-0.2から0.2までのSEのヒストグラムを描画しよう.
    • ビン幅は0.005とする.
    • \(x\)軸のラベルはEarnings Interval\(y\)軸のラベルはFrequencyとする.
    • (余力がある人は)Burgstahler and Dichev (1997)同様,\(x = 0\)の破線を引き,ベンチマークが一目瞭然で分かるように調整.

4.10 オリジナル論文のヒストグラム (Fig. 3)

4.11 SEのヒストグラムの描画

Code
# SEのヒストグラムの描画
ggplot(sample) +
  geom_histogram(aes(x = SE),
                 breaks = seq(-0.2, 0.2, 0.005)) +
  labs(x = "Earnings Interval", y = "Frequency") + 
  scale_y_continuous(expand = c(0, 0)) +
  theme_classic()

4.12 \(x = 0\)を表す破線の追加

  • geom_vline()関数は,vertical(垂直)に直線を引く.xintercept引数で値を指定し,またlinetype引数で適当な線種を指定する(dashedの他にも,dottedsolidなど多様にオプションが用意されている).
Code
# SEのヒストグラムの描画
ggplot(sample) +
  geom_histogram(aes(x = SE),
                 breaks = seq(-0.2, 0.2, 0.005)) +
  labs(x = "Earnings Interval", y = "Frequency") + 
  scale_y_continuous(expand = c(0, 0)) +
  geom_vline(xintercept = 0, linetype = "dashed") + 
  theme_classic()

4.13 完成したグラフをPNG形式で出力

目標

  • こうして描画されたヒストグラムをfiguresフォルダにfigure_1.pngの名前を付して出力しよう.
  • (ヒント1) 直前に出力したグラフを保存したい場合,ggsave()関数を利用する.第一引数にはファイル名を指定しよう.
  • (ヒント2) figuresフォルダのある階層を意識して第一引数を指定しよう.

4.14 目標を達成するためのコード例

Code
# ggsave()関数を使ってSEのヒストグラムをfigure_1.pngとして出力
ggsave("../figures/figure_1.png") 
Saving 7 x 5 in image
  • 必要に応じてグラフのサイズなどの細部を調整したい場合は,別途引数により調整すれば良い.
Code
# 出力サイズを明示的に指定して出力
ggsave("../figures/figure_1.png", 
       width = 20, # 幅を指定
       height = 10, # 高さを指定
       units = "cm") # 幅と高さの単位を指定

5 練習問題

5.1 損失回避期間と利益マネジメントのインセンティブ

  • Burgstahler and Dichev (1997)や首藤 (2010)1では,過去において損失回避期間が長い企業の経営者ほど,損失を回避するインセンティブが高まり,分布の歪みがより顕著に現れることが明らかにされている.
  • ここでは,その検証結果の再現を試みる一環として,一期前のSEが正で少なくとも過去1期は損失回避できた企業群だけに絞り,同様のヒストグラムを描画する方法を実践していこう.

5.2 前年度損失企業のみを抽出して描画

Exercise

  • 目標1: 最初にmutate()関数とlag()関数を組み合わせて,一期前のSEを表すlagged_SEをデータフレームsampleに追加しよう.
    • (注意点) 一行前が必ずしも一期前とは限らないため,if_else()関数を用いてyearlag(year)との関係を示す条件式を予め指定し,lagged_SEの計算式を工夫するのが理想的である.
  • 目標2: lagged_SE列が追加できれば,前年度損失回避企業をfilter()関数により抽出し,パイプ演算子を使って抽出データをggplot()関数に引き渡して一気にヒストグラムの可視化まで行ってみよう.