RでPSM分析 #TokyoR

まずは直接測定法

# テスト用データ
cheap     <- c(rep(2, 5), rep(4, 20), rep(6, 25), rep(8, 30), rep(10, 15),
               rep(12, 5))
expensive <- c(rep(12, 5), rep(14, 5), rep(16, 15), rep(18, 25), rep(20, 30),
               rep(22, 15), rep(24, 5))

# 度数の算出、テーブルをデータフレームに変換しマージする
A.df <- data.frame(table(cheap))
B.df <- data.frame(table(expensive))
colnames(A.df) <- c("PRICE", "A.freq")
colnames(B.df) <- c("PRICE", "B.freq")
AB.df <- merge(A.df, B.df, all=TRUE)
AB.df[is.na(AB.df)] <- 0

# 価格順にソート
AB.df[, 1] <- type.convert(as.character(AB.df[, 1]))
AB.df <- AB.df[order(AB.df[, 1]), ]

# 累積和とその反転、Aは安い方が100%、Bは高い方が100%
AB.df[, 2] <- rev(cumsum(rev(AB.df[, 2])))
AB.df[, 3] <- cumsum(AB.df[, 3])
N <- length(cheap)
for (i in 2:3) {
  AB.df[, i+2] <- N - AB.df[,i]
  }
AB.df[, 2:5] <- AB.df[, 2:5]/N*100

###交点########################################################################
# 2直線の交点座標を求める関数 - 裏 RjpWiki
# <http://blog.goo.ne.jp/r-de-r/e/e81316c26c521b31e9d47526f9bd5861>
# (x1,y1)と(x2,y2)を通る直線と,(x3,y3)と(x4,y4)を通る直線の交点の座標を求める
# xだけ算出するように改造
###############################################################################
prog <- function(x1, y1, x2, y2, x3, y3, x4, y4)
  {
  a1 <- (y2-y1)/(x2-x1)
  a3 <- (y4-y3)/(x4-x3)
  x <- (a1*x1-y1-a3*x3+y3)/(a1-a3)
  y <- (y2-y1)/(x2-x1)*(x-x1)+y1
  return(c(x, y))
  }

# 最低価格
GAP <- AB.df[,2] - AB.df[,4]
if (max(GAP[GAP <= 0])==0){
	MIN <- c(AB.df[GAP==0,1],50)
	} else {
	X <- AB.df[, c(1,2,4)][GAP == max(GAP[GAP<0]), ]
	X <- X[X$PRICE==min(X$PRICE),]
	Y <- AB.df[, c(1,2,4)][GAP == min(GAP[GAP>0]), ]
	Y <- Y[Y$PRICE==max(Y$PRICE),]
	MIN <- prog(X[1], X[2], Y[1], Y[2], X[1], X[3], Y[1], Y[3])
	}
MIN.p <- round(as.numeric(MIN[1]), 0)

# 最高価格
GAP <- AB.df[,3] - AB.df[,5]
if (max(GAP[GAP <= 0])==0){
	MAX <- c(AB.df[GAP==0,1],50)
	} else{
	X <- AB.df[, c(1,3,5)][GAP == max(GAP[GAP<0]), ]
	X <- X[X$PRICE==max(X$PRICE),]
	Y <- AB.df[, c(1,3,5)][GAP == min(GAP[GAP>0]), ]
	Y <- Y[Y$PRICE==min(Y$PRICE),]
	MAX <- prog(X[1], X[2], Y[1], Y[2], X[1], X[3], Y[1], Y[3])
	}
MAX.p <- round(as.numeric(MAX[1]), 0)

# 最適価格
GAP <- AB.df[,2] - AB.df[,3]
if (max(GAP[GAP <= 0])==0){
	OPT <- c(AB.df[GAP==0,1:2])
	} else{
	X <- AB.df[, c(1,2,3)][GAP == max(GAP[GAP < 0]), ]
	X <- X[X$PRICE==min(X$PRICE),]
	Y <- AB.df[, c(1,2,3)][GAP == min(GAP[GAP > 0]), ]
	Y <- Y[Y$PRICE==max(Y$PRICE),]
	OPT <- prog(X[1], X[2], Y[1], Y[2], X[1], X[3], Y[1], Y[3])
	}
