# R Code for Colada 88 - Hot hand artifact
#
# Written by Uri Simonsohn (urisohn@gmail.com)
# date: 2020 05 08
#
#
# This script simulates a sequence of shot attempts, where the probability of conversion
# depends on whether the previous 0,1,2,3 shots were made, and then the correlation between
# consecutive shots is computed.
# It is used as calibration for the high correlation between previous shot and prediction
# of next shot in Gilovich Vallone and Tversky Study 4.
#
# Two alterantive definitios of being hot: a streak of 1,2 or 3, whereas the streak is killed and is cold with one miss
# or the simple average conversion of the last 3 shots. Both lead to the same result
#
###############################################################################################
#Clear everything
rm(list = ls())
#Function 1 - Compute how long a streak over the last 3 attempts
get.streak=function(h0) #h0 is a sequence of 1/0s so far, e.g., h0=c(0,1) means missed 1st, made 2nd shot
{
#How many shots so far: h0 is the sequence so far
k0=length(h0)
#If the sequence is in one of the initial 3 shots, we add missed shots before, so players start cold.
#Add 0s so at least 3 in the sequence
h0=c(rep(0,max(3-k0,0)),h0) # c(1)-->c(0,0,1)
#Adjust k, the number of shots total so far, as we now know it is at least 3
k=max(k0,3)
#Count consecutive successes prior to this shot
if (h0[k]==0) return(0) #if missed last, streak is 0
if (h0[k]==1 & h0[k-1]==0) return(1) #if made exactly 1 and missed previous, streak is 1
if (h0[k]==1 & h0[k-1]==1 & h0[k-2]==0) return(2) #if made previous 2, ...2
if (h0[k]==1 & h0[k-1]==1 & h0[k-2]==1) return(3) #if made previous 3 ...3
}
#Examples of how the function works
get.streak(c(1,0,1)) #Made, missed, made--> streak of 1 prior to this shot
get.streak(c(0,1,1)) #2 in a row
get.streak(c(1,1,0)) #0 in a row before next throw
#Function 2 - get.moving.sum - This is an alterantive definition of hand-hotness
#Instead of setting the streak to 0 if one is missed, the hand is assumed to cool off incrementaly
#so here we compute the moving average of successes over the previous 3 shots
get.moving.sum=function(h0) #h0 is a sequence of 1/0s so far, e.g., h0=c(0,1) means missed 1st, made 2nd shot
{
#How many shots so far
k0=length(h0)
#Add 0s so at least 3 in the sequence
h0=c(rep(0,max(3-k0,0)),h0) # c(1)-->c(0,0,1)
#Adjust k as we now know it is at least 3
k=max(k0,3)
#Sum last 3 shots
moving.sum=(h0[k]+h0[k-1]+h0[k-2])
return(moving.sum)
}
#Examples
get.moving.sum(c(1,0,1)) #2 out of 3
get.moving.sum(c(0,1,1)) #2 out of 3
get.moving.sum(c(1,1,0)) #2 out of 3
get.moving.sum(c(1,1,1,1)) #3 out of 3
get.moving.sum(c(1,1,1,0)) #2 out of 3
#Function 3 - get the shooting probability for the next shot based on the previous 3 (here we generate the hand-hotness)
get.shooting.prob=function(h0, p0,p1,p2,p3,type="streak") {
#SYNTAX
# p0,p1,p2,p3 are the probabilities of making the next shot going from the coldest state, 0, to hottest 3
# type=c(streak, moving.sum) we define how we consider the hot hand
#STRICT STREAK
if (type=="streak")
{
streak=get.streak(h0)
if (streak==0) return(p0)
if (streak==1) return(p1)
if (streak==2) return(p2)
if (streak==3) return(p3)
}
#MOVING AVERAGE
if (type=='moving.sum')
{
moving.sum=get.moving.sum(h0)
if (moving.sum==0) return(p0)
if (moving.sum==1) return(p1)
if (moving.sum==2) return(p2)
if (moving.sum==3) return(p3)
}#End if type='moving_average'
}#End function get shooting ptob
#Example
get.shooting.prob(c(1,0,1),.1,.2,.3,.4,type='streak')
get.shooting.prob(c(1,0,1),.1,.2,.3,.4,type='moving.sum')
get.shooting.prob(c(1,0,0),.1,.2,.3,.4,type='streak')
get.shooting.prob(c(1,1,1),.1,.2,.3,.4,type='streak')
get.shooting.prob(c(0,1,1),.1,.2,.3,.4,type='streak')
#Function 4 - generate sequence of hits/misses, defining how many attempts total, and the probabilities from cold to hot, p0-p4, and how the hotness is defined
gen.shots=function(N,p0,p1,p2,p3,type) {
h=c()
for (k in 1:N) h[k]=rbinom(1,size=1,prob=get.shooting.prob(h,p0=p0,p1=p1,p2=p2,p3=p3))
return(h)
}
#Function 5 - Computes the correlation between a vector and the previous element of the same vector
cor.real=function(shots)
{
shots.previous=shots[2:length(shots)]
cor.test(shots.previous,shots[1:length(shots)-1])
}
#Function 6 simulate the shots, compute the correlation and mean hit rate, and report results
sim=function(...)
{
shots=gen.shots(...)
r=cor.real(shots)$estimate
p.all=mean(shots)
return(data.frame(r=as.numeric(r),mean=mean(shots)))
}
#######################################################
#Calibrations
#Result in the post
#After 0 - 'top' 129 .376 Tarean Prince
#After 1 - top 75 .450 Bobby Portis
#After 2 - top 25 .508 Karl-Anthony towns
#After 3 - Best .742 Mitchell Robinson
#Method to define hot-hand: sum of hits (hotness is base on SUM of hits from last 3 attempts)
set.seed(1110)#get it ?, miss after streak of 3
r=c()
for (k in 1:10000) r[k]=sim(100,p0=.376,p1=.45,p2=.508,p3=.742,type='moving.sum')$r
mean(r)
#Method=streak (hotness is base on streat of 0-3 in a row))
set.seed(1110)
r=c()
for (k in 1:10000) r[k]=sim(100,p0=.376,p1=.45,p2=.508,p3=.742,type='streak')$r
mean(r)
##############################################################################################################