Budget Constraints
Why this matters
Before we can talk about what a consumer wants to do, we have to talk about what they can do. This lecture is about the second question. The thing that stops you from buying a private jet isn’t lack of desire — it’s the budget. So we want a clean way of writing down “the set of affordable bundles.” Once that’s in hand, every later result in consumer theory will follow the same pattern: combine what’s feasible (budget set) with what’s desired (preferences) and pick the best feasible option.
That pattern — agents have goals, they choose actions to pursue those goals, they face constraints that make the goals hard to reach — is the backbone of every model in the course. Today we set up the constraint side.
Bundles, prices, and what “affordable” means
A consumption bundle is a list of how much of each good you consume: \[ (x_1, x_2, \ldots, x_n). \] If there are \(n\) goods, there are \(n\) entries. The prices are \[ (p_1, p_2, \ldots, p_n). \] The total amount you’d spend on the bundle is just the sum of expenditures on each good: \[ p_1 x_1 + p_2 x_2 + \cdots + p_n x_n. \] You can afford the bundle as long as that total doesn’t exceed your income \(m\). Two definitions worth keeping separate:
- Budget set — the set of bundles you can afford: \[ B(p_1, \ldots, p_n, m) = \{(x_1, \ldots, x_n) \geq 0 : p_1 x_1 + \cdots + p_n x_n \leq m\}. \]
- Budget constraint — the boundary of that set, where you spend every dollar: \[ p_1 x_1 + \cdots + p_n x_n = m. \]
The constraint is just one piece of the budget set — the edge. Every bundle on the constraint is also in the budget set, but not vice versa: the interior bundles (where you don’t spend everything) are also affordable.
The two-good picture
Most of microeconomics is taught in two dimensions. That’s not because real consumers buy two goods. It’s because almost everything generalizes from two, and two fits on a chalkboard. (Later we’ll see that the “second good” can stand in for “everything else.”)
With two goods the constraint is \[ p_1 x_1 + p_2 x_2 = m. \] Solve for \(x_2\): \[ x_2 \;=\; \frac{m}{p_2} \;-\; \frac{p_1}{p_2} x_1. \] A line. So the budget constraint is a straight line with three landmarks you should be able to read off without thinking:
| Landmark | Value | What it means |
|---|---|---|
| Vertical intercept | \(m/p_2\) | “Spend everything on good 2” — most \(x_2\) you can buy |
| Horizontal intercept | \(m/p_1\) | “Spend everything on good 1” — most \(x_1\) you can buy |
| Slope | \(-p_1/p_2\) | Trade-off the market lets you make |
What the slope means
The slope \(-p_1/p_2\) has a clean interpretation: it’s the opportunity cost of good 1 in units of good 2. Move one unit to the right (buy one more unit of \(x_1\)) and you must move down by \(p_1/p_2\) (give up that much \(x_2\)) to stay affordable. If apples cost twice as much as bananas, an extra apple costs you two bananas.
(Equivalently, going the other way: an extra unit of \(x_2\) costs \(p_2/p_1\) units of \(x_1\). Same trade, told from the other side.)
#| standalone: true
#| viewerHeight: 620
library(shiny)
ui <- fluidPage(
titlePanel("Budget Set Explorer"),
sidebarLayout(
sidebarPanel(
width = 4,
sliderInput("m", "Income (m):", min = 20, max = 200, value = 100, step = 10),
sliderInput("p1", "Price of good 1 (p₁):", min = 1, max = 20, value = 5, step = 1),
sliderInput("p2", "Price of good 2 (p₂):", min = 1, max = 20, value = 10, step = 1),
hr(),
checkboxInput("show_old", "Overlay the previous budget line", value = TRUE),
htmlOutput("readout")
),
mainPanel(
width = 8,
plotOutput("budget_plot", height = "480px")
)
)
)
server <- function(input, output, session) {
prev <- reactiveValues(m = 100, p1 = 5, p2 = 10)
observeEvent(list(input$m, input$p1, input$p2), {
isolate({
prev$m_old <- prev$m
prev$p1_old <- prev$p1
prev$p2_old <- prev$p2
prev$m <- input$m
prev$p1 <- input$p1
prev$p2 <- input$p2
})
})
output$budget_plot <- renderPlot({
m <- input$m; p1 <- input$p1; p2 <- input$p2
x1_intercept <- m / p1
x2_intercept <- m / p2
xmax <- max(220 / 5, x1_intercept) * 1.1
ymax <- max(220 / 5, x2_intercept) * 1.1
par(mar = c(4.2, 4.5, 1, 1))
plot(NA, xlim = c(0, xmax), ylim = c(0, ymax),
xlab = expression(x[1]), ylab = expression(x[2]), main = "")
polygon(c(0, x1_intercept, 0), c(0, 0, x2_intercept),
col = adjustcolor("#3498db", 0.18), border = NA)
if (isTRUE(input$show_old) &&
(prev$m_old != m || prev$p1_old != p1 || prev$p2_old != p2)) {
segments(0, prev$m_old / prev$p2_old,
prev$m_old / prev$p1_old, 0,
col = "#bdc3c7", lwd = 2, lty = 2)
}
segments(0, x2_intercept, x1_intercept, 0, col = "#185FA5", lwd = 3)
points(c(x1_intercept, 0), c(0, x2_intercept), pch = 19, col = "#185FA5")
text(x1_intercept, 0, sprintf("m/p₁ = %.1f", x1_intercept),
pos = 3, cex = 0.9, col = "#185FA5")
text(0, x2_intercept, sprintf("m/p₂ = %.1f", x2_intercept),
pos = 4, cex = 0.9, col = "#185FA5")
legend("topright",
legend = c("Current budget line", "Budget set (affordable)",
if (isTRUE(input$show_old)) "Previous budget line" else NULL),
col = c("#185FA5", adjustcolor("#3498db", 0.4),
if (isTRUE(input$show_old)) "#bdc3c7" else NULL),
lwd = c(3, NA, if (isTRUE(input$show_old)) 2 else NULL),
lty = c(1, NA, if (isTRUE(input$show_old)) 2 else NULL),
pch = c(NA, 15, if (isTRUE(input$show_old)) NA else NULL),
pt.cex = 1.6,
bty = "n", cex = 0.9)
})
output$readout <- renderUI({
m <- input$m; p1 <- input$p1; p2 <- input$p2
HTML(sprintf(paste(
"<div style='margin-top:10px;font-size:13px;line-height:1.7;'>",
"<b>Constraint:</b> %d·x₁ + %d·x₂ = %d<br>",
"<b>Slope:</b> %.2f<br>",
"<b>Max x₁:</b> %.1f <b>Max x₂:</b> %.1f",
"</div>"
), p1, p2, m, -p1 / p2, m / p1, m / p2))
})
}
shinyApp(ui, server)
Comparative statics: income vs. price
Two kinds of shifts to keep straight.
Income changes are parallel shifts. The slope is \(-p_1/p_2\), which has no \(m\) in it — so changing \(m\) doesn’t change the slope. A larger \(m\) pushes both intercepts outward by the same factor; the new constraint is parallel to the old one. Lower income, same story going the other way. Higher income gives you more choice. Lower income gives you less.
Own-price changes are pivots. If \(p_1\) falls, the \(x_2\) intercept \(m/p_2\) doesn’t budge, but \(m/p_1\) shoots out — the constraint pivots outward around the \(x_2\) axis and gets flatter (slope \(-p_1/p_2\) shrinks in magnitude). Old bundles stay affordable; new ones appear. A price increase does the reverse: pivot inward, slope steepens.
If you like having more options, you’d rather have lots of income facing low prices.
Numéraire and relative prices
Only relative prices and real income matter, not the units they’re measured in. If you multiply every price and income by the same positive constant \(c\) — measuring everything in cents instead of dollars, say — the constraint \(p_1 x_1 + p_2 x_2 = m\) becomes \((c p_1) x_1 + (c p_2) x_2 = c m\), which is exactly the same line after dividing through by \(c\). The set of affordable bundles doesn’t change.
This invariance lets us pick any one good to be the numéraire — the unit of account — by setting its price to 1. If we set \(p_1 = 1\), then \(p_2\) becomes “the price of good 2 measured in units of good 1.” The relative price \(p_2/p_1 = 3/2\) tells you that you’d give up 1.5 units of good 1 for an extra unit of good 2. Same exchange rate, expressed in real terms instead of dollars.
This isn’t a mathematical curiosity. The fact that only relative prices matter is the reason that, in macro, doubling all prices and all wages overnight changes nothing real. The budget set is unchanged.
Two goods is enough
You might worry that “two goods” is an absurd simplification of consumer choice. It isn’t. Pick any one good you care about — call it good 1. Lump everything else the consumer might spend their income on into a composite “other stuff” good 2, with price normalized to 1. Then \(p_1 x_1 + x_2 = m\) — the same algebra, but \(x_2\) is now “dollars spent on everything else.” Two-good geometry, n-good reality.
The full \(n\)-commodity budget set is a simplex (a triangle in three dimensions, a tetrahedron in four, etc.). It’s never visualized after \(n = 3\) but the algebra works identically.
When the constraint isn’t a straight line
The picture we’ve been drawing has a constant slope \(-p_1/p_2\). That requires constant prices — you pay the same per unit regardless of how much you buy. Real markets often violate this.
Quantity discounts. Suppose \(p_2 = \$1\) always, but \(p_1 = \$2\) for the first 20 units and $1 for every additional unit. The constraint now has a kink at \(x_1 = 20\): slope \(-2\) for \(x_1 \leq 20\), slope \(-1\) thereafter. Buying in bulk gets cheaper, so the constraint flattens past the threshold.
Quantity penalties. A “buy too much and we charge more” pricing scheme does the opposite — the constraint steepens above some threshold.
Multiple constraints. Sometimes a consumer faces more than just a budget. Maybe at least 10 units of food are needed to survive (\(x_1 \geq 10\)). Maybe time is also limited and producing each good takes some hours, capping the total. The effective choice set is the intersection of all the constraints. Whatever you can do, you must satisfy every constraint at once.
The general lesson — a bundle is feasible only if it satisfies every constraint — generalizes far beyond this page and shows up in producer theory, intertemporal choice, and labor supply.
Did you know?
- The “budget set” and “budget constraint” are formally different objects — the set vs. its boundary. A bundle inside the budget set is affordable but doesn’t exhaust income; a bundle on the budget constraint exhausts it exactly.
- The “two-good with composite outside good” trick is what lets economics keep pictures relevant in a thousand-good world. We’ll use it again in Choice, Demand, and Intertemporal Choice.
- Quantity discounts and penalties give kinked constraints that we’ll revisit when we discuss Slutsky decomposition and policy interventions — many real-world programs (food stamps, EITC, marginal income tax brackets) impose exactly this kind of kink on the budget set.