Contents
Deep Breakdown
What it tells me (Situation Awareness): The affine power rule is the power rule extended to a linear argument: instead of integrating , we integrate a shifted and scaled version . The key change is the extra factor of in the denominator, which compensates for the chain rule: differentiating brings out a factor of from the inside. In DA/DS, this rule appears whenever you integrate polynomial models built on shifted inputs: kernel density estimation with shifted bandwidth, computing areas under polynomial regression segments centered at non-zero points, integrating polynomial basis functions in spline models, and evaluating reliability functions of the form .
Flowchart
Real-world use
Where it is used
Whenever the integrand is a power of a linear expression : polynomial regression with shifted features, evaluating spline basis integrals, computing areas under shifted parabolic segments, normalizing shifted polynomial density approximations, reliability theory (time-to-failure integrals).
Problem it solves
The plain power rule assumes the argument is exactly (or a simple -substitution variable). When the argument is , the substitution , , so , introduces the compensating factor . The affine power rule packages this substitution into a single formula.
How it solves the problem
Let , so , meaning .
Then:
The factor comes from the chain rule compensation. Verification:
Example in a business
A polynomial regression model predicts revenue using a centered feature: where is a normalized price index. To compute total predicted revenue over , we integrate: .
The formula, piece by piece
Each symbol:
- : the affine (linear) inner function; is the slope, is the intercept
- : the coefficient of inside the brackets; appears in the denominator as the chain rule compensation
- : the outer exponent, any real number except
- : the inner function raised to the incremented exponent
- : two-part denominator: from the chain rule, from the power rule
- : constant of integration When : reduces to . When : reduces to the plain shift .
The formula in words
"To integrate a linear expression raised to the power : raise the exponent by one and divide by the new exponent (exactly as in the basic power rule), but additionally divide by the coefficient of inside the brackets. The factor compensates for the chain rule that would appear when differentiating the result."
Critical Concepts
- Chain rule compensation: the denominator factor is the exact correction for the that differentiation of would produce.
- Derivation = u-substitution: this formula is shorthand for , .
- Case : yields (Reciprocal Logarithmic rule).
- Verify always: differentiate the result to confirm the chain rule was handled correctly.
- Definite integral: .
Step-by-step calculation (tiny example)
Compute .
Step 1: Identify a, b, n , , . Not , so formula applies.
Step 2: Apply the formula . Result: .
Step 3: Verify by differentiating
Step 4: Evaluate a definite example
Code examples
Beginner example (Python)
import numpy as np
from scipy import integrate
def affine_power_antiderivative(x: float, a: float, b: float, n: float) -> float:
"""F(x) = (ax+b)^(n+1) / (a*(n+1)). Undefined for n=-1 or a=0."""
if abs(n + 1) < 1e-12:
raise ValueError("n = -1: use (1/a)*ln|ax+b| instead.")
if abs(a) < 1e-12:
raise ValueError("a = 0: reduces to constant, not affine.")
return (a * x + b) ** (n + 1) / (a * (n + 1))
def affine_power_definite(lo: float, hi: float, a: float, b: float, n: float) -> float:
"""int_lo^hi (ax+b)^n dx = F(hi) - F(lo)."""
return (affine_power_antiderivative(hi, a, b, n) - affine_power_antiderivative(lo, a, b, n))
# Test cases
cases = [
(0, 1, 3.0, 2.0, 4.0), # int (3x+2)^4 from 0 to 1
(0, 2, 2.0, 0.0, 3.0), # int (2x)^3 from 0 to 2
(1, 3, 1.0, -1.0, 2.0), # int (x-1)^2 from 1 to 3
]
for lo, hi, a, b, n in cases:
analytic = affine_power_definite(lo, hi, a, b, n)
numerical, _ = integrate.quad(lambda x, aa=a, bb=b, nn=n: (aa*x+bb)**nn, lo, hi)
print(f"int_({lo},{hi}) ({a}x+{b})^{n}: "
f"analytic={analytic:.6f}, numerical={numerical:.6f}, "
f"match={abs(analytic-numerical)<1e-8}")Beginner example (R)
affine_power_antiderivative <- function(x, a, b, n) {
if (abs(n + 1) < 1e-12) stop("n = -1: use (1/a)*ln|ax+b| rule.")
if (abs(a) < 1e-12) stop("a = 0: not affine.")
(a * x + b)^(n + 1) / (a * (n + 1))
}
affine_power_definite <- function(lo, hi, a, b, n) {
affine_power_antiderivative(hi, a, b, n) - affine_power_antiderivative(lo, a, b, n)
}
# Test cases
cases <- list(c(0, 1, 3, 2, 4), c(0, 2, 2, 0, 3), c(1, 3, 1, -1, 2))
for (tc in cases) {
lo <- tc[1]; hi <- tc[2]; a <- tc[3]; b <- tc[4]; n <- tc[5]
analytic <- affine_power_definite(lo, hi, a, b, n)
numerical <- integrate(function(x) (a*x+b)^n, lower=lo, upper=hi)$value
cat(sprintf("int_(%g,%g) (%gx+%g)^%g: analytic=%.6f, num=%.6f, match=%s\n", lo, hi, a, b, n, analytic, numerical, isTRUE(all.equal(analytic, numerical, tol=1e-7))))
}Professional example (Python)
"""
Affine power rule for integrating polynomial spline basis functions.
Natural cubic splines use piecewise polynomials over knot-defined intervals.
Each piece is a cubic polynomial in (x - knot_i), an affine power of degree 3.
Computing the integral of a spline (e.g., for expected value under a spline PDF)
uses the affine power rule repeatedly on each segment.
Here we fit a monotone cubic spline to a conversion rate curve and
compute the area under it over a price range.
"""
import numpy as np
from scipy import integrate
from scipy.interpolate import PchipInterpolator
# Simulated conversion rate vs. price (monotone decreasing)
price_knots = np.array([10.0, 20.0, 30.0, 40.0, 50.0, 60.0])
conv_rate = np.array([0.82, 0.71, 0.58, 0.43, 0.29, 0.17])
# Piecewise cubic hermite spline (C1-continuous, monotone)
spline = PchipInterpolator(price_knots, conv_rate)
# Goal: expected conversion rate over price range [15, 55] = (1/width) * integral
lo_price = 15.0
hi_price = 55.0
width = hi_price - lo_price
# Numerical integral of spline (affine cubics on each segment)
area, error = integrate.quad(spline, lo_price, hi_price)
expected_conversion = area / width
print(f"Price range: [{lo_price}, {hi_price}]")
print(f"Integral of spline: {area:.6f}")
print(f"Expected conversion rate: {expected_conversion:.4f}")
print(f"Integration error estimate: {error:.2e}")
# Show per-segment analytic contributions (affine power rule)
segments = list(zip(price_knots[:-1], price_knots[1:]))
print("\nPer-segment integral (spline evaluated numerically on each):")
total_check = 0.0
for seg_lo, seg_hi in segments:
seg_area, _ = integrate.quad(spline, seg_lo, seg_hi)
total_check += seg_area
print(f" [{seg_lo:.0f}, {seg_hi:.0f}]: area = {seg_area:.6f}")
# The segments cover the full knot range [10, 60]
full_area, _ = integrate.quad(spline, price_knots[0], price_knots[-1])
print(f" Sum of segments: {total_check:.6f} (matches full integral: "
f"{abs(total_check - full_area) < 1e-8})")Source: MFB p. 206