OPT.p <- round(as.numeric(OPT[1]), 0)

# 価格幅
RANGE <- MAX.p - MIN.p

# 描画
windows(16,9)
matplot(AB.df[,1], AB.df[,2:5], type="l", lwd=2, lty=1:2,
         col=c("#0041FF", "#FF3300"),
        main="直接測定法",
        xlab = "(ドル)", ylab = "%", las=2, xlim=c(min(cheap),max(expensive)))
abline(h=50, col = "#728490", lty="dashed")
abline(v=seq(min(AB.df[,1]), max(AB.df[,1]), 1), lty="dotted",
       col = "#E0E0E0")

# 指標の点
points(MAX[1], MAX[2], pch=21, lwd=2, bg="#FF9999", cex=1.25)
points(OPT[1], OPT[2], pch=22, lwd=2, bg="#FFFF00", cex=1.25)
points(MIN[1], MIN[2], pch=23, lwd=2, bg="#66CCFF", cex=1.25)

# 累積比率の凡例 matplotのデフォは(lty = 1:5, col = 1:6)
legend(locator(1), lty=1:3, col=c("#0041FF", "#FF3300"),
       lwd=1.5, bg="white", cex=0.75,
       legend=c("安い", "高い"))

# 指標の表示
TEMP <- c(N, MAX.p, OPT.p, MIN.p, RANGE)
TEMP <- format(TEMP, width=max(nchar(TEMP)))
LEGEND <- c(paste("         n=",  TEMP[1], sep=""),
            paste("上限価格: ", TEMP[2], "ドル", sep=""),
            paste("最適価格: ", TEMP[3], "ドル", sep=""),
            paste("下限価格: ", TEMP[4], "ドル", sep=""),
            paste("   RANGE: ", TEMP[5], "ドル", sep=""))
legend(locator(1), legend=LEGEND, pch=c(NA,21,22,23,NA),
       pt.bg=c(NA, "#FF9999", "#FFFF00", "#66CCFF", NA), bg="white", cex=0.75)


続いてPSM。バグが残ってないことを祈るのみ…

########################################
# PSM分析 van Westendorpの方式
#########################################
cheap     <- c(rep(8,5), rep(10,20), rep(12,25),rep(14,30),rep(16,15),
               rep(18,5))
expensive <- c(rep(6, 5), rep(8, 5), rep(10, 15), rep(12, 25), rep(14, 30),
               rep(16, 15), rep(18, 5))
too_expensive <- c(rep(12, 5), rep(14, 5), rep(16, 15), rep(18, 25),
                   rep(20, 30), rep(22, 15), rep(24, 5))
too_cheap <- c(rep(2, 5), rep(4, 20), rep(6, 25), rep(8, 30), rep(10, 15),
               rep(12, 5))

# A いくら以下だったら「安い」cheap
# B いくら以上だったら「高い」expensive
# C いくら以上だったら「高すぎる」too expensive
# D いくら以下だったら「安すぎる」too cheap

# 度数の算出、テーブルをデータフレームに変換
A.df <- data.frame(table(cheap))
B.df <- data.frame(table(expensive))
C.df <- data.frame(table(too_expensive))
D.df <- data.frame(table(too_cheap))

# 行に名前を付ける
colnames(A.df) <- c("PRICE", "A.freq")
colnames(B.df) <- c("PRICE", "B.freq")
colnames(C.df) <- c("PRICE", "C.freq")
colnames(D.df) <- c("PRICE", "D.freq")

# マージする
AB.df <- merge(A.df, B.df, all=TRUE)
CD.df <- merge(C.df, D.df, all=TRUE)
ABCD.df <- merge(AB.df, CD.df, all=TRUE)

# NAを0に置き換え(20101219)
ABCD.df[is.na(ABCD.df)] <- 0

# 価格順にソート
ABCD.df[,1] <- type.convert(as.character(ABCD.df[,1]))
ABCD.df <- ABCD.df[order(ABCD.df[, 1]), ]

