Thompson Sampling Mock Bandit in Python
Implement a Thompson sampling multi-armed bandit to explore and exploit reward probabilities across multiple options, updating Beta distributions over time.
Python code
31 linesimport random
class ThompsonSamplingBandit:
def __init__(self, num_arms, alpha=1.0, beta=1.0):
self.num_arms = num_arms
self.alpha = [alpha] * num_arms
self.beta = [beta] * num_arms
def select_arm(self):
samples = [random.betavariate(a, b) for a, b in zip(self.alpha, self.beta)]
return max(range(self.num_arms), key=lambda i: samples[i])
def update(self, arm, reward):
if reward:
self.alpha[arm] += 1
else:
self.beta[arm] += 1
if __name__ == "__main__":
random.seed(42)
true_probabilities = [0.2, 0.5, 0.8]
bandit = ThompsonSamplingBandit(len(true_probabilities))
total_reward = 0
for _ in range(1000):
arm = bandit.select_arm()
reward = 1 if random.random() < true_probabilities[arm] else 0
bandit.update(arm, reward)
total_reward += reward
print(f"Arm selections per arm: {[bandit.alpha[i] + bandit.beta[i] - 2 for i in range(len(true_probabilities))]}")
print(f"Total reward: {total_reward}")
print(f"Estimated probabilities: {[round(bandit.alpha[i] / (bandit.alpha[i] + bandit.beta[i]), 3) for i in range(len(true_probabilities))]}")
Output
Arm selections per arm: [6, 81, 913]
Total reward: 767
Estimated probabilities: [0.125, 0.457, 0.795]
How it works
Thompson sampling treats each arm's reward probability as a random variable with a Beta distribution. The select_arm method samples from each Beta and picks the arm with the highest sample, balancing exploration and exploitation. After receiving a binary reward, update increments the alpha parameter on success or beta on failure, refining the distribution. This converges to selecting the best arm (0.8 probability) over time, as seen in the high selection count. The approach is simple yet effective for online decision-making scenarios.
Common mistakes
- Forgetting to update both alpha and beta based on reward type
- Using incorrect Beta parameter order (alpha is success count, beta is failure count)
- Not setting a random seed when reproducibility is needed
Variations
- Use numpy's `random.beta` for faster vectorized sampling with large numbers of arms
- Replace uniform prior with domain-specific prior counts for faster convergence
Real-world use cases
- Automating A/B test allocation to minimize regret while learning conversion rates.
- Dynamic ad placement to maximize click-through rate without sacrificing exploration.
- Recommendation system arm selection to balance new content trials against proven items.
Sponsored
More from A/B testing & experimentation
Keep learning
Related tutorials and quizzes for this topic.