codexsimple-random-sampling-subsets-and-replacement.py
simple-random-sampling-subsets-and-replacement.py
"""Enumerate two-draw designs; Python 3.14, matplotlib 3.10, numpy 2.3."""
from itertools import combinations, product
from pathlib import Path
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize
import numpy as np
def main():
values = np.arange(2, 13, 2)
subsets = list(combinations(values, 2))
replacement = list(product(values, repeat=2))
assert len(subsets) == 15 and len(replacement) == 36
assert np.isclose(np.var(np.mean(subsets, axis=1)), 14 / 3)
assert np.isclose(np.var(np.mean(replacement, axis=1)), 35 / 6)
plt.rcParams.update({'font.size': 11, 'axes.spines.top': False,
'axes.spines.right': False, 'figure.facecolor': 'white'})
fig, axes = plt.subplots(1, 2, figsize=(11, 6.9), gridspec_kw={'width_ratios': [1, 1.12]})
fig.suptitle('Six labelled values: 2, 4, 6, 8, 10, 12 | Two selections', fontsize=15, y=.97)
cmap = plt.get_cmap('Blues')
norm = Normalize(2, 12)
ax = axes[0]
ax.set_title('Without replacement: all 15 subsets\nEach subset has probability 1/15', pad=15)
for row, (a, b) in enumerate(subsets):
mean = (a + b) / 2
ax.scatter([a, b], [row, row], s=46, color='#116a8e', zorder=3)
ax.plot([a, b], [row, row], color='#8db9cc', lw=2)
ax.text(14.2, row, f'{mean:g}', ha='center', va='center', fontsize=10)
ax.text(14.2, -1.1, 'Mean', ha='center', weight='bold')
ax.set(xticks=values, yticks=np.arange(15),
yticklabels=[f'{{{a}, {b}}}' for a, b in subsets], ylim=(15, -1.8), xlim=(1, 15.2),
xlabel='Selected values')
ax.grid(axis='x', alpha=.15)
ax.spines['left'].set_visible(False)
ax.tick_params(axis='y', length=0)
ax = axes[1]
means = (values[:, None] + values[None, :]) / 2
ax.imshow(means, cmap=cmap, norm=norm, origin='upper', extent=(-.5, 5.5, 5.5, -.5))
for i in range(6):
for j in range(6):
ax.text(j, i, f'{means[i, j]:g}', ha='center', va='center',
color='white' if means[i, j] >= 8 else '#183340', fontsize=12)
if i == j:
ax.add_patch(plt.Rectangle((j - .5, i - .5), 1, 1, fill=False,
edgecolor='#d55e00', lw=2.5))
ax.set_title('With replacement: all 36 ordered outcomes\nEach cell has probability 1/36; text = mean', pad=15)
ax.set(xticks=np.arange(6), yticks=np.arange(6), xticklabels=values, yticklabels=values,
xlabel='Second selected value', ylabel='First selected value')
fig.text(.75, .11, 'Orange diagonal: the same label is selected twice',
ha='center', fontsize=10, color='#a74900')
fig.text(.25, .045, 'Expected mean = 7\nMean variance = 14/3', ha='center', fontsize=12)
fig.text(.75, .045, 'Expected mean = 7\nMean variance = 35/6', ha='center', fontsize=12)
fig.subplots_adjust(left=.09, right=.97, bottom=.19, top=.81, wspace=.34)
fig.savefig(Path(__file__).stem + '.png', dpi=120, facecolor='white')
plt.close(fig)
if __name__ == '__main__':
main()