# 累積和とその反転
#Aは安い方が100%
ABCD.df[,2] <- rev(cumsum(rev(ABCD.df[,2])))
#Bは高い方が100%
ABCD.df[,3] <- cumsum(ABCD.df[,3])
#Cは高い方が100%
ABCD.df[,4] <- cumsum(ABCD.df[,4])
#Dは安い方が100%
ABCD.df[,5] <- rev(cumsum(rev(ABCD.df[,5])))
#それぞれの反転
N <- length(cheap)
for (i in 2:5) {
  ABCD.df[, i+4] <- N - ABCD.df[,i]
  }
ABCD.df[, 2:9] <- ABCD.df[, 2:9]/N*100
# A1〜D1がそのままの累積比率(%)、A2〜D2が反転させた数値
colnames(ABCD.df) <- c("PRICE", "A1", "B1", "C1", "D1", "A2", "B2", "C2", "D2")
#A1[,2]:Cheap ☆
#B1[,3]:Expensive ☆
#C1[,4]:Too Expensive to Buy ★☆
#D1[,5]:Too Cheap to Buy ★☆
#A2[,6]:Not Feel Cheap ★
#B2[,7]:Not Feel Expensive ★
#C2[,8]
#D2[,9]

###交点########################################################################
# 2直線の交点座標を求める関数 - 裏 RjpWiki
# <http://blog.goo.ne.jp/r-de-r/e/e81316c26c521b31e9d47526f9bd5861>
# (x1,y1)と(x2,y2)を通る直線と,(x3,y3)と(x4,y4)を通る直線の交点の座標を求める
# xだけ算出するように改造
###############################################################################
prog <- function(x1, y1, x2, y2, x3, y3, x4, y4)
  {
  a1 <- (y2-y1)/(x2-x1)
  a3 <- (y4-y3)/(x4-x3)
  x <- (a1*x1-y1-a3*x3+y3)/(a1-a3)
  y <- (y2-y1)/(x2-x1)*(x-x1)+y1
  return(c(x, y))
  }

### 交点の算出
# 「安くない」「高くない」式
# 下限価格(MCP, Marginal Cheap Point)
# D1[,5]:Too Cheap to Buy ★☆
# A2[,6]:Not Feel Cheap ★

GAP <- ABCD.df[,5] - ABCD.df[,6]
if (max(GAP[GAP <= 0])==0){
	MCP.P <- c(ABCD.df[GAP==0,c(1,5)])
	} else {
	X <- ABCD.df[, c(1,5,6)][GAP == max(GAP[GAP<0]), ]
	X <- X[X$PRICE==min(X$PRICE),]
	Y <- ABCD.df[, c(1,5,6)][GAP == min(GAP[GAP>0]), ]
	Y <- Y[Y$PRICE==max(Y$PRICE),]
	MCP.P <- prog(X[1], X[2], Y[1], Y[2], X[1], X[3], Y[1], Y[3])
	}
MCP <- round(as.numeric(MCP.P[1]), 0)

#上限価格(MEP, Marginal Expensive Point)
# C1[,4]:Too Expensive to Buy ★☆
# B2[,7]:Not Feel Expensive ★
GAP <- ABCD.df[,4] - ABCD.df[,7]
if (max(GAP[GAP <= 0])==0){
	MEP.P <- c(ABCD.df[GAP==0,c(1,4)])
	} else {
	X <- ABCD.df[, c(1,4,7)][GAP == max(GAP[GAP<0]), ]
	X <- X[X$PRICE==min(X$PRICE),]
	Y <- ABCD.df[, c(1,4,7)][GAP == min(GAP[GAP>0]), ]
	Y <- Y[Y$PRICE==max(Y$PRICE),]
	MEP.P <- prog(X[1], X[2], Y[1], Y[2], X[1], X[3], Y[1], Y[3])
	}
MEP <- round(as.numeric(MEP.P[1]), 0)

