Deep Karmaning

技術系の話から日常のことまで色々と書きます

限界分析(Marginal Analysis)を応用した意思決定の試み

概要

先日実務で経済学の限界分析の考え方を応用した意思決定サポートツールを作る機会があり、それ以来経済学への興味が出てきましたし、実務における社会科学の概念の活用可能性を強く感じました。

そこで今回は自分の理解を改めて振り返るという気持ちも込めて、限界分析という経済学の概念を用いた意思決定の方法を考えてみたいと思います。もちろん実務でやった内容をそのまま書くと問題になると思いますので、適当な例をもとに説明を試みたいと思います。そしてRを用いて問題をときながら活用方法を検討したいと思います。

また本記事の注意点としてはアカデミック的な正しさの保証がなく、経済学の教育を大学で受けたわけではない人間が書いていますので、本記事の内容の応用には気をつけてください。

主に参考にした書籍は以下になります。

ミクロ経済学の力

ミクロ経済学の力

限界分析に関して

限界分析(Marginal Analysis)とは何か

そもそも限界分析(Marginal Analysis)とは何なのでしょうか。本記事で言う限界分析というのは、経済学の概念の限界を分析するという意味を指しています。

Wikipediaでは限界分析を、

これは経済現象に直接の影響を及ぼす要素は、限界値であるとして、限界値に注意を払った上で経済分析を行うという方法である。経済変数の絶対値や平均値などの計量分析だけでなく、限界値にこだわる考え方ともいえる。限界分析に基づいた経済学においては、生産者は利潤最大を追求し、消費者は効用最大を追及する、という立場になる。そこから限界収入と限界費用というのは等しいであろう、といった限界量の関係が導き出されるという論理である。

