Module 6 · Project Management Fundamentals
Earned value
Measure a project's progress and spending honestly with earned value (planned value, earned value and actual cost), calculate schedule and cost performance, and forecast the final cost at week 10.
About 25 minutes
The problem
At the week 10 meeting, the finance director said: "We've spent about ₦58 million of an ₦81.5 million budget, so we're fine." The operations manager said: "Most tasks are finished, so we're ahead." Both were wrong, and for the same reason: neither compared what was spent with what was achieved for that money, and with what should have been achieved by now.
Earned value does exactly that comparison, in naira.
The concept
Three numbers at the status date
| Measure | Question | How |
|---|---|---|
| Planned value (PV) | what work should be done by now, in budget terms? | budget of the work scheduled up to today |
| Earned value (EV) | what work is done, in budget terms? | each task's budget × its % complete |
| Actual cost (AC) | what have we spent? | from the accounts |
What they tell you
| Measure | Formula | Meaning |
|---|---|---|
| Schedule variance | EV − PV | negative: behind schedule |
| Cost variance | EV − AC | negative: over budget |
| SPI | EV ÷ PV | below 1: less done than planned |
| CPI | EV ÷ AC | below 1: getting less than ₦1 of work per ₦1 spent |
| Estimate at completion (EAC) | budget ÷ CPI | final cost if spending efficiency stays the same |
The budget for all the work is the budget at completion (BAC).
Example
Budgets from most likely durations, the baseline schedule from lesson 4, and the progress reported at the end of week 10 (working day 50):
import numpy as np
import pandas as pd
base = "https://academy.cloudtechanalytics.com/datasets/project/"
tasks = pd.read_csv(base + "tasks.csv").fillna({"predecessors": ""}).set_index("task_id")
status = pd.read_csv(base + "weekly_status.csv")
STATUS_DAY, WEEK = 50, 10
# Baseline: earliest start of each task in the most likely plan.
es, ef = {}, {}
for t, row in tasks.iterrows():
preds = [p for p in row["predecessors"].split(";") if p]
es[t] = max((ef[p] for p in preds), default=0)
ef[t] = es[t] + row["likely_days"]
tasks["budget"] = tasks["likely_days"] * tasks["daily_cost_ngn"]
planned_fraction = ((STATUS_DAY - pd.Series(es)) / tasks["likely_days"]).clip(0, 1)
now = status[status["week"] == WEEK].set_index("task_id")
tasks["pv"] = tasks["budget"] * planned_fraction
tasks["ev"] = tasks["budget"] * now["percent_complete"].reindex(tasks.index).fillna(0) / 100
tasks["ac"] = now["actual_cost_ngn"].reindex(tasks.index).fillna(0)
BAC, PV, EV, AC = tasks["budget"].sum(), tasks["pv"].sum(), tasks["ev"].sum(), tasks["ac"].sum()
SPI, CPI = EV / PV, EV / AC
print(f"BAC ₦{BAC:,.0f} PV ₦{PV:,.0f} EV ₦{EV:,.0f} AC ₦{AC:,.0f}")
print(f"Schedule variance ₦{EV - PV:,.0f} SPI {SPI:.2f}")
print(f"Cost variance ₦{EV - AC:,.0f} CPI {CPI:.2f}")
print(f"Estimate at completion ₦{BAC / CPI:,.0f} variance at completion ₦{BAC - BAC / CPI:,.0f}")BAC ₦81,530,000 PV ₦55,610,000 EV ₦44,515,000 AC ₦57,648,000
Schedule variance ₦-11,095,000 SPI 0.80
Cost variance ₦-13,133,000 CPI 0.77
Estimate at completion ₦105,583,319 variance at completion ₦-24,053,319Less work is done than planned (SPI 0.80), and the work that is done cost far more than budgeted (CPI 0.77). If spending efficiency stays as it has been, the project finishes about ₦24 million over budget. Even the kinder forecast, where everything left is done at budget (AC + BAC − EV), is about ₦94.7 million. Where is the overspend?
tasks["cost_variance"] = tasks["ev"] - tasks["ac"]
tasks.loc[tasks["ac"] > 0, ["name", "budget", "ev", "ac", "cost_variance"]].sort_values("cost_variance").head(5).round(0)name budget ev ac cost_variance
task_id
B4 Import and clear racking 8000000 6880000.0 13000000.0 -6120000.0
B2 Fit-out works: floor, power and security 23750000 10925000.0 13338000.0 -2413000.0
C2 Buy laptops, scanners and printers 6500000 6500000.0 8775000.0 -2275000.0
A3 Obtain building and trading permits 2250000 2250000.0 3769000.0 -1519000.0
D1 Hire depot manager 1800000 1800000.0 2193000.0 -393000.0The racking import is the biggest overrun: it took longer than planned and was priced in dollars, paid after the naira weakened. The fit-out and the permits come next, because running longer means paying the contractor and the team for more days. The laptops, scanners and printers were also priced in dollars. Most of this is risks R01 and R02 in the register (lesson 8) happening, not a team spending carelessly.
Walkthrough
- Run the cells. Compute SPI and CPI for the Facilities phase alone.
- Recompute EAC assuming the remaining work will be done at budget (EAC = AC + BAC − EV). Which forecast do you believe, and why?
- Why can't "most tasks are finished" tell you whether you're ahead?
- Explain the numbers to the finance director (the task below).
Practice
Practice
What is the project's CPI at week 10? Two decimal places.
Task
5 minReply to the finance director's "we've spent less than the budget, so we're fine" in 50 to 120 words, using earned value: what was spent, what was earned, the CPI, the forecast final cost, and the main cause.
Your work is checked for
- Mentions earned value or work done
- Gives the CPI
- Gives a forecast final cost
- Names the cause (naira, dollar, imported, racking, laptops)
- Between 50 and 120 words
Check your understanding
Answer every question to check.