Description of activity: http://granite.ices.utexas.edu/coursewiki/index.php/Chess_contingency_tables
import collections
import numpy as np
import scipy
import scipy.stats
class Game(object):
"""Represents the important bits of a single game."""
def __init__(self, description):
self.move1, self.move2, self.outcome = description.strip().split()
if len(self.move1) == 2:
self.p1_piece = 'pawn'
else:
self.p1_piece = self.move1[0]
# awful copy/paste
if len(self.move2) == 2:
self.p2_piece = 'pawn'
else:
self.p2_piece = self.move2[0]
def iter_games():
"""Iterate over all games."""
for game_line in open('data/game_data.txt'):
yield Game(game_line)
class HtmlObject(object):
"""An object that can be displayed as HTML."""
def __init__(self, html):
self.html = html
def _repr_html_(self):
return self.html
class ContingencyTable(object):
"""A generic contingency table."""
def __init__(self, row_names, col_names, counts):
assert counts.shape == (len(row_names), len(col_names))
self.row_names = row_names
self.col_names = col_names
self.counts = counts
def get_chisq_stat(self):
"""Compute the chi-squared test statistic."""
expected = self.get_expected_counts()
return np.sum((self.counts - expected)**2 / expected)
def get_degrees_of_freedom(self):
"""Get the number of degrees of freedom for the chi-squared distribution."""
return np.product(self.counts.shape) - np.sum(self.counts.shape) + 1
def get_expected_counts(self):
"""Get a matric of the expected counts under the null hypothesis."""
expected = np.zeros(self.counts.shape)
for i in xrange(expected.shape[0]):
for j in xrange(expected.shape[1]):
n_i_dot = self.marginal(i, '.')
n_j_dot = self.marginal('.', j)
expected[i, j] = n_i_dot * n_j_dot
expected = expected / self.marginal('.', '.')
return expected
def get_p_value(self):
"""Compute a p-value."""
chisq_stat = self.get_chisq_stat()
dof = self.get_degrees_of_freedom()
return 1.0 - scipy.stats.chi2.cdf(chisq_stat, dof)
def marginal(self, i, j):
"""Compute a marginal (sum of row/column). Each argument can be either
an index or a '.'"""
return self._marginal(i, j, self.counts)
def to_null_hypothesis_html(self):
"""Get a table in HTML format showing the expected counts."""
expected = self.get_expected_counts()
return HtmlObject(
self._to_html(expected, caption='Expected Counts', summary=False))
def to_residuals_html(self):
residuals = self.counts - self.get_expected_counts()
return HtmlObject(
self._to_html(residuals, caption='Residuals (Counts - Expected)',
summary=False, marginals=False))
def to_residuals_normalized_html(self):
normed = (self.counts - self.get_expected_counts()) / self.get_expected_counts()
return HtmlObject(
self._to_html(normed, caption='(Counts - Expected) / Expected',
summary=False, marginals=False))
def to_row_probs_html(self):
probs = table.counts / np.tile(table.counts.sum(axis=1), (3, 1)).T
return HtmlObject(
self._to_html(probs, caption='Outcome Probabilities',
summary=False, marginals=False))
def _marginal(self, i, j, cell_values):
i_dot = i == '.'
j_dot = j == '.'
assert i_dot or j_dot
if i_dot and j_dot:
return cell_values.sum()
elif i_dot:
return cell_values[:, j].sum()
elif j_dot:
return cell_values[i].sum()
def _to_html(self, cell_values, caption='Counts', summary=True, marginals=True):
lines = ['<table>']
lines.append('<caption>{0}</caption>'.format(caption))
def start_row():
lines.append('<tr>')
def end_row():
lines.append('</tr>')
def cell(contents):
lines.append('<td>{0}</td>'.format(str(contents)))
# Column names
start_row()
cell('')
for col_name in self.col_names:
cell(col_name)
if marginals:
cell('')
end_row()
# Rows
for i, row_name in enumerate(self.row_names):
start_row()
cell(row_name)
for j in xrange(len(self.col_names)):
cell(cell_values[i, j])
if marginals:
cell(self._marginal(i, '.', cell_values))
end_row()
if marginals:
start_row()
cell('')
for j in xrange(len(self.col_names)):
cell(self._marginal('.', j, cell_values))
cell(self._marginal('.', '.', cell_values))
end_row()
lines.append('</table>')
if summary:
chi2 = self.get_chisq_stat()
df = self.get_degrees_of_freedom()
s = 's' if df > 1 else ''
pval = self.get_p_value()
fmt = "$\\chi^2 = {0}$. {1} degree{2} of freedom. P-value {3}."
lines.append(fmt.format(chi2, df, s, pval))
return '\n'.join(lines)
def _repr_html_(self):
return self._to_html(self.counts)
Is there a significant association between the type of piece white chooses to move first and the outcome of the game?
# Create a contingency table from the data.
counts = np.zeros((2, 3))
for game in iter_games():
row = 0
if game.p1_piece == 'pawn':
row = 1
col = 0
if game.outcome == 'B':
col = 1
elif game.outcome == 'D':
col = 2
counts[row, col] += 1
table = ContingencyTable(('P1 Moves Knight', 'P1 Moves Pawn'),
('White Wins', 'Black Wins', 'Draw'),
counts)
table
White Wins | Black Wins | Draw | ||
P1 Moves Knight | 3259.0 | 2319.0 | 3386.0 | 8964.0 |
P1 Moves Pawn | 35869.0 | 27459.0 | 27708.0 | 91036.0 |
39128.0 | 29778.0 | 31094.0 | 100000.0 |
With a p-value of approximately zero, there is definitely some significant association here, but the p-value along doesn't tell us which move is better for which player.
We can compare the actual counts in the tables to the expected counts from the null hypothesis to see what's going on.
table.to_residuals_html()
White Wins | Black Wins | Draw | |
P1 Moves Knight | -248.43392 | -350.29992 | 598.73384 |
P1 Moves Pawn | 248.43392 | 350.29992 | -598.73384 |
So if P1 moves a pawn first, both players are more likely to win than if a knight was moved, but the black player gains more than the white. However, this also means that there are fewer draws.
We can also look at the percentage increase/decrease in the number of events above/below the expected values under the null hypothesis:
table.to_residuals_normalized_html()
White Wins | Black Wins | Draw | |
P1 Moves Knight | -0.0708306772605 | -0.131232881467 | 0.214810429155 |
P1 Moves Pawn | 0.00697445176593 | 0.0129220478653 | -0.0211516398672 |
And the observed probability of each outcome:
table.to_row_probs_html()
White Wins | Black Wins | Draw | |
P1 Moves Knight | 0.363565372602 | 0.258701472557 | 0.377733154842 |
P1 Moves Pawn | 0.394008963487 | 0.301627927413 | 0.3043631091 |
Is there a significant association between the pair of opening moves taken together and the outcome of the game?
opening_move_pairs = collections.Counter()
for game in iter_games():
move_pair = game.move1 + '-' + game.move2
opening_move_pairs[move_pair] += 1
print len(opening_move_pairs), 'unique opening pairs'
# Ignore move pairs that didn't happen very frequently.
threshold = 10
opening_move_pairs = sorted([pair for pair in opening_move_pairs if opening_move_pairs[pair] >= threshold])
179 unique opening pairs
index = dict((move_pair, i) for i, move_pair in enumerate(opening_move_pairs))
counts = np.zeros((len(index), 3))
for game in iter_games():
pair = game.move1 + '-' + game.move2
if pair not in index:
# Skip game whose move pair was not very common.
continue
row = index[pair]
col = 0
if game.outcome == 'B':
col = 1
elif game.outcome == 'D':
col = 2
counts[row, col] += 1
table = ContingencyTable(opening_move_pairs, ('White', 'Black', 'Draw'), counts)
table
White | Black | Draw | ||
Nc3-Nf6 | 12.0 | 4.0 | 6.0 | 22.0 |
Nc3-c5 | 10.0 | 23.0 | 4.0 | 37.0 |
Nc3-d5 | 42.0 | 37.0 | 16.0 | 95.0 |
Nc3-e5 | 10.0 | 7.0 | 7.0 | 24.0 |
Nc3-g6 | 6.0 | 10.0 | 1.0 | 17.0 |
Nf3-Nc6 | 36.0 | 31.0 | 23.0 | 90.0 |
Nf3-Nf6 | 1526.0 | 1046.0 | 1720.0 | 4292.0 |
Nf3-b5 | 8.0 | 7.0 | 5.0 | 20.0 |
Nf3-b6 | 14.0 | 16.0 | 8.0 | 38.0 |
Nf3-c5 | 336.0 | 267.0 | 381.0 | 984.0 |
Nf3-c6 | 5.0 | 4.0 | 6.0 | 15.0 |
Nf3-d5 | 741.0 | 503.0 | 762.0 | 2006.0 |
Nf3-d6 | 101.0 | 67.0 | 98.0 | 266.0 |
Nf3-e6 | 64.0 | 46.0 | 68.0 | 178.0 |
Nf3-f5 | 137.0 | 87.0 | 86.0 | 310.0 |
Nf3-g6 | 204.0 | 148.0 | 192.0 | 544.0 |
b3-Nf6 | 26.0 | 22.0 | 38.0 | 86.0 |
b3-b6 | 5.0 | 3.0 | 5.0 | 13.0 |
b3-c5 | 8.0 | 10.0 | 4.0 | 22.0 |
b3-d5 | 42.0 | 37.0 | 34.0 | 113.0 |
b3-e5 | 83.0 | 72.0 | 41.0 | 196.0 |
b4-Nf6 | 15.0 | 11.0 | 11.0 | 37.0 |
b4-a5 | 13.0 | 5.0 | 5.0 | 23.0 |
b4-c6 | 14.0 | 5.0 | 8.0 | 27.0 |
b4-d5 | 22.0 | 32.0 | 18.0 | 72.0 |
b4-e5 | 88.0 | 59.0 | 35.0 | 182.0 |
b4-e6 | 10.0 | 9.0 | 9.0 | 28.0 |
b4-f5 | 7.0 | 1.0 | 4.0 | 12.0 |
c4-Nc6 | 6.0 | 9.0 | 10.0 | 25.0 |
c4-Nf6 | 1135.0 | 723.0 | 923.0 | 2781.0 |
c4-b6 | 21.0 | 29.0 | 27.0 | 77.0 |
c4-c5 | 320.0 | 215.0 | 399.0 | 934.0 |
c4-c6 | 144.0 | 91.0 | 167.0 | 402.0 |
c4-d5 | 9.0 | 3.0 | 1.0 | 13.0 |
c4-d6 | 23.0 | 14.0 | 9.0 | 46.0 |
c4-e5 | 707.0 | 503.0 | 540.0 | 1750.0 |
c4-e6 | 379.0 | 253.0 | 405.0 | 1037.0 |
c4-f5 | 130.0 | 91.0 | 104.0 | 325.0 |
c4-g5 | 9.0 | 5.0 | 1.0 | 15.0 |
c4-g6 | 299.0 | 243.0 | 304.0 | 846.0 |
d3-d5 | 6.0 | 8.0 | 3.0 | 17.0 |
d4-Nc6 | 41.0 | 26.0 | 25.0 | 92.0 |
d4-Nf6 | 8345.0 | 6268.0 | 7146.0 | 21759.0 |
d4-b5 | 18.0 | 16.0 | 4.0 | 38.0 |
d4-b6 | 19.0 | 13.0 | 6.0 | 38.0 |
d4-c5 | 198.0 | 115.0 | 122.0 | 435.0 |
d4-c6 | 37.0 | 20.0 | 15.0 | 72.0 |
d4-d5 | 3274.0 | 2066.0 | 2527.0 | 7867.0 |
d4-d6 | 306.0 | 228.0 | 274.0 | 808.0 |
d4-e5 | 14.0 | 13.0 | 2.0 | 29.0 |
d4-e6 | 613.0 | 380.0 | 508.0 | 1501.0 |
d4-f5 | 495.0 | 365.0 | 326.0 | 1186.0 |
d4-g6 | 406.0 | 321.0 | 260.0 | 987.0 |
e3-Nf6 | 5.0 | 2.0 | 5.0 | 12.0 |
e3-d5 | 4.0 | 4.0 | 2.0 | 10.0 |
e3-e5 | 7.0 | 7.0 | 2.0 | 16.0 |
e3-g6 | 9.0 | 7.0 | 2.0 | 18.0 |
e4-Nc6 | 138.0 | 141.0 | 88.0 | 367.0 |
e4-Nf6 | 588.0 | 446.0 | 353.0 | 1387.0 |
e4-a6 | 13.0 | 13.0 | 5.0 | 31.0 |
e4-b6 | 45.0 | 27.0 | 16.0 | 88.0 |
e4-c5 | 6716.0 | 6101.0 | 5367.0 | 18184.0 |
e4-c6 | 1210.0 | 864.0 | 1012.0 | 3086.0 |
e4-d5 | 443.0 | 302.0 | 247.0 | 992.0 |
e4-d6 | 766.0 | 609.0 | 505.0 | 1880.0 |
e4-e5 | 4804.0 | 3587.0 | 3156.0 | 11547.0 |
e4-e6 | 2540.0 | 1917.0 | 1664.0 | 6121.0 |
e4-g6 | 583.0 | 495.0 | 435.0 | 1513.0 |
f4-Nf6 | 38.0 | 38.0 | 30.0 | 106.0 |
f4-c5 | 20.0 | 25.0 | 13.0 | 58.0 |
f4-d5 | 109.0 | 115.0 | 57.0 | 281.0 |
f4-e5 | 37.0 | 54.0 | 17.0 | 108.0 |
f4-e6 | 12.0 | 11.0 | 1.0 | 24.0 |
f4-g6 | 11.0 | 21.0 | 14.0 | 46.0 |
g3-Nf6 | 48.0 | 32.0 | 54.0 | 134.0 |
g3-c5 | 18.0 | 32.0 | 32.0 | 82.0 |
g3-d5 | 94.0 | 88.0 | 101.0 | 283.0 |
g3-e5 | 59.0 | 55.0 | 55.0 | 169.0 |
g3-f5 | 17.0 | 8.0 | 8.0 | 33.0 |
g3-g6 | 74.0 | 52.0 | 79.0 | 205.0 |
g4-d5 | 58.0 | 22.0 | 12.0 | 92.0 |
g4-e5 | 14.0 | 6.0 | 3.0 | 23.0 |
h3-d5 | 5.0 | 8.0 | 2.0 | 15.0 |
h3-e5 | 9.0 | 7.0 | 3.0 | 19.0 |
39033.0 | 29683.0 | 31043.0 | 99759.0 |
Again, we get a p-value of approximately 0, so there's definitely something interesting happening.
Looking at the percent increases/decreases in outcome rates:
table.to_residuals_normalized_html()
White | Black | Draw | |
Nc3-Nf6 | 0.394051187457 | -0.388943166122 | -0.123570531199 |
Nc3-c5 | -0.309253916125 | 1.08915376988 | -0.652586516872 |
Nc3-d5 | 0.129915172991 | 0.308948059939 | -0.458766363126 |
Nc3-e5 | 0.0649002126406 | -0.0197629956541 | -0.062707373643 |
Nc3-g6 | -0.0979668787044 | 0.976948580193 | -0.810966193004 |
Nf3-Nc6 | 0.022304204135 | 0.15761322418 | -0.178753127382 |
Nf3-Nf6 | -0.0913116172472 | -0.180938964282 | 0.287825842631 |
Nf3-b5 | 0.022304204135 | 0.176284405215 | -0.196606320265 |
Nf3-b6 | -0.0584040225073 | 0.415078983717 | -0.323457953908 |
Nf3-c5 | -0.127301289153 | -0.0880721945632 | 0.244280455199 |
Nf3-c6 | -0.148079829888 | -0.103783310312 | 0.285429887575 |
Nf3-d5 | -0.0559229620339 | -0.157283783189 | 0.2207098384 |
Nf3-d6 | -0.0295796558493 | -0.153479536526 | 0.183948580661 |
Nf3-e6 | -0.0810748726877 | -0.131475399039 | 0.227657757797 |
Nf3-f5 | 0.129481257794 | -0.0568042096432 | -0.108492174746 |
Nf3-g6 | -0.0415898086235 | -0.0856612816605 | 0.134202841978 |
b3-Nf6 | -0.227328217805 | -0.140257245358 | 0.419951619996 |
b3-b6 | -0.0170151883318 | -0.224427864693 | 0.235990276515 |
b3-c5 | -0.0706325416955 | 0.527642084695 | -0.415713687466 |
b3-d5 | -0.0500713147418 | 0.10044305924 | -0.0330837128858 |
b3-e5 | 0.0822863385612 | 0.234584215386 | -0.327772635324 |
b4-Nf6 | 0.0361191258125 | -0.000839501361686 | -0.0446129213967 |
b4-a5 | 0.444560288452 | -0.269388568189 | -0.301396800231 |
b4-c6 | 0.325209153508 | -0.377627298828 | -0.0478297129072 |
b4-d5 | -0.219073177397 | 0.493694482813 | -0.196606320265 |
b4-e5 | 0.235752334669 | 0.0894941900736 | -0.382004861743 |
b4-e6 | -0.0872283891652 | 0.0802611884629 | 0.0329347310873 |
b4-f5 | 0.490860297697 | -0.719932284473 | 0.0711915729794 |
c4-Nc6 | -0.386617477519 | 0.209892531078 | 0.285429887575 |
c4-Nf6 | 0.0430737789403 | -0.126261236995 | 0.0665693871197 |
c4-b6 | -0.302974406272 | 0.265760584462 | 0.126837888459 |
c4-c5 | -0.124364707379 | -0.22636541107 | 0.372822604771 |
c4-c6 | -0.0845036977896 | -0.239219041403 | 0.334992482743 |
c4-d5 | 0.769372661003 | -0.224427864693 | -0.752801944697 |
c4-d6 | 0.277880255169 | 0.0228560045349 | -0.371257120208 |
c4-e5 | 0.0325272461763 | -0.0340064394723 | -0.0083826581562 |
c4-e6 | -0.0659274508989 | -0.180052474116 | 0.255060521861 |
c4-f5 | 0.022304204135 | -0.0589724758279 | 0.0283439100602 |
c4-g5 | 0.533456306202 | 0.12027086211 | -0.785761685404 |
c4-g6 | -0.0967229401999 | -0.0346602145651 | 0.154759709879 |
d3-d5 | -0.0979668787044 | 0.581558864155 | -0.432898579011 |
d4-Nc6 | 0.138980227433 | -0.0502051386462 | -0.126746000289 |
d4-Nf6 | -0.0198161009805 | -0.0318685007992 | 0.0553888019455 |
d4-b5 | 0.210623399634 | 0.415078983717 | -0.661728976954 |
d4-b6 | 0.277880255169 | 0.14975167427 | -0.492593465431 |
d4-c5 | 0.163311680567 | -0.111509316258 | -0.0987215730794 |
d4-c6 | 0.313376928923 | -0.066440948242 | -0.330505266888 |
d4-d5 | 0.063627801048 | -0.117396872587 | 0.032249054882 |
d4-d6 | -0.0321005988079 | -0.0516518939567 | 0.0897518230063 |
d4-e5 | 0.233815418784 | 0.506571159389 | -0.778374157315 |
d4-e6 | 0.0437582896981 | -0.14916137055 | 0.0876055677686 |
d4-f5 | 0.0666959971476 | 0.0343141602108 | -0.116673390916 |
d4-g6 | 0.0513057418409 | 0.0930302332437 | -0.153465626217 |
e3-Nf6 | 0.0649002126406 | -0.439864568945 | 0.338989466224 |
e3-d5 | 0.022304204135 | 0.344325034532 | -0.357285056212 |
e3-e5 | 0.118145223273 | 0.470355506519 | -0.598303160133 |
e3-g6 | 0.277880255169 | 0.306982672461 | -0.64293614234 |
e4-Nc6 | -0.0389783367124 | 0.291211375129 | -0.229442574206 |
e4-Nf6 | 0.0834803028683 | 0.0806938814006 | -0.182125540169 |
e4-a6 | 0.0717705365931 | 0.409373020073 | -0.481681496945 |
e4-b6 | 0.306922988241 | 0.0311584071691 | -0.415713687466 |
e4-c5 | -0.056066454717 | 0.127602155147 | -0.0515147648184 |
e4-c6 | 0.00209663561512 | -0.059059599939 | 0.0538359091269 |
e4-d5 | 0.141332566612 | 0.0231506059187 | -0.19984581091 |
e4-d6 | 0.0413364632545 | 0.0886887580182 | -0.136779131349 |
e4-e5 | 0.0632955305846 | 0.0440144407345 | -0.121673004852 |
e4-e6 | 0.0605508407543 | 0.0525531331469 | -0.126386483857 |
e4-g6 | -0.0151960492223 | 0.0995388170739 | -0.0760707186133 |
f4-Nf6 | -0.0837839679923 | 0.20481960642 | -0.0904977210552 |
f4-c5 | -0.118703272297 | 0.448626114797 | -0.279716011272 |
f4-d5 | -0.00861958851681 | 0.375421521095 | -0.348136089041 |
f4-e5 | -0.124415380718 | 0.680406293164 | -0.494159534982 |
f4-e6 | 0.277880255169 | 0.540372435401 | -0.866101053378 |
f4-g6 | -0.388839877963 | 0.534284006802 | -0.0219555203231 |
g3-Nf6 | -0.0845036977896 | -0.197417889832 | 0.29502264793 |
g3-c5 | -0.43897940017 | 0.311536619055 | 0.254077939098 |
g3-d5 | -0.15109014851 | 0.0450583307312 | 0.146894157642 |
g3-e5 | -0.10775224787 | 0.0937555754325 | 0.0458379262817 |
g3-f5 | 0.316603899265 | -0.185257554829 | -0.220951583288 |
g3-g6 | -0.0774327913904 | -0.147501197614 | 0.238401964859 |
g4-d5 | 0.611240321734 | -0.196327425008 | -0.580838080138 |
g4-e5 | 0.55568031064 | -0.123266281827 | -0.580838080138 |
h3-d5 | -0.148079829888 | 0.792433379375 | -0.571523370808 |
h3-e5 | 0.210623399634 | 0.238194110753 | -0.492593465431 |
Looking at the observed outcome probabilities for each pair of starting moves reveals some interesting information. For example, it can help the black player decide what to do in response to the white player's first move. Suppose the white player starts out with move Nc3. Then, looking at the first 5 rows of the following table, we can see that the black player making the move c5 results in a 62% chance of winning!
table.to_row_probs_html()
White | Black | Draw | |
Nc3-Nf6 | 0.545454545455 | 0.181818181818 | 0.272727272727 |
Nc3-c5 | 0.27027027027 | 0.621621621622 | 0.108108108108 |
Nc3-d5 | 0.442105263158 | 0.389473684211 | 0.168421052632 |
Nc3-e5 | 0.416666666667 | 0.291666666667 | 0.291666666667 |
Nc3-g6 | 0.352941176471 | 0.588235294118 | 0.0588235294118 |
Nf3-Nc6 | 0.4 | 0.344444444444 | 0.255555555556 |
Nf3-Nf6 | 0.355545200373 | 0.243709226468 | 0.400745573159 |
Nf3-b5 | 0.4 | 0.35 | 0.25 |
Nf3-b6 | 0.368421052632 | 0.421052631579 | 0.210526315789 |
Nf3-c5 | 0.341463414634 | 0.271341463415 | 0.387195121951 |
Nf3-c6 | 0.333333333333 | 0.266666666667 | 0.4 |
Nf3-d5 | 0.369391824526 | 0.25074775673 | 0.379860418744 |
Nf3-d6 | 0.37969924812 | 0.251879699248 | 0.368421052632 |
Nf3-e6 | 0.359550561798 | 0.258426966292 | 0.38202247191 |
Nf3-f5 | 0.441935483871 | 0.28064516129 | 0.277419354839 |
Nf3-g6 | 0.375 | 0.272058823529 | 0.352941176471 |
b3-Nf6 | 0.302325581395 | 0.255813953488 | 0.441860465116 |
b3-b6 | 0.384615384615 | 0.230769230769 | 0.384615384615 |
b3-c5 | 0.363636363636 | 0.454545454545 | 0.181818181818 |
b3-d5 | 0.371681415929 | 0.327433628319 | 0.300884955752 |
b3-e5 | 0.423469387755 | 0.367346938776 | 0.209183673469 |
b4-Nf6 | 0.405405405405 | 0.297297297297 | 0.297297297297 |
b4-a5 | 0.565217391304 | 0.217391304348 | 0.217391304348 |
b4-c6 | 0.518518518519 | 0.185185185185 | 0.296296296296 |
b4-d5 | 0.305555555556 | 0.444444444444 | 0.25 |
b4-e5 | 0.483516483516 | 0.324175824176 | 0.192307692308 |
b4-e6 | 0.357142857143 | 0.321428571429 | 0.321428571429 |
b4-f5 | 0.583333333333 | 0.0833333333333 | 0.333333333333 |
c4-Nc6 | 0.24 | 0.36 | 0.4 |
c4-Nf6 | 0.408126573175 | 0.259978425027 | 0.331895001798 |
c4-b6 | 0.272727272727 | 0.376623376623 | 0.350649350649 |
c4-c5 | 0.3426124197 | 0.230192719486 | 0.427194860814 |
c4-c6 | 0.358208955224 | 0.226368159204 | 0.415422885572 |
c4-d5 | 0.692307692308 | 0.230769230769 | 0.0769230769231 |
c4-d6 | 0.5 | 0.304347826087 | 0.195652173913 |
c4-e5 | 0.404 | 0.287428571429 | 0.308571428571 |
c4-e6 | 0.365477338476 | 0.243972999036 | 0.390549662488 |
c4-f5 | 0.4 | 0.28 | 0.32 |
c4-g5 | 0.6 | 0.333333333333 | 0.0666666666667 |
c4-g6 | 0.353427895981 | 0.287234042553 | 0.359338061466 |
d3-d5 | 0.352941176471 | 0.470588235294 | 0.176470588235 |
d4-Nc6 | 0.445652173913 | 0.282608695652 | 0.271739130435 |
d4-Nf6 | 0.383519463211 | 0.288064708856 | 0.328415827933 |
d4-b5 | 0.473684210526 | 0.421052631579 | 0.105263157895 |
d4-b6 | 0.5 | 0.342105263158 | 0.157894736842 |
d4-c5 | 0.455172413793 | 0.264367816092 | 0.280459770115 |
d4-c6 | 0.513888888889 | 0.277777777778 | 0.208333333333 |
d4-d5 | 0.416168806407 | 0.262615990848 | 0.321215202746 |
d4-d6 | 0.378712871287 | 0.282178217822 | 0.339108910891 |
d4-e5 | 0.48275862069 | 0.448275862069 | 0.0689655172414 |
d4-e6 | 0.408394403731 | 0.253164556962 | 0.338441039307 |
d4-f5 | 0.4173693086 | 0.307757166948 | 0.274873524452 |
d4-g6 | 0.41134751773 | 0.325227963526 | 0.263424518744 |
e3-Nf6 | 0.416666666667 | 0.166666666667 | 0.416666666667 |
e3-d5 | 0.4 | 0.4 | 0.2 |
e3-e5 | 0.4375 | 0.4375 | 0.125 |
e3-g6 | 0.5 | 0.388888888889 | 0.111111111111 |
e4-Nc6 | 0.376021798365 | 0.384196185286 | 0.239782016349 |
e4-Nf6 | 0.423936553713 | 0.321557317952 | 0.254506128335 |
e4-a6 | 0.41935483871 | 0.41935483871 | 0.161290322581 |
e4-b6 | 0.511363636364 | 0.306818181818 | 0.181818181818 |
e4-c5 | 0.369335679718 | 0.335514738231 | 0.29514958205 |
e4-c6 | 0.392093324692 | 0.279974076474 | 0.327932598833 |
e4-d5 | 0.446572580645 | 0.304435483871 | 0.248991935484 |
e4-d6 | 0.407446808511 | 0.323936170213 | 0.268617021277 |
e4-e5 | 0.416038797956 | 0.310643457175 | 0.273317744869 |
e4-e6 | 0.41496487502 | 0.313184120242 | 0.271851004738 |
e4-g6 | 0.385327164574 | 0.327164573695 | 0.287508261732 |
f4-Nf6 | 0.358490566038 | 0.358490566038 | 0.283018867925 |
f4-c5 | 0.344827586207 | 0.431034482759 | 0.224137931034 |
f4-d5 | 0.387900355872 | 0.409252669039 | 0.202846975089 |
f4-e5 | 0.342592592593 | 0.5 | 0.157407407407 |
f4-e6 | 0.5 | 0.458333333333 | 0.0416666666667 |
f4-g6 | 0.239130434783 | 0.45652173913 | 0.304347826087 |
g3-Nf6 | 0.358208955224 | 0.238805970149 | 0.402985074627 |
g3-c5 | 0.219512195122 | 0.390243902439 | 0.390243902439 |
g3-d5 | 0.332155477032 | 0.310954063604 | 0.356890459364 |
g3-e5 | 0.349112426036 | 0.325443786982 | 0.325443786982 |
g3-f5 | 0.515151515152 | 0.242424242424 | 0.242424242424 |
g3-g6 | 0.360975609756 | 0.253658536585 | 0.385365853659 |
g4-d5 | 0.630434782609 | 0.239130434783 | 0.130434782609 |
g4-e5 | 0.608695652174 | 0.260869565217 | 0.130434782609 |
h3-d5 | 0.333333333333 | 0.533333333333 | 0.133333333333 |
h3-e5 | 0.473684210526 | 0.368421052632 | 0.157894736842 |
If white's first move is (pawn to e4), is there a significant association between black's response and the outcome of the game?
opening_move_pairs = [pair for pair in opening_move_pairs if pair.startswith('e4')]
index = dict((move_pair, i) for i, move_pair in enumerate(opening_move_pairs))
counts = np.zeros((len(index), 3))
for game in iter_games():
pair = game.move1 + '-' + game.move2
if pair not in index:
# Skip game whose move pair was not very common.
continue
row = index[pair]
col = 0
if game.outcome == 'B':
col = 1
elif game.outcome == 'D':
col = 2
counts[row, col] += 1
table = ContingencyTable(opening_move_pairs, ('White', 'Black', 'Draw'), counts)
table
White | Black | Draw | ||
e4-Nc6 | 138.0 | 141.0 | 88.0 | 367.0 |
e4-Nf6 | 588.0 | 446.0 | 353.0 | 1387.0 |
e4-a6 | 13.0 | 13.0 | 5.0 | 31.0 |
e4-b6 | 45.0 | 27.0 | 16.0 | 88.0 |
e4-c5 | 6716.0 | 6101.0 | 5367.0 | 18184.0 |
e4-c6 | 1210.0 | 864.0 | 1012.0 | 3086.0 |
e4-d5 | 443.0 | 302.0 | 247.0 | 992.0 |
e4-d6 | 766.0 | 609.0 | 505.0 | 1880.0 |
e4-e5 | 4804.0 | 3587.0 | 3156.0 | 11547.0 |
e4-e6 | 2540.0 | 1917.0 | 1664.0 | 6121.0 |
e4-g6 | 583.0 | 495.0 | 435.0 | 1513.0 |
17846.0 | 14502.0 | 12848.0 | 45196.0 |
So yes, we again see very significant association.
table.to_residuals_normalized_html()
White | Black | Draw | |
e4-Nc6 | -0.0477036199199 | 0.197361108136 | -0.156507782464 |
e4-Nf6 | 0.0736431963249 | 0.00214484499913 | -0.10471209712 |
e4-a6 | 0.062039745059 | 0.306934304946 | -0.432621620536 |
e4-b6 | 0.295057206606 | -0.0437901982172 | -0.360409826786 |
e4-c5 | -0.0646365919223 | 0.0456436428842 | 0.0382612476914 |
e4-c6 | -0.00700157442638 | -0.12745080952 | 0.153583572297 |
e4-d5 | 0.130970209282 | -0.0512159613135 | -0.124109626702 |
e4-d6 | 0.0318819879775 | 0.0095586228752 | -0.055073560584 |
e4-e5 | 0.0536416851074 | -0.0318685911955 | -0.0385376091929 |
e4-e6 | 0.0509219147945 | -0.0239505241727 | -0.0436972283523 |
e4-g6 | -0.0241372559636 | 0.0196200574199 | 0.0113810240679 |
table.to_row_probs_html()
White | Black | Draw | |
e4-Nc6 | 0.376021798365 | 0.384196185286 | 0.239782016349 |
e4-Nf6 | 0.423936553713 | 0.321557317952 | 0.254506128335 |
e4-a6 | 0.41935483871 | 0.41935483871 | 0.161290322581 |
e4-b6 | 0.511363636364 | 0.306818181818 | 0.181818181818 |
e4-c5 | 0.369335679718 | 0.335514738231 | 0.29514958205 |
e4-c6 | 0.392093324692 | 0.279974076474 | 0.327932598833 |
e4-d5 | 0.446572580645 | 0.304435483871 | 0.248991935484 |
e4-d6 | 0.407446808511 | 0.323936170213 | 0.268617021277 |
e4-e5 | 0.416038797956 | 0.310643457175 | 0.273317744869 |
e4-e6 | 0.41496487502 | 0.313184120242 | 0.271851004738 |
e4-g6 | 0.385327164574 | 0.327164573695 | 0.287508261732 |
Black's best move depends on whether they would prefer a loss or a draw. Suppose it's the last game of the tournament and black's only hope of winning the tournament is by winning this game, not drawing. Then the move a6 gives them a 42% chance of winning; higher than any other move.