Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
allendowney
GitHub Repository: allendowney/thinkbayes2
Path: blob/master/scripts/price.py
1901 views
1
"""This file contains code for use with "Think Bayes",
2
by Allen B. Downey, available from greenteapress.com
3
4
Copyright 2013 Allen B. Downey
5
License: GNU GPLv3 http://www.gnu.org/licenses/gpl.html
6
"""
7
8
from __future__ import print_function, division
9
10
import csv
11
import numpy as np
12
import thinkbayes2
13
import thinkplot
14
15
import matplotlib.pyplot as pyplot
16
17
18
FORMATS = ['png', 'pdf', 'eps']
19
20
21
def ReadData(filename='showcases.2011.csv'):
22
"""Reads a CSV file of data.
23
24
Args:
25
filename: string filename
26
27
Returns: sequence of (price1 price2 bid1 bid2 diff1 diff2) tuples
28
"""
29
fp = open(filename)
30
reader = csv.reader(fp)
31
res = []
32
33
for t in reader:
34
_heading = t[0]
35
data = t[1:]
36
try:
37
data = [int(x) for x in data]
38
# print heading, data[0], len(data)
39
res.append(data)
40
except ValueError:
41
pass
42
43
fp.close()
44
return list(zip(*res))
45
46
47
class Price(thinkbayes2.Suite):
48
"""Represents hypotheses about the price of a showcase."""
49
50
def __init__(self, pmf, player, label=None):
51
"""Constructs the suite.
52
53
pmf: prior distribution of price
54
player: Player object
55
label: string
56
"""
57
thinkbayes2.Suite.__init__(self, pmf, label=label)
58
self.player = player
59
60
def Likelihood(self, data, hypo):
61
"""Computes the likelihood of the data under the hypothesis.
62
63
hypo: actual price
64
data: the contestant's guess
65
"""
66
price = hypo
67
guess = data
68
69
error = price - guess
70
like = self.player.ErrorDensity(error)
71
72
return like
73
74
75
class GainCalculator(object):
76
"""Encapsulates computation of expected gain."""
77
78
def __init__(self, player, opponent):
79
"""Constructs the calculator.
80
81
player: Player
82
opponent: Player
83
"""
84
self.player = player
85
self.opponent = opponent
86
87
def ExpectedGains(self, low=0, high=75000, n=101):
88
"""Computes expected gains for a range of bids.
89
90
low: low bid
91
high: high bid
92
n: number of bids to evaluates
93
94
returns: tuple (sequence of bids, sequence of gains)
95
96
"""
97
bids = np.linspace(low, high, n)
98
99
gains = [self.ExpectedGain(bid) for bid in bids]
100
101
return bids, gains
102
103
def ExpectedGain(self, bid):
104
"""Computes the expected return of a given bid.
105
106
bid: your bid
107
"""
108
suite = self.player.posterior
109
total = 0
110
for price, prob in sorted(suite.Items()):
111
gain = self.Gain(bid, price)
112
total += prob * gain
113
return total
114
115
def Gain(self, bid, price):
116
"""Computes the return of a bid, given the actual price.
117
118
bid: number
119
price: actual price
120
"""
121
# if you overbid, you get nothing
122
if bid > price:
123
return 0
124
125
# otherwise compute the probability of winning
126
diff = price - bid
127
prob = self.ProbWin(diff)
128
129
# if you are within 250 dollars, you win both showcases
130
if diff <= 250:
131
return 2 * price * prob
132
else:
133
return price * prob
134
135
def ProbWin(self, diff):
136
"""Computes the probability of winning for a given diff.
137
138
diff: how much your bid was off by
139
"""
140
prob = (self.opponent.ProbOverbid() +
141
self.opponent.ProbWorseThan(diff))
142
return prob
143
144
145
class Player(object):
146
"""Represents a player on The Price is Right."""
147
148
n = 101
149
price_xs = np.linspace(0, 75000, n)
150
151
def __init__(self, prices, bids, diffs):
152
"""Construct the Player.
153
154
prices: sequence of prices
155
bids: sequence of bids
156
diffs: sequence of underness (negative means over)
157
"""
158
self.pdf_price = thinkbayes2.EstimatedPdf(prices)
159
self.cdf_diff = thinkbayes2.MakeCdfFromList(diffs)
160
161
mu = 0
162
sigma = np.std(diffs)
163
self.pdf_error = thinkbayes2.NormalPdf(mu, sigma)
164
165
def ErrorDensity(self, error):
166
"""Density of the given error in the distribution of error.
167
168
error: how much the bid is under the actual price
169
"""
170
return self.pdf_error.Density(error)
171
172
def PmfPrice(self):
173
"""Returns a new Pmf of prices.
174
175
A discrete version of the estimated Pdf.
176
"""
177
return self.pdf_price.MakePmf(xs=self.price_xs)
178
179
def CdfDiff(self):
180
"""Returns a reference to the Cdf of differences (underness).
181
"""
182
return self.cdf_diff
183
184
def ProbOverbid(self):
185
"""Returns the probability this player overbids.
186
"""
187
return self.cdf_diff.Prob(-1)
188
189
def ProbWorseThan(self, diff):
190
"""Probability this player's diff is greater than the given diff.
191
192
diff: how much the oppenent is off by (always positive)
193
"""
194
return 1 - self.cdf_diff.Prob(diff)
195
196
def MakeBeliefs(self, guess):
197
"""Makes a posterior distribution based on estimated price.
198
199
Sets attributes prior and posterior.
200
201
guess: what the player thinks the showcase is worth
202
"""
203
pmf = self.PmfPrice()
204
self.prior = Price(pmf, self, label='prior')
205
self.posterior = self.prior.Copy(label='posterior')
206
self.posterior.Update(guess)
207
208
def OptimalBid(self, guess, opponent):
209
"""Computes the bid that maximizes expected return.
210
211
guess: what the player thinks the showcase is worth
212
opponent: Player
213
214
Returns: (optimal bid, expected gain)
215
"""
216
self.MakeBeliefs(guess)
217
calc = GainCalculator(self, opponent)
218
bids, gains = calc.ExpectedGains()
219
gain, bid = max(zip(gains, bids))
220
return bid, gain
221
222
def PlotBeliefs(self, root):
223
"""Plots prior and posterior beliefs.
224
225
root: string filename root for saved figure
226
"""
227
thinkplot.Clf()
228
thinkplot.PrePlot(num=2)
229
thinkplot.Pdfs([self.prior, self.posterior])
230
thinkplot.Save(root=root,
231
xlabel='price ($)',
232
ylabel='PMF',
233
formats=FORMATS)
234
235
236
def MakePlots(player1, player2):
237
"""Generates two plots.
238
239
price1 shows the priors for the two players
240
price2 shows the distribution of diff for the two players
241
"""
242
243
# plot the prior distribution of price for both players
244
thinkplot.Clf()
245
thinkplot.PrePlot(num=2)
246
pmf1 = player1.PmfPrice()
247
pmf1.label = 'showcase 1'
248
pmf2 = player2.PmfPrice()
249
pmf2.label = 'showcase 2'
250
thinkplot.Pdfs([pmf1, pmf2])
251
thinkplot.Save(root='price1',
252
xlabel='price ($)',
253
ylabel='PDF',
254
formats=FORMATS)
255
256
# plot the historical distribution of underness for both players
257
thinkplot.Clf()
258
thinkplot.PrePlot(num=2)
259
cdf1 = player1.CdfDiff()
260
cdf1.label = 'player 1'
261
cdf2 = player2.CdfDiff()
262
cdf2.label = 'player 2'
263
264
print('Player median', cdf1.Percentile(50))
265
print('Player median', cdf2.Percentile(50))
266
267
print('Player 1 overbids', player1.ProbOverbid())
268
print('Player 2 overbids', player2.ProbOverbid())
269
270
thinkplot.Cdfs([cdf1, cdf2])
271
thinkplot.Save(root='price2',
272
xlabel='diff ($)',
273
ylabel='CDF',
274
formats=FORMATS)
275
276
277
def MakePlayers():
278
"""Reads data and makes player objects."""
279
data = ReadData(filename='showcases.2011.csv')
280
data += ReadData(filename='showcases.2012.csv')
281
282
cols = zip(*data)
283
price1, price2, bid1, bid2, diff1, diff2 = cols
284
285
#print(list(sorted(price1)))
286
#print(len(price1))
287
288
player1 = Player(price1, bid1, diff1)
289
player2 = Player(price2, bid2, diff2)
290
291
return player1, player2
292
293
294
def PlotExpectedGains(guess1=20000, guess2=40000):
295
"""Plots expected gains as a function of bid.
296
297
guess1: player1's estimate of the price of showcase 1
298
guess2: player2's estimate of the price of showcase 2
299
"""
300
player1, player2 = MakePlayers()
301
MakePlots(player1, player2)
302
303
player1.MakeBeliefs(guess1)
304
player2.MakeBeliefs(guess2)
305
306
print('Player 1 prior mle', player1.prior.MaximumLikelihood())
307
print('Player 2 prior mle', player2.prior.MaximumLikelihood())
308
print('Player 1 mean', player1.posterior.Mean())
309
print('Player 2 mean', player2.posterior.Mean())
310
print('Player 1 mle', player1.posterior.MaximumLikelihood())
311
print('Player 2 mle', player2.posterior.MaximumLikelihood())
312
313
player1.PlotBeliefs('price3')
314
player2.PlotBeliefs('price4')
315
316
calc1 = GainCalculator(player1, player2)
317
calc2 = GainCalculator(player2, player1)
318
319
thinkplot.Clf()
320
thinkplot.PrePlot(num=2)
321
322
bids, gains = calc1.ExpectedGains()
323
thinkplot.Plot(bids, gains, label='Player 1')
324
print('Player 1 optimal bid', max(zip(gains, bids)))
325
326
bids, gains = calc2.ExpectedGains()
327
thinkplot.Plot(bids, gains, label='Player 2')
328
print('Player 2 optimal bid', max(zip(gains, bids)))
329
330
thinkplot.Save(root='price5',
331
xlabel='bid ($)',
332
ylabel='expected gain ($)',
333
formats=FORMATS)
334
335
336
def PlotOptimalBid():
337
"""Plots optimal bid vs estimated price.
338
"""
339
player1, player2 = MakePlayers()
340
guesses = np.linspace(15000, 60000, 21)
341
342
res = []
343
for guess in guesses:
344
player1.MakeBeliefs(guess)
345
346
mean = player1.posterior.Mean()
347
mle = player1.posterior.MaximumLikelihood()
348
349
calc = GainCalculator(player1, player2)
350
bids, gains = calc.ExpectedGains()
351
gain, bid = max(zip(gains, bids))
352
353
res.append((guess, mean, mle, gain, bid))
354
355
guesses, means, _mles, gains, bids = zip(*res)
356
357
thinkplot.PrePlot(num=3)
358
pyplot.plot([15000, 60000], [15000, 60000], color='gray')
359
thinkplot.Plot(guesses, means, label='mean')
360
#thinkplot.Plot(guesses, mles, label='MLE')
361
thinkplot.Plot(guesses, bids, label='bid')
362
thinkplot.Plot(guesses, gains, label='gain')
363
thinkplot.Save(root='price6',
364
xlabel='guessed price ($)',
365
formats=FORMATS)
366
367
368
def TestCode(calc):
369
"""Check some intermediate results.
370
371
calc: GainCalculator
372
"""
373
# test ProbWin
374
for diff in [0, 100, 1000, 10000, 20000]:
375
print(diff, calc.ProbWin(diff))
376
print()
377
378
# test Return
379
price = 20000
380
for bid in [17000, 18000, 19000, 19500, 19800, 20001]:
381
print(bid, calc.Gain(bid, price))
382
print()
383
384
385
def main():
386
PlotExpectedGains()
387
PlotOptimalBid()
388
389
390
391
if __name__ == '__main__':
392
main()
393
394