Skip to content

Instantly share code, notes, and snippets.

@FrankRuns
Created August 17, 2025 11:56
Show Gist options
  • Select an option

  • Save FrankRuns/13c76f300f15da7d74b3d60d7b7c0d5d to your computer and use it in GitHub Desktop.

Select an option

Save FrankRuns/13c76f300f15da7d74b3d60d7b7c0d5d to your computer and use it in GitHub Desktop.
risk_complacency_model.R
###############################################################
# TUTORIAL: The “complacency model” for safety incidents
#
# Audience: Curious operators and analysts. No math background required.
#
# Big idea in plain English:
# - Instead of assuming risk is constant, let’s assume it RISES
# the longer we go without an incident or intervention.
# - Think of it like tension in a spring. The longer it goes untouched,
# the more tightly wound it gets. Eventually something snaps.
# - This better reflects human systems, where people become complacent
# after long clean streaks.
#
# This code shows:
# - How to model risk as "baseline + drift per week without incident"
# - How that risk resets after a safety drill, training, or actual incident
# - How it looks on a chart (the sawtooth pattern)
###############################################################
# ---------------------------
# 1) Define the complacency model
# ---------------------------
# Baseline risk (probability of incident the first week after training)
baseline <- 0.002 # ~0.2% chance in week 1
# Drift per week (how much risk creeps up each week with no incident)
drift <- 0.0005 # adds 0.05% risk per week
# Number of weeks to simulate
T <- 30
# Vector for risk each week
weeks <- 1:T
# Age since last reset (like "days since last training").
# For simplicity, assume no incident actually occurs, so age just increases.
age <- weeks - 1 # starts at 0 in week 1
# Risk function: probability this week = 1 - exp(-(baseline + drift*age))
# (This is just a smooth way to map "baseline + drift" into a probability).
lambda <- baseline + drift * age
risk_this_week <- 1 - exp(-lambda)
# ---------------------------
# 2) Simulate a reset
# ---------------------------
# Imagine we do a safety training on week 15 that resets risk back to baseline.
risk_reset <- risk_this_week
risk_reset[weeks >= 15] <- 1 - exp(-(baseline + drift * (weeks[weeks >= 15] - 15)))
# ---------------------------
# 3) Bundle into a table
# ---------------------------
out <- data.frame(
week = weeks,
complacency_risk = round(risk_reset, 4)
)
print(out, row.names = FALSE)
# ---------------------------
# 4) Visualize the sawtooth
# ---------------------------
# install.packages("ggplot2") # if not already installed
library(ggplot2)
ggplot(out, aes(week, complacency_risk)) +
geom_line(color = "red", size = 1.2) +
geom_vline(xintercept = 15, linetype = "dashed") +
labs(
x = "Weeks since start",
y = "Probability of an incident this week",
title = "Complacency Model: Risk creeps upward, then resets after incident / training"
) +
annotate("text", x = 15, y = max(out$complacency_risk) - 0.001, vjust = -1,
label = "Training / reset at week 15")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment