I turned a fictional zombie outbreak into a hands-on way to practice Monte Carlo simulation, Streamlit, testing, and data visualization. Zombie Carlo Simulator shows how a fun idea can still become a serious data science project.
In This Post:
- Putting Fun into Data Science
- Building one outbreak, then hundreds
- Letting the charts tell the story
- Working through the messy parts
- Knowing when to share it
Putting Fun into Data Science
After building a small blackjack game in Python, I wanted another project that would be fun to build while giving me more practice with data science. Blackjack worked because I already knew the rules and enjoyed the game. When I came across the idea of a Monte Carlo zombie simulator, it had the same appeal: the concept was easy enough to understand, small enough to finish quickly, and strange enough to keep me interested.
What started as a fictional outbreak model became an interactive Streamlit app for exploring how the same starting conditions can lead to very different outcomes.
The zombies gave me a reason to start building while the statistics gave me something to think about.
Building one outbreak, then hundreds
I started by learning about the SIR model, which divides a population into susceptible, infected, and removed groups. For Zombie Carlo, those became humans who could still be infected, active zombies spreading the outbreak, and zombies removed through either elimination or decay.
I first built a single outbreak simulation inside a ZombieSIR class. Instead of making infections and removals completely deterministic, I used binomial draws so chance could influence what happened at each step.
That gave me one possible outbreak.
The next step was turning it into a Monte Carlo simulation by running the same scenario hundreds or thousands of times. Rather than asking, "What happens in this one outbreak?" I could start asking, "What range of outcomes is possible under these same assumptions?"
From there, I built the Streamlit interface, created the visualizations with Matplotlib, and added controls for exploring different scenarios. I kept the simulation logic, plotting functions, and Streamlit interface in separate modules so each part of the project had a clear responsibility.
Most of the individual tools were already familiar. The useful practice came from putting them together into one application. Along the way, I also learned more about managing Streamlit execution with forms and st.stop(), especially when controlling when simulations should run and when results should appear.
Letting the charts tell the story
My favorite visualization in the app is the uncertainty chart. It plots the median number of active zombies over time, surrounded by percentile bands showing how much the simulations vary.
That variation is the point.
Changing the parameters can create very different outbreak patterns. Some outbreaks disappear almost immediately. Others grow into clear waves. Even when the parameters stay exactly the same, rerunning the scenario can still produce noticeably different results because randomness is built into the model.
A single simulation shows one possible path. Running the simulation repeatedly reveals the range of paths the model can produce.
That was one of the ideas I most wanted the app to communicate visually. Instead of treating one simulated curve as the answer, the Monte Carlo results make the uncertainty visible.
| Classic Movie Zombies. The uncertainty chart shows the median number of active zombies along with the middle 50% and 90% of simulated outcomes. |
| Apocalypse Zombies. The uncertainty chart shows the median number of active zombies along with the middle 50% and 90% of simulated outcomes. |
I’m especially proud that the visualizations can communicate much of that story without requiring me to stand beside them and explain every graph. Building a model is one skill. Presenting its behavior clearly enough for someone else to explore is another.
Working through the messy parts
The hardest part of the project was not the math or the Streamlit code. It was giving myself enough time to struggle before asking AI for the next step.
When something didn't work right the first time, it was tempting to ask for the fix immediately. Instead, I tried to slow down, inspect what the program was doing, and reason through the problem myself first. Sometimes that meant making the wrong change before understanding why the right one worked.
One issue surfaced while I was checking the population counts, but the problem turned out to be in my test rather than the simulation itself. I had calculated the expected results incorrectly, so I revised the test and verified that the counts matched the correct total. I also tested whether using the same random seed reproduced the same simulation results.
Beyond those automated checks, I tried different parameter combinations and manually inspected how the model behaved. Did extreme settings produce extreme results? Did a scenario that should die out actually die out? Did repeated simulations vary while remaining reproducible when I fixed the seed?
Those checks became part of building the project, not something separate I saved for the end.
Knowing when to share it
There are still several directions I could take Zombie Carlo, including adding a vaccinated population.
For this version, my goal was a manageable simulation with working controls, meaningful visualizations, modular code, and enough testing to give me confidence in its behavior. Once I reached that point, adding another feature was less important than finishing the project and putting it in front of other people.
Zombie Carlo gave me practice with stochastic simulation, Monte Carlo methods, modular Python development, Streamlit, testing, and communicating uncertainty through visualization. Just as importantly, it gave me another chance to work through problems more independently.
It also reinforced something I learned from the blackjack project: a portfolio project does not have to begin with the most serious idea possible to produce serious learning.
Sometimes a good project starts because the idea makes you want to sit down and build it.
You can try Zombie Carlo Simulator or explore the code on GitHub. Try changing the settings and comparing the outcomes. I’d especially like feedback on the app and whether the visualizations make the uncertainty in the simulations clear.

Comments
Post a Comment