#最適価格(OPP, Optimum Pricing Point)
# C1[,4]:Too Expensive to Buy ★☆
# D1[,5]:Too Cheap to Buy ★☆
GAP <- ABCD.df[,4] - ABCD.df[,5]
if (max(GAP[GAP <= 0])==0){
	OPP.P <- c(ABCD.df[GAP==0,c(1,4)])
	} else {
	X <- ABCD.df[, c(1,4,5)][GAP == max(GAP[GAP<0]), ]
	X <- X[X$PRICE==min(X$PRICE),]
	Y <- ABCD.df[, c(1,4,5)][GAP == min(GAP[GAP>0]), ]
	Y <- Y[Y$PRICE==max(Y$PRICE),]
	OPP.P <- prog(X[1], X[2], Y[1], Y[2], X[1], X[3], Y[1], Y[3])
	}
OPP <- round(as.numeric(OPP.P[1]), 0)

	#妥協価格(IDP, Indifferent Point)
	# A2[,6]:Not Feel Cheap ★	
	# B2[,7]:Not Feel Expensive ★
GAP <- ABCD.df[,6] - ABCD.df[,7]
if (max(GAP[GAP <= 0])==0){
	IDP.P <- c(ABCD.df[GAP==0,c(1,6)])
	} else {
	X <- ABCD.df[, c(1,6,7)][GAP == max(GAP[GAP<0]), ]
	X <- X[X$PRICE==min(X$PRICE),]
	Y <- ABCD.df[, c(1,6,7)][GAP == min(GAP[GAP>0]), ]
	Y <- Y[Y$PRICE==max(Y$PRICE),]
	IDP.P <- prog(X[1], X[2], Y[1], Y[2], X[1], X[3], Y[1], Y[3])
	}
IDP <- round(as.numeric(IDP.P[1]), 0)

RANGE <- MEP - MCP


###############################################################################
#作図
###############################################################################
windows(16,9)
# 「安くない」「高くない」式
matplot(ABCD.df[,1], ABCD.df[,4:7], type="l", pch=c(21,22,23,24), lwd=2,
         col=c("#FF3300", "#990066", "#339966", "#0041FF"),
        main="van Westendorp \n Price Sensitivity Meter",
        xlab = "(ドル)", ylab = "%", las=2, xlim=c(min(too_cheap),max(too_expensive)))
abline(h=50, col = "#728490", lty="dashed")
abline(v=seq(min(ABCD.df[,1]), max(ABCD.df[,1]), 1), lty="dotted",
       col = "#E0E0E0")

# 指標の点
points(MEP.P[1], MEP.P[2], pch=21, lwd=2, bg="#FF9999", cex=1.25)
points(IDP.P[1], IDP.P[2], pch=22, lwd=2, bg="#FFFF00", cex=1.25)
points(OPP.P[1], OPP.P[2], pch=23, lwd=2, bg="#66CCFF", cex=1.25)
points(MCP.P[1], MCP.P[2], pch=24, lwd=2, bg="#FF9900", cex=1.25)

# 累積比率の凡例 matplotのデフォは(lty = 1:5, col = 1:6)
legend(locator(1), lty=1:4, col=c("#FF3300", "#990066", "#339966", "#0041FF"),
       lwd=1.5, bg="white", cex=0.75,
       legend=c("高すぎる", "安すぎる",
                "安くない", "高くない"))

###########################
# 指標の表示
TEMP <- c(N, MEP, IDP, OPP, MCP, RANGE)
TEMP <- format(TEMP, width=max(nchar(TEMP)))
LEGEND <- c(paste("         n=",  TEMP[1], ".", sep=""),
            paste("上限価格: ", TEMP[2], "ドル", sep=""),
            paste("妥協価格: ", TEMP[3], "ドル", sep=""),
            paste("最適価格: ", TEMP[4], "ドル", sep=""),
            paste("下限価格: ", TEMP[5], "ドル", sep=""),
            paste("   RANGE: ", TEMP[6], "ドル", sep=""))
legend(locator(1), legend=LEGEND, pch=c(NA,21,22,23,24,NA),
       pt.bg=c(NA, "#FF9999", "#FFFF00", "#66CCFF","#FF9900", NA), bg="white", cex=0.75)