Contents
Deep Breakdown
What it tells me (Situation Awareness): When crosses zero inside , the plain definite integral computes signed area: positive parts add, negative parts subtract, and they can cancel. The absolute area formula ignores the sign at every point and always sums non-negative contributions. In DA/DS this distinction matters constantly: Mean Absolute Error (MAE) is the expected value of , not the expectation of the raw residual (which would be near zero for an unbiased model). Total variation of a signal, the L1 norm of a function over an interval, and Wasserstein-1 distance all involve this absolute area concept. If you use the signed integral when you need absolute area you will systematically undercount error and misread model performance.
Flowchart
Real-world use
Where it is used
Any time a DA/DS metric computes total deviation without caring about direction: residual analysis (MAE, MAPE), total variation of a KPI time series, signal processing (L1 norm of a waveform), L1 regularization penalty as a functional, Wasserstein-1 distance between two continuous distributions, and testing for mean-reversion by measuring total oscillation.
Problem it solves
The plain definite integral underestimates total error whenever positive and negative residuals cancel. Consider a model that overshoots by 5 and undershoots by 5: the signed integral of residuals is 0 (looks perfect), but the absolute area is 10 (reveals real deviation). The absolute area formula always returns the correct total magnitude.
How it solves the problem
By wrapping in absolute value before integrating: flips all negative portions to positive. In practice: (1) find where crosses zero, (2) split the integral at those crossings, (3) compute each piece separately, (4) add magnitudes. Numerically, scipy.integrate.quad(lambda x: abs(f(x)), a, b) handles it automatically.
Example in a business
A revenue forecasting model has residuals that oscillate around zero. Total signed error (bias is low, model is well-calibrated). But total absolute error is large — the model has high variance, swinging wildly each day. MAE per day = . Reporting only signed totals would hide the true inaccuracy of the model.
The formula, piece by piece
Each symbol:
- : the total unsigned area, always
- : integrate from to
- : absolute value of at each — negative portions are flipped above the axis
- : infinitesimal width, is the variable Key identity (triangle inequality for integrals):
Equality holds only when does not change sign on .
The formula in words
"At every point , take the height of the curve but always make it non-negative (flip it if it is below zero). Then integrate those non-negative heights from to . This gives the true total area swept out by the curve, regardless of whether the curve dips below the axis."
Critical Concepts
- Must find zeros first: split the interval at each zero crossing; if you skip this and just compute , you get the signed area, not the absolute area.
- Triangle inequality for integrals: . Signed area absolute area always.
- MAE connection: For a continuous error distribution with density , .
- L1 norm of a function: . This is the functional analogue of the L1 vector norm.
- Total variation: A discrete approximation is ; the continuous version is .
- Computational note:
scipy.integrate.quadhandles the absolute value correctly by adaptive subdivision, but it helps to pass the known breakpoints (zeros) explicitly for efficiency.
Step-by-step calculation (tiny example)
Compare (signed) vs. (absolute).
Step 1: Signed integral (naive) This combines (area below axis from to ) and (area above axis from to ), yielding .
Step 2: Find zeros of on is the only zero. Split at : sub-intervals and .
Step 3: Compute each sub-interval separately On : , so . On : , so .
Step 4: Sum the absolute pieces The absolute area (2.5) is larger than the signed area (1.5) because the below-axis piece was subtracted in the signed version but added here.
Code examples
Beginner example (Python)
import numpy as np
from scipy import integrate
def f(x: float) -> float:
"""Simple function that crosses zero: f(x) = x."""
return float(x) # Signed area: cancellation occurs
area_signed, _ = integrate.quad(f, -1, 2) # Absolute area: pass the known breakpoint (zero at x=0) for efficiency
area_absolute, _ = integrate.quad(lambda x: abs(f(x)), -1, 2, points=[0.0])
print(f"Signed area (int x dx from -1 to 2): {area_signed:.4f}") # 1.5
print(f"Absolute area (int |x| dx from -1 to 2): {area_absolute:.4f}") # 2.5
print(f"Triangle inequality holds: {area_absolute >= abs(area_signed)}") # True # Manual split to show the mechanics
piece_left, _ = integrate.quad(lambda x: abs(f(x)), -1, 0)
piece_right, _ = integrate.quad(lambda x: abs(f(x)), 0, 2)
print(f"Left piece ([-1,0]): {piece_left:.4f}") # 0.5
print(f"Right piece ([0,2]): {piece_right:.4f}") # 2.0
print(f"Sum: {piece_left + piece_right:.4f}") # 2.5Beginner example (R)
f <- function(x) x # f crosses zero at
x = 0 # Signed area
area_signed <- integrate(f, lower = -1, upper = 2)$value
cat("Signed area:", area_signed, "\n") # 1.5 # Absolute area: pass subdivisions to help the integrator
area_absolute <- integrate(function(x) abs(f(x)), lower = -1, upper = 2, subdivisions = 200L)$value
cat("Absolute area:", area_absolute, "\n") # 2.5 # Triangle inequality
cat("Triangle inequality holds:", area_absolute >= abs(area_signed), "\n") # TRUE # Manual split
piece_left <- integrate(function(x) abs(f(x)), lower = -1, upper = 0)$value
piece_right <- integrate(function(x) abs(f(x)), lower = 0, upper = 2)$value
cat("Left piece: ", piece_left, "\n") # 0.5
cat("Right piece: ", piece_right, "\n") # 2.0
cat("Sum: ", piece_left + piece_right, "\n") # 2.5Professional example (Python)
"""
MAE as absolute area under the error density curve. In regression, residuals
e = y_true - y_pred form a distribution.
MAE = E[|e|] = integral_{-inf}^{inf} |e| * p_e(e)
de = int_0^inf e * p_e(e) de + int_{-inf}^0 (-e) * p_e(e)
de = int_0^inf (pos piece) + int_{-inf}^0 (neg piece, flipped) We demonstrate that:
1. sklearn MAE on discrete residuals matches
2. scipy.integrate.quad on fitted KDE of residuals matches
3. Signed integral of residuals is near zero (unbiasedness), NOT MAE.
"""
import numpy as np
from scipy import integrate
from scipy.stats import gaussian_kde
from sklearn.datasets import make_regression
from sklearn.linear_model import Ridge
from sklearn.metrics import mean_absolute_error
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
RANDOM_SEED = 42
rng = np.random.default_rng(RANDOM_SEED) # Reproducible regression dataset
x_data, y_data = make_regression( n_samples=1_000, n_features=15, noise=25.0, random_state=RANDOM_SEED )
x_train, x_test, y_train, y_test = train_test_split( x_data, y_data, test_size=0.3, random_state=RANDOM_SEED )
scaler = StandardScaler()
x_train_scaled = scaler.fit_transform(x_train)
x_test_scaled = scaler.transform(x_test)
model = Ridge(alpha=1.0)
model.fit(x_train_scaled, y_train)
y_pred = model.predict(x_test_scaled)
residuals = y_test - y_pred # 1. Discrete MAE (sklearn)
mae_discrete = mean_absolute_error(y_test, y_pred) # 2. Signed integral of residuals (should be near zero: unbiased model)
signed_sum = float(np.mean(residuals)) # E[e] ≈ 0 # 3. Continuous MAE via KDE density + absolute area integral
kde = gaussian_kde(residuals, bw_method="scott")
e_min = float(residuals.min()) - 10.0
e_max = float(residuals.max()) + 10.0 # Signed integral of e * p(e): should be ~0 (unbiasedness)
signed_expectation, _ = integrate.quad( lambda e: e * kde(e)[0], e_min, e_max ) # Absolute area: E[|e|] = integral |e| * p(e) de -- TRUE MAE
mae_continuous, _ = integrate.quad( lambda e: abs(e) * kde(e)[0], e_min, e_max, points=[0.0] )
print(f"Signed mean residual (E[e]): {signed_sum:+.4f} <- near 0 (unbiased)")
print(f"Signed integral via KDE (E[e]): {signed_expectation:+.4f} <- near 0")
print(f"MAE (sklearn discrete): {mae_discrete:.4f}")
print(f"MAE (KDE absolute integral): {mae_continuous:.4f}")
print(f"Triangle ineq holds: |E[e]| <= E[|e|]: " f"{abs(signed_expectation) <= mae_continuous + 1e-6}")
# Key lesson: E[e] ≈ 0 does NOT mean MAE ≈ 0. Absolute area captures true error.Source: domain knowledge