How to Create a Simple Simulation in Python – Numeric Data
See Python: Tips and Tricks for similar articles.
In this article, I show how to create a simple Simulation class in Python. The purpose of the class is to run a simulation many times and then return stats (e.g., mean, median, etc.) on the results of the simulation.
Let’s start with a simple Die class that has a roll() method that returns an integer between 1 and 6 (inclusive):
import random
class Die:
def __init__(self, sides=6):
self.sides = sides
def roll(self):
return random.randint(1, self.sides)
We want our Simulation class to take two arguments: a function to run and the number of times to run it (n) and then to run the function n times and return a report like this:
Number of Simulations: 1,000 Max: 6 Min: 1 Mean: 3.522 Median: 4.0 Mode: 4 Standard Deviation: 1.7018849586304041 Variance: 2.8964124124124124
Here is the Simulation class that does just this:
import statistics as stats
class SimulationNumeric:
"""Runs a function that returns a number n times and prints stats
Args:
fnct_to_run (function): The function to run
iterations (integer): The number of times to run it"""
def __init__(self, fnct_to_run, iterations):
self._fnct_to_run = fnct_to_run
self._iterations = iterations
self._results = []
self.run()
@property
def mode(self):
try:
return stats.mode(self._results)
except:
return None
def run(self):
for i in range(self._iterations):
result = self._fnct_to_run()
self._results.append(result)
self.report()
def report(self):
max_num = max(self._results)
min_num = min(self._results)
mean = stats.mean(self._results)
median = stats.median(self._results)
mode = self.mode
std_dev = stats.stdev(self._results)
variance = stats.variance(self._results)
print(
f"""Number of Simulations: {self._iterations:,}
Max: {max_num}
Min: {min_num}
Mean: {mean}
Median: {median}
Mode: {mode}
Standard Deviation: {std_dev}
Variance: {variance}"""
)
die = Die()
sim = SimulationNumeric(die.roll, 1000)
This class should work fine with an function that returns a number.
Note that the reason mode is written as a property is that not all result sets will have a mode. If, for example, two numbers are rolled the same number of times and both those numbers are rolled more often than any other number, then there will be no mode. In that case, stats.mode(self._results) will error. Our code catches that error and returns None for mode in that case.
