This file replicates all figures and results from the paper that were produced with Python codes. This file calls preamble.ipynb and uses data from data.dta.
%run preamble.ipynb
Sender’s Strategy: Commitment vs. Revision, $\rho = 0.8$
UpredictionR = {}
UpredictionR['red']=0
UpredictionR['blue']=0
UpredictionR['none']=0
UpredictionR
UpredictionB = {}
UpredictionB['red']=5/8
UpredictionB['blue']=-5/8
UpredictionB['none']=0
Upredictions = {}
Upredictions['r']= UpredictionR
Upredictions['b']= UpredictionB
VpredictionR = {}
VpredictionR['red']=1
VpredictionR['blue']=np.nan
VpredictionR['none']=-1
VpredictionR
VpredictionB = {}
VpredictionB['red']=np.nan
VpredictionB['blue']=-.75
VpredictionB['none']=+.75
Vpredictions = {}
Vpredictions['r']= VpredictionR
Vpredictions['b']= VpredictionB
col = {'red': 'black', 'blue':'black', 'none':'gray'}
plt.figure(figsize=(15,7))
## The following code plots the histogram
for treatment in ['U80', 'V80']:
DF = load_data(treatment = treatment, match = 16, role = 'S')
DF = prepare_data(DF, 'session', 'Strong')
plt.subplot(1,2, 1 if treatment[0] == 'U' else 2)
plt.tight_layout(pad = 6)
plt.axhline(y=0, color='gray',alpha = .75, linestyle='-')
xColor = 0
for color in ['r','b']:
k = 0
for msg in ['red', 'blue', 'none']:
variable1 = '' + color + msg
variable2 = 'rev' + color + msg
variable = - DF[variable1]/100 + DF[variable2]/100
mean = variable.mean()
## Confidence intervals
if treatment[0] == 'V' and color == 'r' and msg == 'blue': # Ignore the zero probability event: verifiable treatment, red ball & blue message
cih, cil = 0, 0
elif treatment[0] == 'V' and color == 'b' and msg == 'red': # Ignore the zero probability event: verifiable treatment, blue ball & red message
cih, cil = 0, 0
else:
cih, cil = stats.t.interval(0.96, len(variable)-1, loc=np.mean(variable), scale=stats.sem(variable))
plt.bar(xColor + k, mean, width = .4, edgecolor= 'black', color= 'rosybrown' if msg == 'red' else "#34495e" if msg == 'blue' else 'whitesmoke', alpha = 1, label = '$m=' + msg[0] + '$' if color == 'r' else '_nolabel')
plt.plot([xColor + k, xColor + k],[cih,cil], c='black', alpha = .5)
k += .5
xColor += 2
## Formatting the looks of the plot
plt.xticks([.5,2.5], [r'$\theta = R$', r"$\theta = B$"], fontsize = 15)
plt.yticks([-.50, -.25, 0, .25, .50])
plt.axis([-.5,3.5, -.5,.5])
plt.title('Treatment $' + treatment +'$', fontsize = 16)
plt.ylabel(r'$\pi_R(m|\theta) - \pi_C(m|\theta)$', fontsize = 16)
plt.text(-.5, -.31, 'Informativeness:', fontsize = 14, fontweight='bold')
corrcomm = round(correlation_bayesian_receiver(DF, '').mean(),2)
corrrev = round(correlation_bayesian_receiver(DF, 'rev').mean(),2)
plt.text(-.5 , -.38, '$\phi^B(\pi_C)='+ str(corrcomm)+'$', fontsize = 14)
plt.text(-.5 , -.45, '$\phi^B(\pi_R)='+ str(corrrev)+'$' , fontsize = 14)
if treatment == 'U80':
plt.legend(loc = (0,.76), fontsize = 16)
plt.show()
Receiver’s Response to Persuasive Messages: $\rho = 0.2$ vs. $\rho = 1$
from scipy.optimize import curve_fit
## Define polynomial function of degree 2, will be used to fit the data
def quadratic_function(x, a, b, c):
return a + b * x + c * x ** 2
plt.figure(figsize=(15,6))
for rule in ['U', 'V']: # Compare Unverifiable and Verifiable information in two separate subplots
plt.subplot(1,2, 1 if rule == 'U' else 2)
for treatment in [rule + '20', rule + '100']: # Compare treatments \rho=0.2 and \rho=1 in the same subplot
msg = 'none' if rule == 'V' else 'red' # Choose the "persuasive" message (see main draft): red for U and none for V
storem = []
storeh = []
storel = []
DF = load_data(treatment, match = 16, role = 'S')
DF = prepare_data(DF, 'session', 'Strong')
# The next few lines use the variables "post_red" and "post_none", which have been defined in "prepare_data" (see preamble)
DF = DF[np.isnan(DF['post_' + msg]) == False] # Drop observations for which the persuasive msg has probability zero
temp = DF.sort_values('post_' + msg, ascending = True) # Generate a temporary dataframe "temp" ordered by post_msg
x = temp['post_' + msg].values # Generate a vector with post_msg
y = temp['m'+ msg[0] + 'dec'] # Generate a vector with receiver's guess to persuasive message
## Estimate Quadratic Fit
params, covar = curve_fit(quadratic_function, x, y, bounds = [0,1])
## Simulate 10'000 times to generate confidence bounds
rangex = np.linspace(0,1,100) # Range of posterior beliefs
temp_par = np.random.multivariate_normal(params, covar, 10000) ## Generate 10'000 parameters from estimation above
a, b, c = temp_par[:,0], temp_par[:,1], temp_par[:,2]
for x in rangex:
temp = quadratic_function(x, a, b, c)
tempstorem, tempstoreh, tempstorel = np.percentile(temp, 50), np.percentile(temp, 95), np.percentile(temp, 5)
storeh = np.append(storeh, tempstoreh)
storel = np.append(storel, tempstorel)
storem = np.append(storem, tempstorem)
## Format the looks of the plot
plt.plot(rangex, storem, '--' if treatment[1:]!='20' else '-', alpha =.7, c = 'black', linewidth = 2, label = '$' + treatment + "$")
plt.plot(rangex, storel, '--' if treatment[1:]!='20' else '-', alpha =.35, c = 'black', linewidth = 1)
plt.plot(rangex, storeh, '--' if treatment[1:]!='20' else '-', alpha =.35, c = 'black', linewidth = 1)
string = 'r' if rule == 'U' else 'n'
plt.xlabel('Interim Posterior Conditional on $m=' + string +'$', fontsize = 15)
plt.axis([0,1,0,1])
plt.ylabel('Frequency of $a=red$', fontsize = 15)
plt.title('Unverifiable Information' if treatment[0] == 'U' else 'Verifiable Information', fontsize = 15)
plt.legend(loc = 2, fontsize = 14)
plt.show()
Cumulative Distribution of Sender-Average $\phi_B(\pi_C, \pi_R)$ by Treatment
plt.figure(figsize = (16,6))
shades = {'20': 1, '80': 1, '100': .9}
face = {'20': 'white', '80': 'gainsboro', '100': 'black'}
for rule in ['U','V']: # Compare Unverifiable and Verifiable information in two separate subplots
plt.subplot(1, 2, 1 if rule == 'U' else 2)
for treatment in [rule + '20', rule + '80', rule + '100']: # Compare treatments with \rho=0.2, \rho=0.8, and \rho=1 in the same subplot
DF = load_data(treatment, match = 16, role = 'S')
DF = prepare_data(DF, 'session', 'Strong')
data = []
for i in DF.id.unique(): ## Consider each sender separately.
temp = DF[DF['id'] == i]
data_temp = correlation_bayesian_receiver(temp, 'agg')
data = np.append(data, np.mean(data_temp)) ## Compute sender average
## Next lines create CDF and plot
N = data.size
Z = np.sort(data)
F = np.array(range(N)) / float(N)
plt.plot(Z, F, label = "$"+treatment+"$", c = 'black', alpha = shades[treatment[1:]], marker = 'o', linewidth = .4, markerfacecolor = face[treatment[1:]], markeredgewidth=.5)
## Format the looks of the plot
plt.title('Unverifiable Information' if rule == 'U' else 'Verifiable Information', fontsize = 15)
plt.ylabel('Percentile', fontsize = 15)
plt.xlabel('Bayesian Correlation $\phi^B(\pi_C,\pi_R)$ by Sender', fontsize = 15)
plt.yticks([0,.25,.5,.75,1])
plt.xticks([0,.25,.5,.75,1])
plt.axis([-.02, 1.02, -0.02, 1.02])
plt.legend(fontsize = 15)
plt.show()
Average Bayesian Correlations $\phi^B$
for rule in ['U','V']: # Compare Unverifiable and Verifiable information in two separate subplots
for treatment in [rule + '20', rule + '80', rule + '100']:
DF = load_data(treatment, match = 16, role = 'S')
DF = prepare_data(DF, 'session', 'Strong')
mean_corr = round(correlation_bayesian_receiver(DF, 'agg').mean(), 4)
print(treatment + " " + str(mean_corr))
Sender’s Empirical Expected Payoff
In preparation for the probit estimation, we begin by reshaping the DF. The following code defines a function that reshapes the dataframe DF. In the reshaped dataframe, which we denoted by df, each line corresponds to a single play by a recevier. We invoke the strategy method and define one line for each possible message. If the message had probability zero, the line is dropped. The columns of the reshaped dataframe are
session subject match id posterior Tot_prob msg guess intercept msg_red msg_blue msg_none
def reshape_data(DF):
DF = DF[['session', 'subject', 'match', 'aggpost_red', 'aggpost_blue', 'aggpost_none', 'aggTotprob_red',
'aggTotprob_blue', 'aggTotprob_none', 'mrdec', 'mbdec', 'mndec', 'id']]
vars = [c for c in DF if c.startswith('aggpost')]
varsTOT = [c for c in DF if c.startswith('aggTot')]
all = [c for c in DF]
other = [c for c in all if c not in vars]
stacked = DF.set_index(other).stack()
df = stacked.reset_index()
df.level_10 = np.where(df.level_10 == 'aggpost_red', 'red', df.level_10)
df.level_10 = np.where(df.level_10 == 'aggpost_blue', 'blue', df.level_10)
df.level_10 = np.where(df.level_10 == 'aggpost_none', 'none', df.level_10)
df['msg_post'] = df['level_10']
del df['level_10']
all = [c for c in DF]
other = [c for c in all if c not in (varsTOT + vars)]
other = other + ['posterior', 'msg_post']
df['posterior'] = df[0]
del df[0]
stacked = df.set_index(other).stack()
df = stacked.reset_index()
df['Tot_prob'] = df[0]
del df[0]
df.level_9 = np.where(df.level_9 == 'aggTotprob_red', 'red', df.level_9)
df.level_9 = np.where(df.level_9 == 'aggTotprob_blue', 'blue', df.level_9)
df.level_9 = np.where(df.level_9 == 'aggTotprob_none', 'none', df.level_9)
df['msg_tot'] = df['level_9']
del df['level_9']
vars = [c for c in df if c.endswith('dec')]
all = [c for c in df]
other = [c for c in all if c not in vars]
stacked = df.set_index(other).stack()
df = stacked.reset_index()
df.level_8 = np.where(df.level_8 == 'mrdec', 'red', df.level_8)
df.level_8 = np.where(df.level_8 == 'mbdec', 'blue', df.level_8)
df.level_8 = np.where(df.level_8 == 'mndec', 'none', df.level_8)
df['msg_dec'] = df['level_8']
del df['level_8']
df = df[df.msg_post == df.msg_tot]
df = df[df.msg_post == df.msg_dec]
df['msg'] = df.msg_dec
df['guess'] = df[0]
del df['msg_post'], df['msg_tot'], df['msg_dec'], df[0]
df = df.reset_index()
df['intercept'] = 1
for message in ['red', 'blue', 'none']:
df['msg_' + message] = np.where(df.msg == message, 1, 0)
return df
The following functions are used in the Probit estimation
## The following function computes the Bayesian posterior that is induced by strategy(x) (defined later).
# The Bayesian posterior for messages that have 0 probability is set to NaN
def posterior(strategy):
store = [-1, -1, -1]
for i in [0, 1, 2]:
totprob = 1/3 * strategy[i] + 2/3 * strategy[i + 3] # \mu(R) + \pi(i|R) + \mu(B) + \pi(i|B)
if totprob > 0:
store[i] = 1/3 * strategy[i] / totprob
else:
store[i] = np.nan
return store
## The following function estimates Probit for receiver "id".
# The model is alpha + beta * posterior + gamma * msg_red + delta * msg_blue and outputs the vector of coefficients
def probit(verbose, id):
dfreg = df[df.id == id]
inter = ['intercept']
result = sm.Probit(dfreg.guess, dfreg[inter + ['posterior', 'msg_red', 'msg_blue']]).fit(maxiter = 200, method = 'bfgs', disp = (verbose == 'Yes'))
coeff = result.params.values
if verbose == 'Yes':
print(result.summary())
return coeff
## The following function computes the following Probit linear model:
# Phi(intercept + beta * Bayesian posterior + gamma * dummy(msg is red) + delta * dummy(msg is blue)
# where Phi denotes the normal CDF
# x denotes the Bayesian posterior at a given message msg
def yhat(x, msg, coeff):
if np.isnan(x):
return np.nan
else:
xhat = np.dot(coeff[0 : 4], ([1, x, msg == 'red', msg == 'blue']))
return norm.cdf(xhat) # Normal CDF
## The following function computes the expected payoff given a strategy(x) in a certain treatment and a receiver's avg_response
# The expected payoff is expressed in dollars, hence it has a multiplier of 2 since the sender wins $2 per round when the receiver guesses red
def expectedpayoff(strategy, treatment, avg_response):
return 2 * ((1/3 * strategy[treatment][0] + 2/3 * strategy[treatment][3]) * np.nan_to_num(avg_response['red']) +
(1/3 * strategy[treatment][1] + 2/3 * strategy[treatment][4])* np.nan_to_num(avg_response['blue']) +
(1/3 * strategy[treatment][2] + 2/3 * strategy[treatment][5])* np.nan_to_num(avg_response['none'])
)
This last piece of code runs the estimation algorithm and plots the figure
This routine produces warnings because there are few receivers who always guess blue. For these receiver, the Probit estimation outputs a warning. Despite the warning, however, the coefficients that are recorded are correct in the sense that these receivers are estimated to guess red for any posterior x with probability zero, as it should be.
import warnings
warnings.simplefilter('ignore')
# The previous code silence the warnings. This routine produces warnings because there are few receivers
# who always guess blue. For these receiver, the Probit estimation outputs a warning. Despite the warning,
# however, the coefficients that are recorded are correct in the sense that these receivers are estimated
# to guess red for any posterior x with probability zero, as it should be.
import statsmodels.discrete.discrete_model as sm
from scipy.stats import norm
rangex = np.linspace(0, 1, 50)
plt.figure(figsize = (8,6))
for treatment in ['U100','V100']:
paystore = []
paystore_pred = []
for x in rangex: ## For loop a relevant class of strategies parameterized by x (see main draft)
strategy = {'U100' : [1, 0, 0, 1 - x, x, 0],
'V100' : [0, 0, 1, 0, x, 1-x]}
DF = load_data(treatment, 16, 'R') ## Select the database of receivers
DF = prepare_data(DF, '', '') # imputation method is irrelevant since this is is a full-commitment treatment, left blank
df = reshape_data(DF)
df['id'] = df.id.astype('int')
intercept = 'Yes'
phistore = []
ESstore = []
for id in df.id.unique(): ## For every receiver
coeff = probit(verbose = 'No', id = id) ## Estimate probit coefficients for each receiver
avg_response = {'red': yhat(posterior(strategy[treatment])[0], 'red', coeff),
'blue': yhat(posterior(strategy[treatment])[1], 'blue', coeff),
'none': yhat(posterior(strategy[treatment])[2], 'none', coeff) }
ES = expectedpayoff(strategy, treatment, avg_response)
ESstore = np.append(ESstore, ES)
paystore = np.append(paystore, np.median(ESstore)) # Median
## Compute predictions: receiver's best respond to strategy(x); compute sender's expected payoff
temp = np.array(posterior(strategy[treatment])) # Posteriors that strategy(x) induces
## receiver's best response, breaking ties in favor of sender
avg_response_pred = {'red': (temp >=.5)[0],
'blue': (temp >=.5)[1],
'none': (temp >=.5)[2]}
## Next line computes sender's predicted expected payoff (2 dollars times probability of guessing red)
payoff_pred = 2 * ((1/3 * strategy[treatment][0] + 2/3 * strategy[treatment][3]) * np.nan_to_num(avg_response_pred['red']) +
(1/3 * strategy[treatment][1] + 2/3 * strategy[treatment][4])* np.nan_to_num(avg_response_pred['blue']) +
(1/3 * strategy[treatment][2] + 2/3 * strategy[treatment][5])* np.nan_to_num(avg_response_pred['none']))
# store payoff_pred
paystore_pred = np.append(paystore_pred, payoff_pred)
## --> end of x loop <--
## Plot and format the looks:
if treatment == "U100":
plt.plot(rangex, paystore[:] , c = 'black', marker='o', markevery=3, markeredgecolor='black', markeredgewidth = 1, markerfacecolor='white', markersize=8, linestyle = "dashed", alpha = 1, label = "$" + treatment + "$")
if treatment == "V100":
plt.plot(rangex, paystore[:] , c = 'gray', marker='s', markevery=3, markeredgecolor='gray', markerfacecolor='gray', markersize=6, alpha = 1, label = "$"+treatment+ "$")
plt.scatter(rangex, paystore_pred[:] , c = 'black', s=13, alpha = .75, marker='o',label = "Predictions")
plt.xlabel("$x$ -- Parametrized Sender's Strategy", fontsize = 15)
plt.ylabel("Sender's Expected Payoff", fontsize = 15)
plt.legend(loc = 2, prop = {'size' : 15})
plt.axis([0,1,-0.01,1.35])
plt.yticks([0,.25,.5,.75,1,1.25])
plt.xticks([0,.25,.5,.75,1])
plt.show()
Treatment U80 – Clustering of Senders’ Strategies
treatment = "U80"
n = 4 # Number of clusters to estimate
DF = load_data(treatment, match = 16, role = 'S')
DF = prepare_data(DF, 'subject', 'Weak') ## Use imputation method (subject-weak), see Footnote 32
DF["corr_agg"] = correlation_bayesian_receiver(DF, 'agg')
np.random.seed(1)
colors = ['orange','r', 'b','g']
markers = ['^','o','s',"D"]
df, AS = clusters(DF, n) # Computes clusters and stores them
relsize = (np.round((100*df.cluster.value_counts(sort = False)/df.cluster.size).values,0))
fig = plt.figure(figsize=(16,6))
## Commitment Stage Strategy
plt.subplot(1,2, 1)
plt.axhline(0, c = 'black', alpha = 1, linewidth = 1.3, zorder = -1)
plt.axvline(0, c = 'black', linewidth = 1.3, zorder=0)
plt.scatter(100, 5/8*100, c = 'white', s = 200, alpha = .75, edgecolors = 'black', marker = 'X', linewidth = 1)
for i in range(n): # Plot observed commitment strategies
plt.scatter(df[df.cluster==i].rred, df[df.cluster==i].bblue, c = colors[i], s = 25, alpha = .25, edgecolors= colors[i], marker = markers[i], label = "_nolabel")
for i in range(n): # Plot cluster centroids
plt.scatter(AS[i, 0], AS[i, 3], c = colors[i], s = 150, alpha = .75, edgecolors= colors[i], marker = markers[i], linewidth = 2)
plt.axis([-5,105,-5,105])
plt.xlabel('$\pi_{C}(r|R)$', fontsize = 18)
plt.ylabel('$\pi_{C}(b|B)$', fontsize = 18)
plt.title("$U80$ -- Commitment Stage", fontsize = 18)
## Revision Stage Strategy
plt.subplot(1,2, 2)
plt.axhline(0, c = 'black', alpha = 1, linewidth = 1.3, zorder = -1)
plt.axvline(0, c = 'black', linewidth = 1.3, zorder=0)
plt.title("$U80$ -- Revision Stage", fontsize = 18)
sns.despine()
plt.axis([-5,105,-5,105])
plt.xlabel('$\pi_{R}(r|R)$', fontsize = 18)
plt.ylabel('$\pi_{R}(b|B)$', fontsize = 18)
plt.scatter(100, 0, c = 'white', s = 200, alpha = .75, edgecolors = 'black', marker = 'X', linewidth = 1, label = "Prediction, $\phi^B = 0.50$")
for i in [2,1,3,0]: # Plot observed revision strategies
plt.scatter(df[df.cluster==i].revrred, df[df.cluster==i].revbblue, c = colors[i], s = 25, alpha = .25, edgecolors= colors[i], marker = markers[i], label = "_nolabel")
for i in [2,1,0,3]: # Plot cluster centroids
plt.scatter(AS[i, 4], AS[i, 7], c = colors[i], s = 150, alpha = .75, edgecolors= colors[i], marker = markers[i], linewidth = 2, label = "Freq " + str(relsize[i]) +"\%, $\phi^B = $ " + str(np.round(df[df.cluster==i].corr_agg.mean(),2)))
plt.legend(loc = 'upper right', ncol=5, fontsize = 14, bbox_to_anchor=(1.1, -0.15))
plt.show()
Treatment V80 – Clustering of Senders’ Strategies
treatment = "V80"
n = 4 # Number of clusters to estimate
DF = load_data(treatment, match = 16, role = 'S')
DF = prepare_data(DF, 'subject', 'Weak') ## Use imputation method (subject-weak), see Footnote 32
DF["corr_agg"] = correlation_bayesian_receiver(DF, 'agg')
np.random.seed(1)
df, AS = clusters(DF, n) # Computes clusters and stores them
colors = ['orange','r', 'orchid','b']
markers = ['^','o','*',"s"]
relsize = (np.round((100*df.cluster.value_counts(sort = False)/df.cluster.size).values,0))
fig = plt.figure(figsize=(16,6))
## Commitment Stage Strategy
plt.subplot(1,2, 1)
plt.scatter(0, 3/4*100, c = 'white', s = 200, alpha = .75, edgecolors = 'black', marker = 'X', linewidth = 1)
plt.axis([-5,105,-5,105])
plt.xlabel('$\pi_{C}(r|R)$', fontsize = 18)
plt.ylabel('$\pi_{C}(b|B)$', fontsize = 18)
plt.axvline(0, c = 'gray', linewidth = 1)
plt.axhline(0, c = 'gray', linewidth = 1)
plt.title("$V80$ -- Commitment Stage", fontsize = 18)
for i in range(n): # Plot observed commitment strategies
plt.scatter(df[df.cluster==i].rred, df[df.cluster==i].bblue, c = colors[i], s = 25, alpha = .25, edgecolors= colors[i], marker = markers[i])
for i in range(n): # Plot cluster centroids
plt.scatter(AS[i, 0], AS[i, 3], c = colors[i], s = 150 if i!=2 else 250, alpha = .75, edgecolors= colors[i], marker = markers[i], linewidth = 2)
## Revision Stage Strategy
plt.subplot(1,2, 2)
plt.scatter(100, 0, c = 'white', s = 200, alpha = .75, edgecolors = 'black', marker = 'X', linewidth = 1, label = "Prediction, $\phi^B = 0.57$")
plt.axis([-5,105,-5,105])
plt.xlabel('$\pi_{R}(r|R)$', fontsize = 18)
plt.ylabel('$\pi_{R}(b|B)$', fontsize = 18)
plt.axvline(0, c = 'gray', linewidth = 1)
plt.axhline(0, c = 'gray', linewidth = 1)
for i in [2,1,3,0]: # Plot observed revision strategies
plt.scatter(df[df.cluster==i].revrred, df[df.cluster==i].revbblue, c = colors[i], s = 25, alpha = .25, edgecolors= colors[i], marker = markers[i], label = '_nolabel')
for i in [3,1,2,0]: # Plot cluster centroids
plt.scatter(AS[i, 4], AS[i, 7], c = colors[i], s = 150 if i!=2 else 250, alpha = .75, edgecolors = colors[i], marker = markers[i], linewidth = 2, label = "Freq " + str(relsize[i]) +"\%, $\phi^B = $ " + str(np.round(df[df.cluster==i].corr_agg.mean(),2)))
plt.title("$V80$ -- Revision Stage", fontsize = 18)
plt.legend(loc = 'upper right', ncol=5, fontsize = 14, bbox_to_anchor=(1.1, -0.15))
plt.show()
QRE-Implied Correlations
We begin by defining the functions that we will use in the main routine
# The following function creates data about the frequency.
def freq(DF):
df_foo = DF
freq_s = np.full([n, 1], np.nan) # Initialize matrices
avg = np.full([n, 3], np.nan) # Initialize matrices
for i in range(0, n):
freq_s[i] = sum(df_foo.cluster == i) # Compute empirical frequency of each cluster, i.e. mixed action of the sender
avg[i, 0] = df_foo[df_foo.cluster == i].mrdec.mean() # Compute empirical freq for receiver, i.e. mixed action of receiver
avg[i, 1] = df_foo[df_foo.cluster == i].mbdec.mean()
avg[i, 2] = df_foo[df_foo.cluster == i].mndec.mean()
# The empirical frequency of observing a message [j] coming from cluster [i] turning into a:
#Red guess
freq_r_RED = freq_s * Tot_prob * avg
#Blue guess
freq_r_BLUE = freq_s * Tot_prob * (1 - avg)
return (freq_s, freq_r_RED, freq_r_BLUE)
def MLE(DF, _lambda_):
#Rename dataframe with a temporary name
df_foo = DF
## Initialize empty matrices
PR = np.full([n, 3], np.nan)
PS = np.full([n, 1], np.nan)
# Compute expected payoffs
# RECEIVER
# Probability of guess Red given cluster i and message, computed with Logit model parameter _lambda_
PR = np.exp(_lambda_ * ER) / (np.exp(_lambda_ * ER) + np.exp(_lambda_ * (2 - ER))) # Colums: 0 msg red, 1 blue, 2 none
# SENDER
# Probability of sender using cluster i, computed with Logit model parameter _lambda_ and empirical ES.
PS = np.exp(_lambda_ * ES) / np.nansum(np.exp(_lambda_ * ES))
# Loglikelihoods: computes the loglikelihood. log(Pr(Red|r,cluster[i])) * Freq(r,cluster[i],Red)
LR = np.nansum((np.log(PR) * freq_r_RED + np.log(1 - PR) * freq_r_BLUE))
LS = np.nansum(freq_s * np.log(PS))
return (LS, LR)
# The following function is a "vectorized" version of MLE,
# It takes vectors of lambdas and outputs vectors of loglikelihoods
def MLE_vect(DF, lambdavector):
LS = np.full([np.size(L), 1], np.nan)
LR = np.full([np.size(L), 1], np.nan)
for i in range(0, np.size(L)):
LS[i], LR[i] = MLE(DF, lambdavector[i])
return LS, LR
# The following function maximizes the loglikelihood
def QRE(DF, lambdavector):
LS, LR = MLE_vect(DF, lambdavector)
lambdaS = L[np.argmax(LS)]
lambdaR = L[np.argmax(LR)]
return (lambdaS, lambdaR)
def load_all(match, treatments):
k = 1
for treatment in treatments:
DF = load_data(treatment, match, role = 'S')
DF = prepare_data(DF, level = '', method = '') # imputation method is irrelevant since this is is a full-commitment treatment, left blank
if k == 1:
DFcommon = DF
else:
DFcommon = pd.concat([DFcommon, DF])
del DF
k = 1 + k
return DFcommon
def datasetup_all(treatments, match, n, target_treatment):
DF = load_all(match, treatments)
DF = prepare_data(DF, level = '', method = '') # imputation method is irrelevant since this is is a full-commitment treatment, left blank
DF, centroids = clusters_full_commitment(DF, n)
DF = DF[DF.treatment == target_treatment]
# We next define matrices as function of clusters that will be used in QRE computation
# Declare space
Tot_prob = np.full([n, 3], np.nan)
Posterior = np.full([n, 3], np.nan)
ER = np.full([n, 3], np.nan)
ES = np.full([n, 1], np.nan)
for i in range(n):
for j in [0, 1, 2]: # [red blue none]
# Given cluster, computes the total prob of sending message r, m, n.
Tot_prob[i, j] = (1/3 * centroids[i, j] + 2/3 * centroids[i, j + 3] )/100
# Given cluster, computes the implied posterior conditional on receiving message r, m, n.
Posterior[i, j] = 1/3 * centroids[i, j] / Tot_prob[i, j] /100
# Given cluster, computes the implied posterior conditional on receiving message r, m, n.
ES[i] = 2 * (Tot_prob[i, 0] * DF[DF.cluster == i].mrdec + Tot_prob[i, 1] * DF[DF.cluster == i].mbdec + Tot_prob[i, 2] * DF[DF.cluster == i].mndec).mean()
# Expected payoff for the receiver if she guesses Red.
ER = 2 * Posterior
return DF, centroids, Tot_prob, Posterior, ES, ER
The following cell defines a function that simulates a dataframe with N observations
def DGP(df, lambdaS, lambdaR, N, ES, ER, Posterior, Tot_prob, AS):
n = len(AS[:, 0])
## Initialize empty matrices
PR = np.full([n, 3], np.nan)
PS = np.full([n, 1], np.nan)
exponential = np.full([n, 1], np.nan)
DF_sim = np.full([N, 6], np.nan)
# Compute objective expected payoffs
for i in range(n):
# RECEIVER
for message in [0, 1, 2]: # 0 means red, 1 means blue, 2 means none
# Logit form to compute probability of action R given action i and message m=[0,1,2]
PR[i, message] = np.exp(lambdaR * ER[i, message]) / (np.exp(lambdaR * ER[i, message]) + np.exp(lambdaR * (2 - ER[i, message])))
# SENDER
exponential[i] = np.exp(lambdaS * ES[i])
for i in range(n):
PS[i] = exponential[i] / (np.nansum(exponential))
# end of SENDER
theta = np.random.choice(2, size = N, p=[2/3, 1/3])
# Theta = 0 => blue # Theta = 1 => red
a_s = np.random.choice(n, size = N, p=np.nan_to_num(PS).T[0])
#####
# These next lines of code are a cumbersome way to generate random data using matrix algebra instead of a FOR loop.
# This makes the code much faster
# Generates variable message
temp0 = np.stack((AS[a_s, 3 * (1 - theta)], AS[a_s, 3 * (1 - theta) + 1], AS[a_s, 3 * (1 - theta) + 2])).T
temp1 = temp0.cumsum(axis=1)
temp2 = np.random.rand(len(temp1), 1)*100
m = (temp2 < temp1).argmax(axis=1)
# Generates variable mrdec mbdec mndec
d = {}
for message in [0, 1, 2]:
temp0 = np.stack((1 - PR[a_s, message], PR[a_s, message])).T
temp1 = temp0.cumsum(axis=1)
temp2 = np.random.rand(len(temp1), 1)
d["a_r{0}".format(message)] = (temp2 < temp1).argmax(axis=1)
####
# Let's put all together in a big matrix
DF_sim[:,0] = theta
DF_sim[:,1] = m
DF_sim[:,2] = a_s
DF_sim[:,3] = d["a_r0"]
DF_sim[:,4] = d["a_r1"]
DF_sim[:,5] = d["a_r2"]
# Translate matrix into a Pandas dataframe
DF_sim = pd.DataFrame(DF_sim, columns=['theta', 'message', 'cluster', 'mrdec', 'mbdec', 'mndec']).astype(int)
# Adds one particular realization of a "session"
DF_sim['decision'] = np.where(DF_sim['message']==0, DF_sim['mrdec'], 0 ) + np.where(DF_sim['message']==1, DF_sim['mbdec'], 0 ) + np.where(DF_sim['message']==2, DF_sim['mndec'], 0 )
DF_sim['pay_R'] = np.where(DF_sim.decision == DF_sim.theta, 2, 0)
DF_sim['pay_S'] = np.where(DF_sim.decision == 1, 2, 0)
corr = np.corrcoef(DF_sim.theta,DF_sim.decision)[0,1]
return (DF_sim, corr)
treatments = ['V100', 'U100']
n = 4 # Number of clusters
match = 16 # Focus on last 10 rounds
N = 10000 # N is the number of observations in the simulated dataframe (see DGP())
K = 30 # Run the same routine K times to integrate the uncertainty created by the starting point of the kMeans algorithm
L = np.linspace(0, 7.5, 200) # Space for the maximization of lambdaR and lambdaS
for treatment in treatments:
storeS = []
storeR = []
storeC_act = []
storeC_bay = []
for i in range(0, K): # Randomize over the seed, so that I eliminate the uncertainty created by the starting point of the clustering algorithm.
np.random.seed(i)
DF, centroids, Tot_prob, Posterior, ES, ER = datasetup_all([treatment], match, n, treatment)
(freq_s, freq_r_RED, freq_r_BLUE) = freq(DF)
LS, LR = QRE(DF, L)
# The following line simulates a dataframe assuming senders and receivers behave according to lambdaS and lambdaR
# From this dataframe we compute the correlation phi -- corr_actual
_, corr_actual = DGP(DF, LS, LR , N, ES, ER, Posterior, Tot_prob, centroids)
# The following line simulates a dataframe assuming that senders behaves according to lambdaS and that receivers is Bayesian
# It is convenient to approximate Bayesian behavior by setting lambdaR to an arbitrary high number
# From this dataframe we compute the bayesian correlation phi^B -- corr_bayesian
_, corr_bayesian = DGP(DF, LS, 50 , N, ES, ER, Posterior, Tot_prob, centroids)
storeS = np.append(storeS, LS)
storeR = np.append(storeR, LR)
storeC_act = np.append(storeC_act, corr_actual)
storeC_bay = np.append(storeC_bay, corr_bayesian)
print(treatment)
print("lambda_R: " + str(round(np.nanmean(storeR),2)) + str(" (footnote 37)"))
print("lambda_S: " + str(round(np.nanmean(storeS),2)) + str(" (footnote 37)"))
print("QRE-Implied Bayesian Correlation: " + str(round(np.nanmean(storeC_bay),3)))
print("QRE-Implied Actual Correlation: " + str(round(np.nanmean(storeC_act),2)))
print("")
Strategy Clusters and CDFs of Posterior Divergence $\psi^B$
np.random.seed(1)
treatment = 'U100H'
q = 3/4 # Persuasion threshold
n = 4 # Number of clusters
## This function computes the bayesian correlation for treatments whose persuasion threshold q is not necessarily 1/2
def correlation_bayesian_receiver_q(DF_foo, stage, q):
if stage == 'comm':
stage = ""
n11 = 1 / (3 * 100) * (DF_foo['{}rred'.format(stage)] * (DF_foo['{}post_red'.format(stage)] >= q) + DF_foo['{}rblue'.format(stage)] * (DF_foo['{}post_blue'.format(stage)] >= q) + DF_foo['{}rnone'.format(stage)] * (DF_foo['{}post_none'.format(stage)] >= q))
n10 = 1 / (3 * 100) * (DF_foo['{}rred'.format(stage)] * (DF_foo['{}post_red'.format(stage)] < q) + DF_foo['{}rblue'.format(stage)] * (DF_foo['{}post_blue'.format(stage)] < q) + DF_foo['{}rnone'.format(stage)] * (DF_foo['{}post_none'.format(stage)] < q) )
n00 = 2 / (3 * 100) * (DF_foo['{}bred'.format(stage)] * (DF_foo['{}post_red'.format(stage)] < q) + DF_foo['{}bblue'.format(stage)] * (DF_foo['{}post_blue'.format(stage)] < q) + DF_foo['{}bnone'.format(stage)] * (DF_foo['{}post_none'.format(stage)] < q))
n01 = 2 / (3 * 100) * (DF_foo['{}bred'.format(stage)] * (DF_foo['{}post_red'.format(stage)] >= q) + DF_foo['{}bblue'.format(stage)] * (DF_foo['{}post_blue'.format(stage)] >= q) + DF_foo['{}bnone'.format(stage)] * (DF_foo['{}post_none'.format(stage)] >= q))
phi = (n11 * n00 - n01 * n10) / np.sqrt((n00 + n01) * (n10 + n11) * (n11 + n01) * (n10 + n00))
return np.nan_to_num(phi)
DF = load_data(treatment, match = 16, role = 'S')
DF = prepare_data(DF, level = '', method = '') # imputation method is irrelevant since this is is a full-commitment treatment, left blank
DF, AS = clusters_full_commitment(DF, n)
DF["corr_agg"] = correlation_bayesian_receiver_q(DF, 'agg', q)
df = DF
AS = AS/100
relsize = (np.round((100*df.cluster.value_counts(sort = False)/df.cluster.size).values,1))
colz = ['orchid','b', 'g','r', 'orange', 'pink']
marz = ['*','s','D',"o","p","o"]
lab = relsize
plt.figure(figsize=(7,6))
plt.scatter(1, 5/6, c = 'white', s = 150, alpha = .75, edgecolors = 'black', marker = 'X', linewidth = 1, label = "Prediction, $\phi^B = 0.79$")
for i in [1,0,3, 2]:
plt.scatter(AS[i,0], AS[i,4], c = colz[i], s = 120, alpha = .75, edgecolors= colz[i], marker = marz[i], linewidth = 2, label = "Freq " + str(relsize[i]) +"\%, $\phi^B = $ " + str(np.round(df[df.cluster==i].corr_agg.mean(),2)))
plt.xlabel('$\pi_C(r|R)$', fontsize = 15)
plt.ylabel('$\pi_C(b|B)$', fontsize = 15)
plt.axis([-.01, 1.02, -.01, 1.01])
plt.xticks([0, .25, .50, .75,1.00])
plt.yticks([0, .25, .50, .75,1.00])
plt.title("Clusters for Treatment $" + treatment +"$", fontsize = 15)
plt.legend(loc = (0.05,-.3), fontsize = 12, ncol=2)
plt.show()
plt.figure(figsize = (7,6))
for treatment in ['U100', 'U100H']:
DF = load_data(treatment, match = 16, role = 'S')
DF = prepare_data(DF, '', '') # imputation method is irrelevant since this is is a full-commitment treatment, left blank
data = []
for i in DF.id.unique():
temp = DF[DF['id'] == i] ## Compute sender-specific
psi_B_temp = (np.nan_to_num(temp.aggpost_red)**2* temp.aggTotprob_red + np.nan_to_num(temp.aggpost_blue)**2* temp.aggTotprob_blue + np.nan_to_num(temp.aggpost_none**2) * temp.aggTotprob_none) - (1/3)**2
psi_B_temp = psi_B_temp/(1/3*2/3)
data = np.append(data, np.mean(psi_B_temp))
print(treatment)
print("Posterior Divergence $\psi^B$: " + str(np.round(np.mean(data),2)))
print("")
# Posterior Variance
N = data.size
Z = np.sort(data)
F = np.array(range(N)) / float(N)
shades = {'20': 1, '100H': 1, '100': .9}
face = {'20': 'white', '100H': 'white', '100': 'black'}
plt.plot(Z, F, label = "$"+treatment+"$", c = 'black', alpha = shades[treatment[1:]], marker = 'o', linewidth = .4, markerfacecolor = face[treatment[1:]], markeredgewidth=.5)
plt.ylabel('Percentile', fontsize = 15)
plt.xlabel('Posterior Divergence $\psi^B$ by Sender', fontsize = 15)
plt.title('CDF of Posterior Divergence $\psi^B$', fontsize = 15)
plt.yticks([0,.25,.5,.75,1])
plt.axis([-.025, 1.02, -0.02, 1.02])
plt.legend(loc = (0.2,-.2), fontsize = 15, ncol=2)
plt.text(0.1,-.325, '.', color='w', fontsize=14)
plt.show()
Senders’ clustered bheavior in U100 vs U100S
n = 4 ## Number of clusters
## Plot clusters for treatment U100S
colors = ['b','r', 'g','orange', 'orange', 'pink']
markers = ['s','o','D',"^","p","o"]
np.random.seed(0)
DF = load_data(treatment = 'U100S', match = 16, role = 'S')
DF = prepare_data(DF, level = '', method = '') # imputation method is irrelevant since this is is a full-commitment treatment, left blank
df, AS = clusters_full_commitment(DF, n)
df["corr_agg"] = correlation_bayesian_receiver(df, 'agg')
AS = AS/100
relsize = (np.round((100*df.cluster.value_counts(sort = False)/df.cluster.size).values,1))
lab = relsize
plt.figure(figsize=(7,6))
for i in [0,3,1, 2]:
plt.scatter(AS[i,0], AS[i,4], c = colors[i], s = 120, alpha = .75, edgecolors= colors[i], marker = markers[i], linewidth = 2, label = "Freq " + str(relsize[i]) +"\%, $\phi^B = $ " + str(np.round(df[df.cluster==i].corr_agg.mean(),2)))
plt.xlabel('$\pi_C(r|R)$', fontsize = 15)
plt.ylabel('$\pi_C(b|B)$', fontsize = 15)
plt.axis([-.01, 1.01, -.01, 1.01])
plt.xticks([0, .25, .50, .75,1.00])
plt.yticks([0, .25, .50, .75,1.00])
plt.title("Clusters in Treatment $U100S$ (solid) and $U100$ (hollow)", fontsize = 15)
## Plot clusters for treatment U100
colors = ['b','orange', 'r','g', 'orange', 'pink']
markers = ['s','^','o',"D","p","o"]
np.random.seed(0)
DF = load_data('U100', match = 16, role = 'S')
DF = prepare_data(DF, level = '', method = '') # imputation method is irrelevant since this is is a full-commitment treatment, left blank
df, AS = clusters_full_commitment(DF, n)
df["corr_agg"] = correlation_bayesian_receiver(df, 'agg')
AS = AS/100
relsize = (np.round((100*df.cluster.value_counts(sort = False)/df.cluster.size).values,1))
for i in [ 0, 1, 2 , 3 ]:
plt.scatter(AS[i,0], AS[i,4], c = 'white', s = 120, alpha = .75, edgecolors= colors[i], marker = markers[i], linewidth = 2, label = "Freq " + str(relsize[i]) +"\%, $\phi^B = $ " + str(np.round(df[df.cluster==i].corr_agg.mean(),2)))
plt.legend(loc = (0.05,-.35), fontsize = 12, ncol=2)
plt.show()
How receivers' behavior compares in U100 vs U100S
## The following function creates a dataframe with the average empirical probability that a receiver guesses Red if the Bayesian posterior induced by the sender belongs to a certain bin.
n = 4 ## Number of "bins" in the posterior belief [0,1]
k = 1
def reshape_average_guess(treatment):
store_df = pd.DataFrame(index=range(n), columns=['a'])
DF = load_data(treatment, match = 16, role = 'S')
DF = prepare_data(DF, '', '') # imputation method is irrelevant since this is is a full-commitment treatment, left blank
post = np.array([])
dec = np.array([])
msg = np.array([])
post = np.append(post, DF.aggpost_red.values)
post = np.append(post, DF.aggpost_blue.values)
post = np.append(post, DF.aggpost_none.values)
dec = np.append(dec, DF.mrdec.values)
dec = np.append(dec, DF.mbdec.values)
dec = np.append(dec, DF.mndec.values)
msg = np.append(msg, 0 * np.ones(DF.mrdec.size))
msg = np.append(msg, 1 * np.ones(DF.mrdec.size))
msg = np.append(msg, 2 * np.ones(DF.mrdec.size))
temp = np.isnan(post) == False
dec = dec[temp]
post = post[temp]
msg = msg[temp]
post = np.where(post > 1, 1, post)
temp_df = pd.DataFrame(sorted(zip(post,dec, msg)), columns=('post', 'dec', 'msg'))
temp = pd.cut(temp_df.post, n)
temp_df['new'] = temp
clusters = temp.unique()
temp_df['bin'] = -10
for i in range(temp_df.post.size):
temp_df.loc[i, 'bin'] = np.argmax(clusters == temp_df.new[i])
temp_df['counter'] = 1
grouped_bin = temp_df.groupby(temp_df.bin)
grouped_bin_and_msg = temp_df.groupby([temp_df.bin, temp_df.msg])
temp_df['freq_post'] = grouped_bin['counter'].transform(lambda x: x.sum())
temp_df['mean_dec_per_msg'] = grouped_bin_and_msg['dec'].transform(lambda x: x.mean())
temp_df['freq_post_per_msg'] = grouped_bin_and_msg['counter'].transform(lambda x: x.sum())
temp_df['mean_dec'] = grouped_bin['dec'].transform(lambda x: x.mean())
df = grouped_bin_and_msg.mean()
df['msg'] = df.index.get_level_values('msg')
df['bin'] = df.index.get_level_values('bin')
df = df.reset_index(drop=True)
store_df['freq_post'] = df.pivot(index='bin', columns='msg', values=('freq_post'))[1.0]
store_df['mean_dec'] = df.pivot(index='bin', columns='msg', values=('mean_dec'))[1.0]
store_df['mean_dec_r'] = df.pivot(index='bin', columns='msg', values=('mean_dec_per_msg'))[0.0]
store_df['mean_dec_b'] = df.pivot(index='bin', columns='msg', values=('mean_dec_per_msg'))[1.0]
if treatment != 'U100S':
store_df['mean_dec_n'] = df.pivot(index='bin', columns='msg', values=('mean_dec_per_msg'))[2.0]
store_df['post_freq_r'] = df.pivot(index='bin', columns='msg', values=('freq_post_per_msg'))[0.0]
store_df['post_freq_b'] = df.pivot(index='bin', columns='msg', values=('freq_post_per_msg'))[1.0]
if treatment != 'U100S':
store_df['post_freq_n'] = df.pivot(index='bin', columns='msg', values=('freq_post_per_msg'))[2.0]
# store_df['treatment'] = treatment
store_df.reset_index(inplace=True)
## PLOTTING
store_df['label']= ' '
store_df.loc[0, 'label'] = '[0.00, 0.25]'
store_df.loc[1, 'label'] = '(0.25, 0.50]'
store_df.loc[2, 'label'] = '(0.50, 0.75]'
store_df.loc[3, 'label'] = '(0.75, 1.00]'
# store_df.loc[4, 'label'] = '(0.80, 1.00]'
return store_df
########
# PLOT #
########
plt.figure(figsize=(7,6))
store_df = reshape_average_guess('U100')
plt.axis([-.03,3.03,-0.0,1])
plt.xticks(range(len(store_df.index)), store_df.label)
plt.xlabel('$\mu(\pi_C,r)$: Posterior Induced by $\pi_C$ Conditional on $r$', fontsize = 15)
plt.ylabel('Frequency of $a=red$', fontsize = 15)
plt.yticks([0,.25,.5,.75,1])
plt.title("Receivers Responsiveness in $U100$ and $U100S$", fontsize=15)
store_df = reshape_average_guess('U100')
plt.scatter(store_df.index, store_df.mean_dec_r, alpha = 1, marker = 's', color = 'gray', edgecolors = 'gray', linewidths = 2, label = '$U100$')
plt.plot(store_df.index, store_df.mean_dec_r, alpha = .2, color = 'gray', label='_nolegend_')
store_df = reshape_average_guess('U100S')
plt.scatter(store_df.index, store_df.mean_dec_r, alpha = 1, color = 'white', edgecolors = 'k', linewidths = 2, label = '$U100S$')
plt.plot(store_df.index, store_df.mean_dec_r, alpha = .2, color = 'k', label='_nolegend_')
plt.legend(loc = (0,.85), prop={'size': 15})
plt.text(0.1,-.33, '.', color='w', fontsize=14)
plt.show()
Probability of Guessing Red by Posterior and Message
Before running this code, run all the codes in the section titled "Replicate Figure 6"
import warnings
warnings.simplefilter('ignore')
# We silence the warnings. Warnings are produced because a few receivers always guess blue.
# For them, the estimation procedure outputs a warning.
# Despite the warning, however, the estimated coefficient is "correct" in that the estimated probability
# that these receivers guess red for any posterior x is zero, as it should be.
import statsmodels.discrete.discrete_model as sm
from scipy.stats import norm
rangex = np.linspace(0, 1, 50)
plt.figure(figsize = (8,6))
for treatment in ['U100S']:
paystore = []
paystore_pred = []
for x in rangex: ## For loop a relevant class of strategies parameterized by x (see main draft)
strategy = {'U100S' : [1, 0, 0, 1 - x, x, 0]}
DF = load_data(treatment, 16, 'R') ## Select the database of receivers
DF = prepare_data(DF, '', '') # imputation method is irrelevant since this is is a full-commitment treatment, left blank
df = reshape_data(DF)
df['id'] = df.id.astype('int')
phistore = []
ESstore = []
for id in df.id.unique(): ## For every receiver
coeff = probit(verbose = 'No', id = id) ## Estimate probit coefficients for each receiver
avg_response = {'red': yhat(posterior(strategy[treatment])[0], 'red', coeff),
'blue': yhat(posterior(strategy[treatment])[1], 'blue', coeff),
'none': yhat(posterior(strategy[treatment])[2], 'none', coeff) }
ES = expectedpayoff(strategy, treatment, avg_response)
ESstore = np.append(ESstore, ES)
paystore = np.append(paystore, np.median(ESstore)) # Median
## Compute predictions: receiver's best respond to strategy(x); compute sender's expected payoff
temp = np.array(posterior(strategy[treatment])) # Posteriors that strategy(x) induces
## receiver's best response, breaking ties in favor of sender
avg_response_pred = {'red': (temp >=.5)[0],
'blue': (temp >=.5)[1],
'none': (temp >=.5)[2]}
## Next line computes sender's predicted expected payoff (2 dollars times probability of guessing red)
payoff_pred = 2 * ((1/3 * strategy[treatment][0] + 2/3 * strategy[treatment][3]) * np.nan_to_num(avg_response_pred['red']) +
(1/3 * strategy[treatment][1] + 2/3 * strategy[treatment][4])* np.nan_to_num(avg_response_pred['blue']) +
(1/3 * strategy[treatment][2] + 2/3 * strategy[treatment][5])* np.nan_to_num(avg_response_pred['none']))
# store payoff_pred
paystore_pred = np.append(paystore_pred, payoff_pred)
## --> end of x loop <--
## Plot and format the looks:
if treatment == "U100S":
plt.plot(rangex, paystore[:] , c = 'black', marker='o', markevery=3, markeredgecolor='black', markeredgewidth = 1, markerfacecolor='white', markersize=8, linestyle = "dashed", alpha = 1, label = "$" + treatment + "$")
plt.scatter(rangex, paystore_pred[:] , c = 'black', s=13, alpha = .75, marker='o',label = "Predictions")
plt.xlabel("$x$ -- Parametrized Sender's Strategy", fontsize = 15)
plt.ylabel("Sender's Expected Payoff", fontsize = 15)
plt.legend(loc = 2, prop = {'size' : 15})
plt.axis([0,1,-0.01,1.35])
plt.yticks([0,.25,.5,.75,1,1.25])
plt.xticks([0,.25,.5,.75,1])
plt.show()
Receiver’s Response to Persuasive Messages: $\rho = 0.20$ vs. $\rho = 0.80$
from scipy.optimize import curve_fit
## Define polynomial function of degree 2, will be used to fit the data
def quadratic_function(x, a, b, c):
return a + b * x + c * x ** 2
plt.figure(figsize=(15,6))
for rule in ['U', 'V']: # Compare Unverifiable and Verifiable information in two separate subplots
plt.subplot(1,2, 1 if rule == 'U' else 2)
for treatment in [rule + '20', rule + '80']: # Compare treatments \rho=0.2 and \rho=1 in the same subplot
msg = 'none' if rule == 'V' else 'red' # Choose the "persuasive" message (see main draft): red for U and none for V
storem = []
storeh = []
storel = []
DF = load_data(treatment, match = 16, role = 'S')
DF = prepare_data(DF, 'session', 'Strong')
# The next few lines use the variables "post_red" and "post_none", which have been defined in "prepare_data" (see preamble)
DF = DF[np.isnan(DF['post_' + msg]) == False] # Drop observations for which the persuasive msg has probability zero
temp = DF.sort_values('post_' + msg, ascending = True) # Generate a temporary dataframe "temp" ordered by post_msg
x = temp['post_' + msg].values # Generate a vector with post_msg
y = temp['m'+ msg[0] + 'dec'] # Generate a vector with receiver's guess to persuasive message
## Estimate Quadratic Fit
params, covar = curve_fit(quadratic_function, x, y, bounds = [0,1])
## Simulate 10'000 times to generate confidence bounds
rangex = np.linspace(0,1,100) # Range of posterior beliefs
temp_par = np.random.multivariate_normal(params, covar, 10000) ## Generate 10'000 parameters from estimation above
a, b, c = temp_par[:,0], temp_par[:,1], temp_par[:,2]
for x in rangex:
temp = quadratic_function(x, a, b, c)
tempstorem, tempstoreh, tempstorel = np.percentile(temp, 50), np.percentile(temp, 95), np.percentile(temp, 5)
storeh = np.append(storeh, tempstoreh)
storel = np.append(storel, tempstorel)
storem = np.append(storem, tempstorem)
## Format the looks of the plot
plt.plot(rangex, storem, '--' if treatment[1:]!='20' else '-', alpha =.7, c = 'black', linewidth = 2, label = '$' + treatment + "$")
plt.plot(rangex, storel, '--' if treatment[1:]!='20' else '-', alpha =.35, c = 'black', linewidth = 1)
plt.plot(rangex, storeh, '--' if treatment[1:]!='20' else '-', alpha =.35, c = 'black', linewidth = 1)
string = 'r' if rule == 'U' else 'n'
plt.xlabel('Interim Posterior Conditional on $m=' + string +'$', fontsize = 15)
plt.axis([0,1,0,1])
plt.ylabel('Frequency of $a=red$', fontsize = 15)
plt.title('Unverifiable Information' if treatment[0] == 'U' else 'Verifiable Information', fontsize = 15)
plt.legend(loc = 2, fontsize = 14)
plt.show()
k-Means – Representative Strategies in Treatments with Full Commitment
n = 4
## Figure in the Left panel
np.random.seed(0)
treatment = 'V100'
DF = load_data(treatment, match = 16, role = 'S')
DF = prepare_data(DF, level = '', method = '') # imputation method is irrelevant since this is is a full-commitment treatment, left blank
DF, AS = clusters_full_commitment(DF, n)
DF["corr_agg"] = correlation_bayesian_receiver(DF, 'agg')
df = DF
AS = AS/100
relsize = (np.round((100*df.cluster.value_counts(sort = False)/df.cluster.size).values,1))
colz = ['b','r', 'orchid','orange', 'orange', 'pink']
marz = ['s','o','*',"^","p","o"]
lab = relsize
fig = plt.figure(figsize=(7,6))
plt.scatter(0.01, 1/2, c = 'white', s = 150, alpha = .75, edgecolors = 'black', marker = 'X', linewidth = 1, label = "Prediction, $\phi^B = 0.79$")
for i in [2,0, 1, 3]:
plt.scatter(AS[i,0], AS[i,4], c = colz[i], s = 120 if i!=0 else 200, alpha = .75, edgecolors= colz[i], marker = marz[i], linewidth = 2, label = "Freq " + str(relsize[i]) +"\%, $\phi^B = $ " + str(np.round(df[df.cluster==i].corr_agg.mean(),2)))
plt.xlabel('$\pi_C(r|R)$', fontsize = 15)
plt.ylabel('$\pi_C(b|B)$', fontsize = 15)
plt.axis([-.01, 1.01, -.01, 1.01])
plt.xticks([0, .25, .50, .75,1.00])
plt.yticks([0, .25, .50, .75,1.00])
plt.title("Treatment $" + treatment +"$", fontsize = 15)
plt.legend(loc = (0.05,-.3), fontsize = 12, ncol=2)
plt.show()
## Figure in the Right panel
np.random.seed(0)
treatment = 'U100'
DF = load_data(treatment, match = 16, role = 'S')
DF = prepare_data(DF, level = '', method = '') # imputation method is irrelevant since this is is a full-commitment treatment, left blank
DF, AS = clusters_full_commitment(DF, n)
DF["corr_agg"] = correlation_bayesian_receiver(DF, 'agg')
df = DF
AS = AS/100
relsize = (np.round((100*df.cluster.value_counts(sort = False)/df.cluster.size).values,1))
colz = ['b','orange', 'r','g', 'orange', 'pink']
marz = ['s','^','o',"D","p","o"]
lab = relsize
fig = plt.figure(figsize=(7,6))
plt.scatter(.99, 1/2, c = 'white', s = 150, alpha = .75, edgecolors = 'black', marker = 'X', linewidth = 1, label = "Prediction, $\phi^B = 0.79$")
for i in [0, 1, 2, 3]:
plt.scatter(AS[i,0], AS[i,4], c = colz[i], s = 120, alpha = .75, edgecolors= colz[i], marker = marz[i], linewidth = 2, label = "Freq " + str(relsize[i]) +"\%, $\phi^B = $ " + str(np.round(df[df.cluster==i].corr_agg.mean(),2)))
plt.xlabel('$\pi_C(r|R)$', fontsize = 15)
plt.ylabel('$\pi_C(b|B)$', fontsize = 15)
plt.axis([-.01, 1.01, -.01, 1.01])
plt.xticks([0, .25, .50, .75,1.00])
plt.yticks([0, .25, .50, .75,1.00])
plt.title("Treatment $" + treatment +"$", fontsize = 15)
plt.legend(loc = (0.05,-.3), fontsize = 12, ncol=2)
plt.show()
for treatment in ["U20", "V20"]:
DF = load_data(treatment, match = 16, role = 'S')
DF = prepare_data(DF, level = 'session', method = 'Strong')
print(treatment)
print(np.round(DF.revrred.mean()/100,2))
print(np.round(DF.revrblue.mean()/100,2))
print(np.round(DF.revrnone.mean()/100,2))
print(np.round(DF.revbred.mean()/100,2))
print(np.round(DF.revbblue.mean()/100,2))
print(np.round(DF.revbnone.mean()/100,2))
print("")
pd.set_option('mode.chained_assignment', None)
for treatment in ["U20", "U100"]:
DF = load_data(treatment, match = 16, role = 'S')
DF = prepare_data(DF, 'subject', 'Weak')
## Focus attention on a subset of strategies as explained in Section D.6
df = DF[(DF.rred >= 95) & (DF.bblue>= 95)]
stage = "agg"
## Compute matrix P
df["thetaR_aR"] = 1 / (3 * 100) * (df['{}rred'.format(stage)] * (df['mrdec']) + df['{}rblue'.format(stage)] * (df['mbdec']) + df['{}rnone'.format(stage)] * (df['mndec']))
df["thetaR_aB"] = 1 / (3 * 100) * (df['{}rred'.format(stage)] * (1 - df['mrdec']) + df['{}rblue'.format(stage)] * (1 - df['mbdec']) + df['{}rnone'.format(stage)] * (1 - df['mndec']))
df["thetaB_aB"] = 2 / (3 * 100) * (df['{}bred'.format(stage)] * (1 - df['mrdec']) + df['{}bblue'.format(stage)] * (1 - df['mbdec']) + df['{}bnone'.format(stage)] * (1 - df['mndec']))
df["thetaB_aR"] = 2 / (3 * 100) * (df['{}bred'.format(stage)] * (df['mrdec']) + df['{}bblue'.format(stage)] * (df['mbdec']) + df['{}bnone'.format(stage)] * (df['mndec']))
print(treatment)
print(np.round(df.thetaR_aR.mean(),2), np.round(df.thetaR_aB.mean(),2))
print(np.round(df.thetaB_aR.mean(),2), np.round(df.thetaB_aB.mean(),2))
print("")
These numbers include the show up fee of 10 dollars.
#### print("In reference to Section 3.1")
DF = pd.read_stata(r'data.dta')
DF = DF[DF.match == 25]
DF = DF[(DF.u100s == 0) & (DF.u100h == 0)]
DF.wealth.mean()
print('Average = ' + str(10 + np.round(DF.wealth.mean(),2)))
print('Max = ' + str(10 + np.max(DF.wealth)))
print('Min = ' + str(10 + np.min(DF.wealth)))
print("")
print("In reference to Section B.1")
DF = pd.read_stata(r'data.dta')
DF = DF[DF.match == 25]
DF = DF[(DF.u100h == 1)]
DF.wealth.mean()
print('Average = ' + str(10 + np.round(DF.wealth.mean(),2)))
print('Max = ' + str(10 + np.max(DF.wealth)))
print('Min = ' + str(10 + np.min(DF.wealth)))
print("")
print("In reference to Section B.2")
DF = pd.read_stata(r'data.dta')
DF = DF[DF.match == 25]
DF = DF[(DF.u100s == 1)]
DF.wealth.mean()
print('Average = ' + str(10 + np.round(DF.wealth.mean(),2)))
print('Max = ' + str(10 + np.max(DF.wealth)))
print('Min = ' + str(10 + np.min(DF.wealth)))