---
title: "Learning Curves"
output:
html_vignette:
fig_width: 7
fig_height: 5
vignette: >
%\VignetteIndexEntry{Learning Curves}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>"
)
```
Learning curves model how efficiency or completion rate evolves over time as a team gains experience. In project management they are used to:
- **Forecast completion**: Predict when a task or deliverable will reach 100% based on past progress.
- **Identify acceleration**: Detect when a project is in its rapid-improvement phase versus plateauing.
- **Set realistic milestones**: Calibrate schedule targets against demonstrated learning rates.
## Sigmoidal Models
Sigmoidal (S-shaped) functions capture the three phases of learning: slow start, rapid improvement, and plateau. The PRA package provides three model types:
| Model | Formula | Parameters |
|-------|---------|------------|
| **Logistic** | K / (1 + exp(−r(t − t₀))) | K = ceiling; r = growth rate; t₀ = inflection time |
| **Pearl** | K / (1 + exp(−r(t − t₀))) | Same as logistic (identical functional form) |
| **Gompertz** | A · exp(−b · exp(−c · t)) | A = ceiling; c = growth rate; b = initial suppression |
**Parameter interpretation (Logistic / Pearl):**
- **K** — the maximum achievable value (e.g., 100% completion)
- **r** — how fast the S-curve rises; larger r = steeper transition
- **t₀** — the time at which the outcome is at its midpoint (inflection point)
## Example: Fitting a Logistic Model
```{r setup}
library(PRA)
```
We have weekly completion percentage data for a construction deliverable over 9 weeks.
```{r}
data <- data.frame(
time = 1:9,
completion = c(5, 15, 40, 60, 70, 75, 80, 85, 90)
)
```
### Fit the Model
```{r}
fit <- fit_sigmoidal(data, "time", "completion", "logistic")
```
### Assess Fit Quality
Use `summary()` to examine the fitted coefficients, their standard errors, and the residual standard error — a measure of how closely the model matches the observed data.
```{r}
summary(fit)
```
A small residual standard error (relative to the response scale) indicates a good fit. Coefficient `t values` with |t| > 2 are statistically meaningful.
### Plot with Confidence Bands
`plot_sigmoidal()` plots the data, fitted curve, and optional confidence bounds in a single call.
```{r}
plot_sigmoidal(
fit, data, "time", "completion", "logistic",
conf_level = 0.95,
main = "Logistic Learning Curve - Completion Forecast",
xlab = "Week",
ylab = "Completion (%)"
)
```
The shaded region is the 95% confidence band — the range within which the true fitted curve is likely to lie. Wider bands at the tails reflect greater uncertainty in extrapolated predictions.
### Predict Future Completion
Use `predict_sigmoidal()` to generate numeric forecasts, including confidence bounds for specific future time points.
```{r}
future_times <- seq(1, 12, length.out = 100)
predictions <- predict_sigmoidal(fit, future_times, "logistic", conf_level = 0.95)
knitr::kable(
tail(round(predictions, 1), 5),
caption = "Predicted completion (final 5 points)",
row.names = FALSE
)
```
## Comparing All Three Model Types
Different model types can produce different forecasts, especially in the tails. It is good practice to fit multiple models and compare their goodness-of-fit.
```{r warning=FALSE}
fit_logistic <- fit_sigmoidal(data, "time", "completion", "logistic")
fit_pearl <- fit_sigmoidal(data, "time", "completion", "pearl")
fit_gompertz <- fit_sigmoidal(data, "time", "completion", "gompertz")
```
```{r}
# Residual standard errors for comparison
rse <- function(fit) summary(fit)$sigma
comparison <- data.frame(
Model = c("Logistic", "Pearl", "Gompertz"),
Residual_StdError = round(c(rse(fit_logistic), rse(fit_pearl), rse(fit_gompertz)), 3)
)
knitr::kable(comparison, caption = "Model Fit Comparison (lower RSE = better fit)")
```
Now plot all three fits side by side:
```{r}
x_seq <- seq(1, 12, length.out = 200)
pred_log <- predict_sigmoidal(fit_logistic, x_seq, "logistic")
pred_prl <- predict_sigmoidal(fit_pearl, x_seq, "pearl")
pred_gom <- predict_sigmoidal(fit_gompertz, x_seq, "gompertz")
# Base plot with observed data
plot(data$time, data$completion,
pch = 16, xlim = c(1, 12), ylim = c(0, 105),
main = "Learning Curve: Model Comparison",
xlab = "Week", ylab = "Completion (%)"
)
lines(pred_log$x, pred_log$pred, col = "steelblue", lwd = 2)
lines(pred_prl$x, pred_prl$pred, col = "tomato", lwd = 2, lty = 2)
lines(pred_gom$x, pred_gom$pred, col = "darkgreen", lwd = 2, lty = 3)
legend("bottomright",
legend = c("Logistic", "Pearl", "Gompertz"),
col = c("steelblue", "tomato", "darkgreen"),
lty = c(1, 2, 3), lwd = 2, bty = "n"
)
```
**Note:** Logistic and Pearl share the same functional form and will produce identical fits on the same data. Gompertz has a different shape, its inflection point occurs earlier and the curve is asymmetric, making it better suited for processes that accelerate quickly early on.
## Summary
The sigmoidal workflow in PRA:
1. `fit_sigmoidal()` — fit a model to observed time-completion data
2. `summary(fit)` — inspect coefficient estimates and goodness-of-fit
3. `predict_sigmoidal()` — generate numeric forecasts with optional confidence bounds
4. `plot_sigmoidal()` — visualize the fit and confidence band
Choose the model type based on the shape of your data and theoretical expectations about the learning process.