というように説明しています(https://ja.wikipedia.org/wiki/%E9%99%90%E7%95%8C%E5%88%86%E6%9E%90)。

これだけだと何を言っているかあまりわかりませんね。この限界値に注意を払うというのは、つまるところ微妙に変化したところの変化を見るということになると思います。

例えばある飲み会における状況を想定してくだい。とりの唐揚げを3個食べた後に4個目を食べたときの幸せを感じる量(効用)と、10個食べた後に11個目を食べたときの幸せを感じる量は異なりますよね*1

この状況を図で表現すると以下のようになります。

f:id:rf00:20181021115003p:plain

横軸がとりのから揚げを食べた数で、縦軸に幸せ感(効用)としています。

図では3個目と4個目、10個目と11個目の幸せ感の増加量をMU_1とMU_2としました。この図を見ると3個目から4個目のMU_1のほうが幸せ感の増加量が大きいのがわかります。

つまりとりの唐揚げを食べた数が少ないときは追加で1個食べたときの幸せ感の増加(MU_1)のほうが、既にある程度食べた段階で追加で1個食べたときの幸せの増加(MU_2)より大きいということです*2

この追加で1個消費を増やしたときの増加の幸せを(効用)を限界効用といったりします。また消費が増えるほど限界効用が減っていくことは経済学の用語で限界効用逓減の法則と呼ぶようです。

これらのMU_1やMU_2がどうなっているのか考えるのが、経済学的な意味での限界分析と言えると思います。

ちなみに今回の図ではとりのからあげ1個増やした時の幸せの増加量という形で限界効用を考えましたが、別の考え方として3個のところで微分をすると傾きがわかるので、この微分した値が限界効用として求めることもできます。イメージとしては以下の図です。

f:id:rf00:20181021115033p:plain

MU_1のところが傾きが急になっており増加量が多いことがわかります。

限界分析を応用した意思決定の試み

ではここまで見てきた限界分析はどのように使えるのでしょうか。例をもとにRでコードを書きながら考えてみたいと思います。

シチュエーション

ある家族の両親があることを考えています。この両親には双子の子供がいます。AちゃんとBちゃんとしましょう。双子は食べ盛りの年齢でとりの唐揚げが大好物です。

両親はいつも双子が夕食で食べたとりのから揚げの量と双子の満足感を観察したところ、二人共とりの唐揚げが好きではあるのですが、食べた量に対応して感じる満足感は異なるようでした。また二人の満足度はそれぞれが食べた量にのみ依存するようで、Aちゃんがたくさん食べた時に、Bちゃんはずるいと感じて満足度が下がってしまうということは起きないようです*3

その観察を記録した結果が以下のような図のようになったとします。

f:id:rf00:20181021161322p:plain

横軸は1日に食べたからあげのグラム数*4、縦軸がそこから得られた一日の食事満足感です。赤い点がAちゃんの結果青い点がBちゃんとなります。

二人共とりの唐揚げを食べれば食べるほど満足感の増加量は減るようですが増加の仕方はAちゃんとBちゃんで異なるのが見えています。

両親はこういった状況から、双子の満足度の合計を最大化するためにとりの唐揚げをどのに配分すると良いのか考えたいと思いました。

ちなみに今回のデータ生成と上記可視化はRで以下のように行いました。

#generate data
set.seed(123)

#A
a_1 <- 2
b_1 <- 0.13

#B
a_2 <- 2
b_2 <- 0.10

#observation gram
x <- sample(seq(1, 500, 1), 100)

#convert data frame
df_a <- data.frame(gram = x, utility = a_1*x^b_1 + rnorm(length(x), 0, 0.25), person = "A")
df_b <- data.frame(gram = x, utility = a_2*x^b_2 + rnorm(length(x), 0, 0.25), person = "B")

#union data 
df <- rbind(df_a, df_b)

#write data
write.csv(df, "data.csv", row.names = FALSE) #書き出して再利用可能にする

library(ggplot2)

df <- read.csv("data.csv")
ggplot(df, aes(x = gram, y = utility)) + geom_point(aes(colour = person))

問題

ある日両親はとりの唐揚げを1800g作りました。そこでこの1800gをAちゃんとBちゃんの満足度の合計が最大になるように配分したいと思います。どのように配分したら満足度の合計は最大になるのでしょうか。条件としては二人には最初に30gを最低でも分けるとします。

モデリング

この問題に回答をしようと思うと、まず直感的には先程の図から考えるてAちゃんのほうが食べたグラム数に応じた満足感の増加が大きそうなので、Aちゃん>Bちゃんになりそうな感じが想定できます。

しかし何グラムずつにすればよいかという問いに答えるのは、先程の図だけだと充分でない気がします。そこで今回はAちゃんとBちゃんにそれぞれグラム数と満足感の関係をモデル化して、そのモデルのパラメータを活用して限界分析を行い最適なグラム数を求めるということを考えたいと思います。

ではどういったモデルが最適でしょうか?線形回帰でしょうか?それとも何らかの非線形回帰モデルでしょうか?

図の印象だとグラム数の増加に応じて満足感の増加は段々と逓減していくことから単純な線形モデルでは正しくなさそうです。また線形モデルで当てはめると、傾きが急な方に基本的に全部割り当てた方が良いという結果になってしまうため現実を反映していない気がします。

そこで今回は非線形回帰が良いと考えて累乗回帰を用いたいと思います。累乗回帰は段々と低減していくような曲線を当てはめる事が可能なモデルです。 数式的には累乗回帰モデルは、


y = ax^{b}

というモデル式になり、aとbのパラメータがいい感じにデータにフィットするように推定します。

では実際にRでAちゃんとBちゃんのグラム数と満足感のデータをフィットしてみます。Aちゃんのほうが、

nls(utility ~ a*gram^b, start = list(a = 1, b = 1), data = df[df$person == "A", ])

Nonlinear regression model
  model: utility ~ a * gram^b
   data: df[df$person == "A", ]
     a      b 
2.0309 0.1265 
 residual sum-of-squares: 5.744

Number of iterations to convergence: 13 
Achieved convergence tolerance: 1.257e-07

Bちゃんの方が、

nls(utility ~ a*gram^b, start = list(a = 1, b = 1), data = df[df$person == "B", ])

Nonlinear regression model
  model: utility ~ a * gram^b
   data: df[df$person == "B", ]
      a       b 
2.00921 0.09934 
 residual sum-of-squares: 5.406

Number of iterations to convergence: 13 
Achieved convergence tolerance: 2.471e-08

つまり、Aちゃんのモデルは、


y = 2.0309x^{0.1265}

Bちゃんのモデルが、


y = 2.00921x^{0.09934}

となります。

では線形回帰等に比べて本当に当てはまりが良いのでしょうか、決定係数を比較してみたいと思います*5。累乗回帰の決定係数の算出は以下の式を用いることにします。

決定係数 - Wikipedia

Aちゃんのデータだと、

nls_a <- nls(utility ~ a*gram^b, start = list(a = 1, b = 1), data = df[df$person == "A", ])
1- sum(residuals(nls_a)^2) / sum((df[df$person == "A", "utility"] - mean(df[df$person == "A", "utility"]))^2)
summary(lm(utility ~ gram, data = df[df$person == "A", ]))$r.squared

累乗回帰モデルが0.7616822で、単純な線形回帰が0.6182474となり、累乗回帰が当てはまりが良いようです。

一方Bちゃんのデータだと、

nls_b <- nls(utility ~ a*gram^b, start = list(a = 1, b = 1), data = df[df$person == "B", ])
1- sum(residuals(nls_b)^2) / sum((df[df$person == "B", "utility"] - mean(df[df$person == "B", "utility"]))^2)
summary(lm(utility ~ gram, data = df[df$person == "B", ]))$r.squared

累乗回帰が0.6138622と、線形回帰が0.4916945となり、やはり累乗回帰が良いようです。

データにそれぞれモデルを当てはめて曲線を書いてみるとAちゃんのほうが以下のようになります。

#累乗回帰モデルのパラメータをデータフレームに格納
df_para <- rbind(data.frame(a = coefficients(nls_a)[1], b = coefficients(nls_a)[2], person = "A"),
      data.frame(a = coefficients(nls_b)[1], b = coefficients(nls_b)[2], person = "B"))

#曲線フィット用横軸データ用意
x <- seq(1, 500, 1)
df_fit_a <- data.frame(x, utility = df_para[df_para$person == "A", 1] * x ^ df_para[df_para$person == "A", 2])
g <- ggplot(NULL)
g <- g + geom_point(data = df[df$person == "A", ], aes(x = gram, y = utility))
g <- g + geom_line(data = df_fit_a, aes(x = x, y = utility))
g

f:id:rf00:20181023221028p:plain

Bちゃんの方は以下です。

df_fit_b <- data.frame(x, utility = df_para[df_para$person == "B", 1] * x ^ df_para[df_para$person == "B", 2])
g <- ggplot(NULL)
g <- g + geom_point(data = df[df$person == "B", ], aes(x = gram, y = utility))
g <- g + geom_line(data = df_fit_b, aes(x = x, y = utility))
g

f:id:rf00:20181023221306p:plain

それぞれいい感じに当てはまっていて、食べたグラム数が少ない段階では満足感の増加が大きく、食べれば食べるほど満足感の増加が低減してくことが表現できていますね。

次にこの累乗回帰モデルのパラメータをもとに最適配分を考えていきます。ここでパラメータを一旦保存しましょう。

write.csv(df_para, "para.csv", row.names = FALSE)

限界分析を活用した最適配分の検討

限界分析を活用して最適配分を考えていきたいのですが、まずはパラメータが入ったデータフレームと対象者を指定し与えられたグラム数で微分する関数を準備します。

#与えられたグラム数で微分する関数
differential <- function(df, person, gram){
  a <- df[df$person == person, "a"]
  b <- df[df$person == person, "b"]
  
  return(a * b * gram^(b - 1))
}

以下のように使います。

df_utility_function <- read.csv("para.csv") #データ読み込み

differential(df_utility_function, "A", 30)

0.01316747となりましたが、これはAちゃんのとりのから揚げ満足度モデルを30gで微分した値です。つまり30g食べた後に1g食べた場合の満足度の増加量なので、これが限界効用と言えます。

一方Bちゃんはどうなのかというと、

differential(df_utility_function, "B", 30)

0.009327841となり、Aちゃんよりも30g食べた後に1g食べた時の満足感の増加量は少ないようです。これは可視化した図からもなんとなくわかっていたことですが、このように数値化して比較することができると意思決定に使えるようになります。

つまり二人の満足度を最大にしたいと考えた時、最初に30gずつとりの唐揚げを分けた後、次に分けた方がいいのはAちゃんになります。なぜなら満足感の増加量が大きいので、制約の範囲内で満足度の合計を最大化するためには効率がいいほうから配分する必要があるからです。

それでは次にメインの問題の設定に応じて満足感(効用)を最大化する配分を求める関数を定義します。

#条件内での効用最大化
gram_allocation_maximize <- function(df, unit, gram_a, gram_b, max_value){
  while(gram_a + gram_b < max_value){
    mu_a <- differential(df, "A", gram_a)
    mu_b <- differential(df, "B", gram_b)
    
    if(mu_a > mu_b){
      gram_a <- gram_a + unit
    }else if(mu_b > mu_a){
      gram_b <- gram_b + unit
    }else{
      gram_a <- gram_a + unit
      gram_b <- gram_b + unit
    }
  }
  
  gram_allocation <- c(gram_a, gram_b)
  
  #上限をオーバーしていた場合上限値になるよう調整
  if(sum(gram_allocation) > max_value){
    gram_allocation <- gram_allocation * (max_value / sum(gram_allocation))
  }
  
  return(gram_allocation)
}

引数の意味としては、第1引数にパラメータが格納されたデータフレーム、第2引数が最適配分をする際の最小の配分単位、第3引数がAちゃんの初期配分、第4引数がBちゃんの初期配分、第5引数が上限の合計グラム数を与えます。

今回の問題を解くためには以下のように値を与えればよく、

gram_allocation_maximize(df_utility_function, 1, 30, 30, 1800)

以下のような値が返ってきます。

[1] 1117  683

1つめの値がAちゃんに配分するとりの唐揚げのグラム数、2つ目の値がBちゃんに配分するとりの唐揚げのグラム数でこれが問題の答えです。

満足度の合計は、

df_utility_function[1, 1] * 1117 ^  df_utility_function[1, 2] + df_utility_function[2, 1] * 683 ^  df_utility_function[2, 2]

で求めることができ、

[1] 8.77707

となりました。試しにAちゃんのグラム数を2g減らしてBちゃんを2g増やしてみます。

df_utility_function[1, 1] * 1115 ^  df_utility_function[1, 2] + df_utility_function[2, 1] * 685 ^  df_utility_function[2, 2]

その結果は、

[1] 8.777068

となり満足度の合計は低下しました。これでAちゃんが1117g 、Bちゃんが683gで満足度の合計は最大化できていると考えられるのではないでしょうか。

まとめ

今回は限界分析の説明と限界分析を活用し、ある家族のとりの唐揚げの配分方法の最適化をテーマにある制約化において効用を最大化する方法を考えてみました。

この方法は例えば複数の工場があった時に、どの工場にどの程度リソースを割くのが効率的なのか考えるといったことのように、複数のリソース割り当て先にどのようにリソースを割り当てたら最適なのかを考える際に応用可能な考え方だと思います。みなさんも自分の取り組んでいる問題に活用できないか是非考えてみてください。

それでは間違い等ありましたら、ご指摘お願いいたします。

*1:おそらく幸せを感じる量は10個目から11個目のほうが少ないと思います

*2:ただし現実的には大食いの人にとってはもしかしたらMU_2の方が大きい可能性もありえます

*3:AちゃんとBちゃんの満足度は独立に決まる

*4:1個30gを想定しています

*5:累乗回帰で単純に決定係数を求めて比較しても問題ないのか自信があまりないです、有識者の方コメントお願いいたします