KPSS Test Calculator
Test whether a time series is stationary with the Kwiatkowski-Phillips-Schmidt-Shin (KPSS) test. Paste your data, pick level or trend stationarity, and get the KPSS statistic, p-value, critical values, four diagnostic charts and ready-to-paste APA wording, with matching Excel, R and Python code.
By StatsUnlock Team · Methods checked against R tseries and Python statsmodels · Last updated
⚡ 0. Quick Answer
The KPSS test checks whether a time series is stationary. Its null hypothesis is stationarity, so a large statistic means the series is not stationary.
Fit a mean (level) or a straight line (trend), add up the residuals over time, and compare the size of those running sums with the long-run variance. Reject stationarity at 5% when η is above 0.463 for level, or above 0.146 for trend.
Worked example: 24 months of rainfall anomaly give η = 0.0856 with lag 2. That is below 0.463, so p > 0.10 and the series is level-stationary.
Key Takeaways
- The KPSS test has the opposite null to the ADF test: H0 = stationary.
- 5% critical values are 0.463 (level) and 0.146 (trend), from Kwiatkowski et al. (1992).
- The default lag is trunc(4(T/100)1/4), the same rule R's
tseries::kpss.testuses. - p-values come from a table, so software reports p > 0.10 or p < 0.01 at the ends.
- Always pair KPSS with ADF: agreement between the two is the strongest evidence you can report.
📚 1. What Is the KPSS Test?
The KPSS test (Kwiatkowski, Phillips, Schmidt and Shin, 1992) is a test for stationarity. A stationary series has a constant mean, a constant variance, and the same correlation pattern at every point in time. Most forecasting models, such as ARIMA and VAR, assume this.
What it measures: the test removes a mean (level version) or a straight line (trend version) from the data. It then adds up the leftover residuals one step at a time. If the series is stationary, these running sums wander a little and come back to zero. If the series has a unit root, the sums drift far away. η is the size of that drift, scaled by the long-run variance.
Two versions:
- Level stationarity (ημ): the series moves around a fixed mean.
- Trend stationarity (ητ): the series moves around a fixed straight-line trend.
A worked one-liner: rainfall anomaly over 24 months, mean 13.54 mm, sum of squared running sums 67.34, long-run variance 1.366, so η = 67.34 ÷ (576 × 1.366) = 0.0856, well under 0.463.
Who uses it: economists before ARIMA, cointegration and VAR models; ecologists testing population counts and camera-trap indices; hydrologists checking streamflow; climate scientists checking temperature anomalies; finance analysts checking returns and spreads.
| Feature | KPSS test | ADF test | Phillips-Perron test |
|---|---|---|---|
| Null hypothesis | Stationary | Unit root (non-stationary) | Unit root (non-stationary) |
| Reject means | Non-stationary | Stationary | Stationary |
| Handles autocorrelation by | Long-run variance (kernel) | Lagged differences | Long-run variance (kernel) |
| Main weakness | Over-rejects with strong autocorrelation or breaks | Low power near a unit root | Size problems with MA errors |
| Best use | Confirmation test beside ADF | Default unit-root test | Alternative to ADF |
🧮 2. Set Up Your Data
Type one value per row, in time order. Empty rows are ignored.
| t | Value |
|---|
📊 3. Results
💡 4. Interpretation of Results, In Detail
▶ Run the analysis above to fill 4.1 to 4.4 with your own numbers. The run will replace every ___ placeholder.
4.1 What was found
η = ___, critical value = ___, decision ___.
4.2 What the p-value means
p = ___.
4.3 Lag choice and sensitivity
Lag = ___.
4.4 What to do next
___
4.5 Reading the KPSS statistic
η has no units. It compares how far the running sums travel with how noisy the data are. Values near 0.05 to 0.15 are typical of clearly stationary series. Values above 1 usually mean a strong trend or a unit root.
4.6 Decision guide: KPSS with ADF
| ADF result | KPSS result | Conclusion | Action |
|---|---|---|---|
| Rejects unit root | Does not reject stationarity | Stationary | Model in levels |
| Does not reject | Rejects stationarity | Unit root | Difference once and test again |
| Rejects | Rejects | Conflict | Look for breaks, long memory or a trend you missed |
| Does not reject | Does not reject | Not enough data | Both tests lack power; collect more data or use theory |
4.7 Level result vs trend result
If the level test rejects but the trend test does not, the series is trend-stationary. Remove the straight line with a linear regression (detrend), do not difference. If both reject, the series is difference-stationary: take first differences.
4.8 Common mistakes this calculator prevents
- Reading the p-value the ADF way round. Here a small p means non-stationary.
- Reporting p = 0.10 as if it were exact. The calculator shows p > 0.10 when η is below the 10% value.
- Forgetting to report the lag. The lag changes η, so the write-up templates always include it.
- Using level critical values for a trend test. The calculator switches tables automatically.
✍ 5. How to Write Your Results in Research
▶ Run the analysis above to auto-fill all five examples with your results.
📌 Key conventions for this style
- Name the version: level or trend.
- Report the statistic, the lag and the kernel.
- Write p > .10 or p < .01 at the table ends; never p = .000.
- Say what the null was, because KPSS is the opposite of ADF.
📌 Key conventions for this style
- Cite Kwiatkowski et al. (1992) and your lag rule (Schwert, 1989).
- Report ADF beside KPSS.
- State what you did next: model in levels, detrend, or difference.
- Name the software and version.
📌 Key conventions for this style
- No symbols. Say "stable over time" for stationary.
- Say the practical consequence.
- Three or four short sentences.
📌 Key conventions for this style
- Background, Methods, Results, Conclusion.
- Put n, lag and alpha in Methods.
- The Conclusion states the modelling consequence.
📌 Key conventions for this style
- Fix the lag rule and kernel before seeing the data.
- Report the exact decision rule.
- Give the package and function, for example tseries::kpss.test.
- Link data and code (OSF, GitHub, Zenodo).
∑ 6. Formulas Used
🔀 7. KPSS Test Calculation Flowchart, Step by Step
This flowchart shows how the KPSS test is calculated, step by step, for the worked example: 24 months of rainfall anomaly (mm), tested for level stationarity. The same numbers appear in Sections 9, 10, 11 and 13.
Excel (Section 9), R (Section 10) and Python (Section 11) all return 0.0856 for this series with lag 2.
📖 8. How to Use This Calculator
- Load or paste your data. Paste values in time order, or load sample 1 (rainfall anomaly, 24 months).
- Name the series. Click the name box and type a label; it is used in every report.
- Or upload a file. Use the Upload tab for CSV or Excel and click the numeric column you want.
- Choose level or trend. Level for a flat series, trend for a steadily rising or falling one.
- Set alpha. 0.05 is standard. 0.10, 0.025 and 0.01 are available.
- Pick the lag rule. Keep the short rule to match R; try the long rule to check sensitivity.
- Choose the kernel. Keep Bartlett unless you have a reason to use rectangular.
- Press Run KPSS Test. For sample 1 you get KPSS = 0.0856, lag 2, p > 0.10.
- Read the verdict and charts. Check the running-sum chart and the assumption table.
- Copy the write-up. Copy an APA, thesis or plain-language sentence from Section 5, or download the Doc or PDF report.
📈 9. How to Run the KPSS Test in Excel
Excel has no KPSS function, and the Analysis ToolPak does not include one either. You can still build it with ordinary formulas in about five columns. The steps below reproduce η = 0.0856 for the rainfall example.
=SUMSQ(C2:C25)/(COUNT(A2:A25)^2*H5) where H5 holds the long-run variance.9.1 The functions you need
| You want | Excel formula | Note |
|---|---|---|
| Mean | =AVERAGE($A$2:$A$25) | Level version only |
| Trend residual | =A2-TREND($A$2:$A$25,$D$2:$D$25,D2) | D holds t = 1 to 24 |
| Running sum | =SUM($B$2:B2) | Fill down |
| γ̂0 | =SUMSQ(B2:B25)/24 | Divide by T, not T − 1 |
| γ̂j | =SUMPRODUCT(B3:B25,B2:B24)/24 | Shift the second range by j rows |
| Lag | =TRUNC(4*(24/100)^0.25) | Short Schwert rule |
SUMSQ/COUNT or VAR.P. Using VAR.S makes η about 4% smaller for T = 24.9.2 Step by step
1 Data and residuals
Paste the 24 values into A2:A25. In B2 type the residual formula and fill down.
=A2-AVERAGE($A$2:$A$25)The mean is 13.5417, so the first residual is 12 − 13.5417 = −1.5417.
2 Running sums
In C2 type a sum with an anchored start and fill down. C25 should come out as 0.
=SUM($B$2:B2)S3 = −2.6250. Then =SUMSQ(C2:C25) gives 67.3403.
3 Autocovariances and long-run variance
Compute γ̂0, γ̂1, γ̂2, then combine them with Bartlett weights.
=H2+2*(1-1/3)*H3+2*(1-2/3)*H43.6649, −1.4532 and −0.5418 combine to a long-run variance of 1.3661.
4 The KPSS statistic and decision
Divide, then compare with the critical value.
=IF(H6>0.463,"Reject: non-stationary","Keep: stationary")η = 0.0856, below 0.463, so the series is level-stationary.
9.3 The awkward cases
For the trend version, put t in column D and use TREND in column B instead of AVERAGE. For a longer lag, add one more SUMPRODUCT row per lag and one more weighted term in the long-run variance formula. Blank cells break the shifted ranges, so remove gaps first.
e (Formulas, Define Name). Then γ̂1 reads =SUMPRODUCT(OFFSET(e,1,0,23),OFFSET(e,0,0,23))/24 and is easier to check.9.4 Why your answer might differ across software
| Software | Default lag | η for the example | p-value display |
|---|---|---|---|
| This calculator | trunc(4(T/100)1/4) = 2 | 0.0856 | p > 0.10 |
R tseries::kpss.test | Same, lshort = TRUE | 0.0856 | 0.1 with a warning |
R urca::ur.kpss | lags = "short", same rule | 0.0856 | No p-value, critical values only |
Python statsmodels kpss | nlags="auto" (Hobijn et al., 2004) | Differs unless you pass nlags=2 | 0.1 with InterpolationWarning |
| Excel (manual build) | Whatever you type | 0.0856 with lag 2 | You read the table |
The KPSS statistic itself is the same formula everywhere. Differences almost always come from the lag rule. Always state the lag you used.
9.5 Charting in Excel
Select column C and insert a Line chart. This is the running-sum plot from chart 3 of this calculator. A line that drifts away and does not come back is the visual sign of non-stationarity.
9.6 Error messages
| Error | Cause | Fix |
|---|---|---|
| #VALUE! | Text inside the data range | Remove labels or NA codes from A2:A25 |
| #N/A in SUMPRODUCT | Ranges of different length | Both ranges must have T − j rows |
| #DIV/0! | Long-run variance is zero | Data are constant; KPSS cannot be computed |
| C25 is not 0 | Mean range not anchored with $ | Use $A$2:$A$25 in the residual formula |
📈 10. How to Run the KPSS Test in R
R has two standard KPSS functions: kpss.test() in the tseries package and ur.kpss() in urca. The script below runs both, adds an ADF test, prints a plain-English verdict, and saves one 300 dpi figure.
y <- c(...) line. Run install.packages(c("tseries","urca")) once first.10.1 The complete script
R, tseries + urca, saves kpss_plot.png
# KPSS stationarity test with ADF cross-check
# StatsUnlock - https://statsunlock.com/kpss-test-calculator/
library(tseries)
library(urca)
set.seed(1)
# ---- 1. Your data (time order, earliest first) ----
# y <- read.csv("data.csv")$value
# y <- y[!is.na(y)]
y <- c(12,15,11,14,16,13,10,14,17,12,15,13,
11,16,14,12,15,13,17,12,14,11,15,13)
null_type <- "Level" # or "Trend"
# ---- 2. KPSS (tseries) ----
k <- kpss.test(y, null = null_type, lshort = TRUE)
print(k) # KPSS Level = 0.0856, Truncation lag parameter = 2
# ---- 3. Same test in urca, gives critical values ----
u <- ur.kpss(y, type = if (null_type == "Level") "mu" else "tau", lags = "short")
print(summary(u))
# ---- 4. ADF cross-check ----
a <- adf.test(y)
print(a)
# ---- 5. Plain-English verdict ----
cv5 <- if (null_type == "Level") 0.463 else 0.146
eta <- unname(k$statistic)
cat(sprintf("\nKPSS = %.4f (lag %d). 5%% critical value = %.3f.\n", eta, k$parameter, cv5))
if (eta > cv5) {
cat("Reject stationarity: difference or detrend before modelling.\n")
} else {
cat("Do not reject: the series is consistent with stationarity.\n")
}
# ---- 6. One 300 dpi figure: series + running sums ----
e <- if (null_type == "Level") y - mean(y) else resid(lm(y ~ seq_along(y)))
S <- cumsum(e)
png("kpss_plot.png", width = 7, height = 5, units = "in", res = 300)
par(mfrow = c(2, 1), mar = c(4, 4, 2, 1))
plot(y, type = "o", pch = 16, col = "#16a34a", xlab = "Time", ylab = "Value",
main = sprintf("KPSS %s = %.4f", null_type, eta))
lines(y - e, col = "#dc2626", lty = 2, lwd = 2)
plot(S, type = "l", lwd = 2, col = "#7c3aed", xlab = "Time", ylab = "Running sum S(t)")
abline(h = 0, lty = 3)
dev.off()10.2 What each line does
| Line | What it does |
|---|---|
kpss.test(y, null = "Level") | Runs KPSS. lshort = TRUE sets lag = trunc(4(T/100)1/4) |
ur.kpss(type = "mu") | Same statistic, prints the 10%, 5%, 2.5% and 1% critical values |
adf.test(y) | The opposite-null test for the decision table in 4.6 |
cumsum(e) | The running sums St used in the statistic |
png(..., res = 300) | Saves a journal-ready figure |
10.3 What the figure shows
The top panel is the series with the fitted mean or trend. The bottom panel is the running sum. For the rainfall data it stays within about ±3 and returns to zero, which is why η is small.
10.4 Problems and fixes
| Message | Meaning | Fix |
|---|---|---|
| p-value greater than printed p-value | η is below the 10% value | Report p > .10 |
| p-value smaller than printed p-value | η is above the 1% value | Report p < .01 |
| could not find function "kpss.test" | tseries not loaded | install.packages("tseries") |
| NAs in y | Missing values | Remove or impute them before the test |
10.5 One-liners
tseries::kpss.test(y, null = "Trend")
tseries::kpss.test(diff(y)) # test the differenced series
forecast::ndiffs(y, test = "kpss") # how many differences are needed📈 11. How to Run the KPSS Test in Python
In Python the KPSS test lives in statsmodels. The script below sets the lag to match R and this calculator, runs ADF beside it, prints a verdict and saves one 300 dpi figure.
pip install numpy matplotlib statsmodels11.1 The complete script
Python 3, statsmodels, saves kpss_plot.png
# KPSS stationarity test with ADF cross-check
# StatsUnlock - https://statsunlock.com/kpss-test-calculator/
import warnings
import numpy as np
import matplotlib.pyplot as plt
from statsmodels.tsa.stattools import kpss, adfuller
np.random.seed(1)
# ---- 1. Your data (time order, earliest first) ----
# import pandas as pd
# y = pd.read_csv("data.csv")["value"].dropna().to_numpy()
y = np.array([12,15,11,14,16,13,10,14,17,12,15,13,
11,16,14,12,15,13,17,12,14,11,15,13], dtype=float)
y = y[~np.isnan(y)]
regression = "c" # "c" = level, "ct" = trend
# ---- 2. Lag rule that matches R tseries ----
T = len(y)
lag = int(4 * (T / 100) ** 0.25) # = 2 for T = 24
# ---- 3. KPSS ----
with warnings.catch_warnings():
warnings.simplefilter("ignore") # p-value is table-limited
stat, p, used_lag, crit = kpss(y, regression=regression, nlags=lag)
print(f"KPSS = {stat:.4f}, lag = {used_lag}, p = {p}") # 0.0856, 2, 0.1
print("Critical values:", crit)
# ---- 4. ADF cross-check ----
adf_stat, adf_p, *_ = adfuller(y, autolag="AIC")
print(f"ADF = {adf_stat:.4f}, p = {adf_p:.4f}")
# ---- 5. Plain-English verdict ----
if stat > crit["5%"]:
print("Reject stationarity: difference or detrend before modelling.")
else:
print("Do not reject: the series is consistent with stationarity.")
# ---- 6. One 300 dpi figure ----
t = np.arange(1, T + 1)
fit = np.full(T, y.mean()) if regression == "c" else np.polyval(np.polyfit(t, y, 1), t)
S = np.cumsum(y - fit)
fig, ax = plt.subplots(2, 1, figsize=(7, 5), sharex=True)
ax[0].plot(t, y, "o-", color="#16a34a")
ax[0].plot(t, fit, "--", color="#dc2626")
ax[0].set_title(f"KPSS = {stat:.4f} (lag {used_lag})")
ax[1].plot(t, S, color="#7c3aed")
ax[1].axhline(0, ls=":", color="grey")
ax[1].set_xlabel("Time"); ax[1].set_ylabel("Running sum S(t)")
fig.tight_layout(); fig.savefig("kpss_plot.png", dpi=300)11.2 What each line does
| Line | What it does |
|---|---|
kpss(y, regression="c", nlags=lag) | Level KPSS. Use "ct" for trend |
nlags=lag | Forces the R lag rule. Leaving the default "auto" changes η |
crit["5%"] | The 5% critical value from the same table as this page |
adfuller(y) | The opposite-null unit-root test |
savefig(dpi=300) | Publication-quality figure |
11.3 What the figure shows
The same two panels as the R figure: the series with its fit, and the running sums. Both scripts give KPSS = 0.0856 with lag 2.
11.4 Problems and fixes
| Message | Meaning | Fix |
|---|---|---|
| InterpolationWarning: p-value is greater than the indicated p-value | η below the 10% critical value | Report p > .10 |
| InterpolationWarning: p-value is smaller | η above the 1% critical value | Report p < .01 |
| Answer differs from R | Default nlags="auto" | Pass the same lag as R |
| ValueError on NaN | Missing values | Drop or impute them first |
11.5 One-liners
kpss(y, regression="ct", nlags=2) # trend version
kpss(np.diff(y), regression="c") # after first differencing
from pmdarima.arima import ndiffs; ndiffs(y, test="kpss")📋 12. Reference Tables
Table 1. KPSS critical values (Kwiatkowski et al., 1992)
| Version | 10% | 5% | 2.5% | 1% |
|---|---|---|---|---|
| Level (ημ) | 0.347 | 0.463 | 0.574 | 0.739 |
| Trend (ητ) | 0.119 | 0.146 | 0.176 | 0.216 |
What to conclude: if η is above the value in your α column, reject stationarity.
Table 2. Default lag by sample size
| T | Short l4 | Long l12 | floor(3√T/13) |
|---|---|---|---|
| 24 | 2 | 8 | 1 |
| 50 | 3 | 10 | 1 |
| 100 | 4 | 12 | 2 |
| 200 | 4 | 14 | 3 |
| 500 | 5 | 17 | 5 |
| 1000 | 7 | 21 | 7 |
What to conclude: the long rule is safer with persistent data but weakens the test in short series.
Table 3. Reading η (level version)
| ημ | p (approx.) | Verdict |
|---|---|---|
| below 0.347 | > 0.10 | Stationary |
| 0.347 to 0.463 | 0.05 to 0.10 | Weak evidence against |
| 0.463 to 0.739 | 0.01 to 0.05 | Non-stationary at 5% |
| above 0.739 | < 0.01 | Strongly non-stationary |
What to conclude: values in the amber band call for a second test (ADF) and a look at the chart.
Table 4. Reading η (trend version)
| ητ | p (approx.) | Verdict |
|---|---|---|
| below 0.119 | > 0.10 | Trend-stationary |
| 0.119 to 0.146 | 0.05 to 0.10 | Weak evidence against |
| 0.146 to 0.216 | 0.01 to 0.05 | Non-stationary at 5% |
| above 0.216 | < 0.01 | Strongly non-stationary |
What to conclude: rejecting the trend version usually means a unit root, so difference the series.
Table 5. Minimum series length
| T | Reliability |
|---|---|
| under 20 | Poor: critical values are asymptotic and size is distorted |
| 20 to 49 | Fair: report with ADF and a plot |
| 50 to 99 | Good |
| 100 or more | Very good, but small drifts become significant |
What to conclude: under 20 points, treat the result as a hint, not a decision.
Table 6. Which transformation after KPSS
| Level test | Trend test | Do this |
|---|---|---|
| Not rejected | Not rejected | Use the series as it is |
| Rejected | Not rejected | Detrend (remove the straight line) |
| Rejected | Rejected | First difference, then test again |
| Not rejected | Rejected | Rare. Check for a break or a curved trend |
What to conclude: never difference a trend-stationary series; it adds a non-invertible MA term.
📈 13. Example Results
Eight worked KPSS examples. Each uses the default short lag and the Bartlett kernel. Example 1 is the same worked dataset used in Sections 7, 9, 10 and 11.
Rainfall anomaly, 24 months
The worked example from Sections 7, 9, 10 and 11.
Monthly rainfall anomaly in mm. Is the series stable around its mean?
| Statistic | Value | Note |
|---|---|---|
| T | 24 | Observations |
| Lag | 2 | trunc(4(T/100)^0.25) |
| Long-run variance | 1.3661 | Bartlett kernel |
| KPSS level | 0.0856 | Test statistic |
| 5% critical value | 0.463 | Kwiatkowski et al. (1992) |
| p-value | > 0.10 | Table interpolation |
What it means: η = 0.0856 is far below 0.463. The running sums stay near zero, so there is no evidence against level stationarity. Model the series in levels.
How to write it: "A KPSS level test did not reject stationarity, KPSS = 0.0856, lag = 2, p > 0.10."
Weekly stock price index
A trend tested as level stationarity.
52 weeks of a price index that rises almost every week.
| Statistic | Value | Note |
|---|---|---|
| T | 52 | Observations |
| Lag | 3 | trunc(4(T/100)^0.25) |
| Long-run variance | 188791.3346 | Bartlett kernel |
| KPSS level | 1.4001 | Test statistic |
| 5% critical value | 0.463 | Kwiatkowski et al. (1992) |
| p-value | < 0.01 | Table interpolation |
What it means: η = 1.4001 is above even the 1% value of 0.739. The mean line misses the data badly at both ends. Re-test with the trend version, then difference if needed.
How to write it: "KPSS rejected level stationarity, KPSS = 1.4001, lag = 3, p < 0.01."
Daily river flow, 50 days
The decision depends on alpha.
River flow with a short cycle and a slow fall in level.
| Statistic | Value | Note |
|---|---|---|
| T | 50 | Observations |
| Lag | 3 | trunc(4(T/100)^0.25) |
| Long-run variance | 1.4579 | Bartlett kernel |
| KPSS level | 0.7181 | Test statistic |
| 5% critical value | 0.463 | Kwiatkowski et al. (1992) |
| p-value | 0.012 | Table interpolation |
What it means: η = 0.7181 is above 0.463 (5%) but below 0.739 (1%). At α = .05 we reject; at α = .01 we would not. Report the exact p and confirm with ADF.
How to write it: "A KPSS level test rejected stationarity at the 5% level, KPSS = 0.7181, lag = 3, p = 0.012."
Annual GDP growth, 40 years
Growth rates are usually stationary.
Year-on-year GDP growth for 40 years.
| Statistic | Value | Note |
|---|---|---|
| T | 40 | Observations |
| Lag | 3 | trunc(4(T/100)^0.25) |
| Long-run variance | 0.1019 | Bartlett kernel |
| KPSS level | 0.0772 | Test statistic |
| 5% critical value | 0.463 | Kwiatkowski et al. (1992) |
| p-value | > 0.10 | Table interpolation |
What it means: η = 0.0772 gives no evidence against stationarity. Growth rates can be modelled directly in an ARMA model.
How to write it: "Stationarity was not rejected, KPSS = 0.0772, lag = 3, p > 0.10."
First 12 weeks of the price index
Too few points for a firm decision.
Only 12 weekly prices are available.
| Statistic | Value | Note |
|---|---|---|
| T | 12 | Observations |
| Lag | 2 | trunc(4(T/100)^0.25) |
| Long-run variance | 1214.6049 | Bartlett kernel |
| KPSS level | 0.5193 | Test statistic |
| 5% critical value | 0.463 | Kwiatkowski et al. (1992) |
| p-value | 0.037 | Table interpolation |
What it means: η = 0.5193 is above 0.463, but with T = 12 the asymptotic critical values are unreliable and KPSS tends to over-reject. Treat this as a warning, not a verdict.
How to write it: "With only 12 observations, KPSS rejected level stationarity (KPSS = 0.5193, lag = 2, p = 0.037); this result should be interpreted with caution."
400 daily sensor readings
Large T finds tiny drifts.
A sensor drifts by 0.004 units per day while daily noise has an SD of about 1.
| Statistic | Value | Note |
|---|---|---|
| T | 400 | Observations |
| Lag | 5 | trunc(4(T/100)^0.25) |
| Long-run variance | 2.4255 | Bartlett kernel |
| KPSS level | 4.4776 | Test statistic |
| 5% critical value | 0.463 | Kwiatkowski et al. (1992) |
| p-value | < 0.01 | Table interpolation |
What it means: η = 4.4776 is huge, yet the drift is about 0.4% of the daily noise per day. With large T even small trends are detected. Ask whether the trend matters for your purpose; the trend version is not rejected.
How to write it: "KPSS rejected level stationarity, KPSS = 4.4776, lag = 5, p < 0.01, although the implied drift was small (0.004 units per day)."
Water level before and after a dam
A break, not a unit root.
80 months of water level. A dam opened at month 41 and raised the level by about 8 units.
| Statistic | Value | Note |
|---|---|---|
| T | 80 | Observations |
| Lag | 3 | trunc(4(T/100)^0.25) |
| Long-run variance | 57.9962 | Bartlett kernel |
| KPSS level | 1.7379 | Test statistic |
| 5% critical value | 0.463 | Kwiatkowski et al. (1992) |
| p-value | < 0.01 | Table interpolation |
What it means: η = 1.7379 strongly rejects, but each half is stationary on its own. The break, not a unit root, drives the result. Use a KPSS test with a break, or test each regime separately.
How to write it: "KPSS rejected level stationarity (KPSS = 1.7379, lag = 3, p < 0.01); the rejection reflected a structural break at month 41."
Forest cover index, 60 years
Level rejects, trend does not.
A forest cover index rising steadily with yearly noise, tested for trend stationarity.
| Statistic | Value | Note |
|---|---|---|
| T | 60 | Observations |
| Lag | 3 | trunc(4(T/100)^0.25) |
| Long-run variance | 0.7963 | Bartlett kernel |
| KPSS trend | 0.0375 | Test statistic |
| 5% critical value | 0.146 | Kwiatkowski et al. (1992) |
| p-value | > 0.10 | Table interpolation |
What it means: ητ = 0.0375 is below 0.146, so the series is trend-stationary (its level test gives 1.60 and rejects). Remove the linear trend instead of differencing.
How to write it: "A KPSS trend test did not reject trend stationarity, KPSS = 0.0375, lag = 3, p > 0.10."
🧪 14. Data Collection Protocol
Design statement: one variable, measured at equal time intervals, in time order, with no gaps.
- Fix the time step (day, week, month, year) before collecting data.
- Record the same quantity in the same units every time.
- Keep the method constant. A new instrument or survey design can create a false break.
- Record a date for every value.
- Leave missing periods blank, never as 0.
- Write down any known events (policy change, dam, new trap layout) with dates.
- Decide on seasonal adjustment before testing if the data are monthly or quarterly.
- Collect at least 50 points where you can; 20 is the practical minimum.
- Pre-register the version (level or trend), lag rule and α.
- Run KPSS and ADF together and report both.
| date | value | note |
|---|---|---|
| 2024-01 | 12 | |
| 2024-02 | 15 | |
| 2024-03 | 11 | gauge recalibrated |
Minimum sample: 20 observations, 50 or more preferred. Pre-registration line: "Stationarity will be assessed with a KPSS level test, lag = trunc(4(T/100)^0.25), Bartlett kernel, α = .05." Common mistakes: mixing time steps, filling gaps with zeros, testing before removing a known seasonal cycle.
🎯 15. When to Use This Calculator
Use it when
- You have one numeric series in time order.
- You need to decide between levels, detrending and differencing.
- You want to confirm an ADF result.
- You are preparing data for ARIMA, VAR, cointegration or regression on time series.
Do not use it when
- The data are not in time order (use a normality or group test instead).
- There are fewer than about 15 points.
- There is a known structural break (use a KPSS test with breaks).
- Strong seasonality has not been removed.
Four real examples
- Ecology: monthly sloth bear detections per camera trap before fitting a count time-series model.
- Economics: inflation rates before an ARIMA forecast.
- Hydrology: annual peak flow to check for a climate trend.
- Public health: weekly case counts before outbreak forecasting.
Decision tree
- Want to test a unit root directly? Use the ADF test or Phillips-Perron test.
- Suspect one break at an unknown date? Use Zivot-Andrews or KPSS with breaks.
- Checking residuals of a fitted model? Use the Ljung-Box test.
- Seasonal unit root? Use OCSB or Canova-Hansen tests.
🔧 16. Troubleshooting and Common Errors
The p-value just says > 0.10 or < 0.01
Cause: The KPSS p-value comes from a four-point table.
Fix: Report it as p > .10 or p < .01. That is the correct, honest report.
My result differs from Python
Cause: statsmodels uses nlags="auto" by default.
Fix: Pass nlags equal to the lag shown here, e.g. kpss(y, nlags=2).
My result differs from EViews or Stata
Cause: Those programs default to other bandwidth rules (Newey-West automatic or a fixed maxlag).
Fix: Set the same lag and kernel in both programs.
KPSS rejects but the plot looks stable
Cause: A short lag with strong positive autocorrelation, or a break.
Fix: Try the long lag rule and inspect chart 1 for a jump.
Both KPSS and ADF reject
Cause: Break, long memory or a missed trend.
Fix: Run the trend version, split at the suspected break, or use Zivot-Andrews.
Long-run variance not positive
Cause: Rectangular kernel with negative autocovariances.
Fix: Switch to the Bartlett kernel.
Error: all values are identical
Cause: Zero variance.
Fix: KPSS cannot be computed on a constant series.
Fewer values than expected
Cause: Text, NA or currency symbols in the input.
Fix: Clean the column so it contains only numbers and a decimal point.
Seasonal data pass KPSS
Cause: Seasonal cycles average out over the sample.
Fix: Check the residual ACF at lag 12 and deseasonalise.
Differenced series still rejects
Cause: Possible I(2) series or remaining break.
Fix: Difference again and test, or look for a break first.
⚠ 17. Assumptions and Limitations
Assumptions
- Equally spaced, ordered data. Check: dates. If broken, the running sums mean nothing.
- No structural break. Check: chart 1. If broken, KPSS over-rejects (says non-stationary when each part is stationary).
- Seasonality removed. Check: ACF spikes at lag 12 or 4. If broken, results can go either way.
- Linear trend (trend version). Check: chart 1. A curved trend makes KPSS over-reject.
- Enough lags for the autocorrelation. Check: chart 4. Too few lags with strong positive autocorrelation makes KPSS over-reject; too many reduce power.
- Large sample. Critical values are asymptotic. With T < 20 the size is distorted, usually toward over-rejection.
Limitations
- Cannot handle breaks. Alternative: KPSS with structural breaks (Carrion-i-Silvestre et al., 2005) or Zivot-Andrews.
- Sensitive to lag choice. Alternative: report both short and long lags.
- Over-rejects near-unit-root but stationary series (Caner and Kilian, 2001). Alternative: confirm with ADF or ERS DF-GLS.
- Cannot detect seasonal unit roots. Alternative: Canova-Hansen test.
- Cannot separate long memory from a unit root. Alternative: fractional integration (d) estimation.
🏁 18. Conclusion
The KPSS test asks one question: can this series be treated as stable around a mean or a straight trend? It is the natural partner to the ADF test because its null hypothesis is the opposite one.
▶ Run the analysis above and this paragraph will summarise your result: η = ___, lag = ___, decision ___.
Always report the version, the lag and the kernel, show a plot of the series, and pair the result with ADF. When the two tests disagree, look for breaks and seasonality before you difference anything.
Used this way, the KPSS test calculator gives you a defensible, reproducible answer to whether your time series is stationary.
❓ 19. Frequently Asked Questions
Q1. What is the KPSS test?
The KPSS test (Kwiatkowski-Phillips-Schmidt-Shin, 1992) is a test of whether a time series is stationary. It fits a mean or a linear trend, sums the residuals over time, and checks whether those running sums drift too far to be explained by a stationary process.
Q2. What is the null hypothesis of the KPSS test?
The null hypothesis is that the series is stationary, either around a constant mean (level) or around a linear trend (trend). The alternative is a unit root. This is the opposite of the ADF test.
Q3. How do I interpret KPSS test results?
Compare the KPSS statistic with the critical value. If it is larger (for example above 0.463 for level at 5%), reject stationarity. If it is smaller, the series is consistent with stationarity.
Q4. What does a KPSS p-value of 0.1 mean?
Software reports 0.1 when the statistic is below the 10% critical value. The true p-value is larger than 0.1, so report p > .10 and conclude the series is consistent with stationarity.
Q5. What is the difference between the KPSS and ADF tests?
ADF assumes a unit root and tries to reject it. KPSS assumes stationarity and tries to reject it. Running both gives four possible outcomes; agreement between them is the strongest evidence.
Q6. What if KPSS and ADF both reject?
The tests conflict. Common causes are a structural break, long memory (fractional integration), a curved trend or strong autocorrelation with too short a lag. Plot the data, test sub-periods and try a break test.
Q7. What if neither KPSS nor ADF rejects?
Both tests lack power, usually because the series is short. Collect more data, use theory, or report the result as inconclusive.
Q8. Should I choose level or trend in the KPSS test?
Choose level if the series moves around a flat mean. Choose trend if it rises or falls steadily. If level rejects but trend does not, the series is trend-stationary and should be detrended, not differenced.
Q9. What are the KPSS critical values?
Level: 0.347 (10%), 0.463 (5%), 0.574 (2.5%), 0.739 (1%). Trend: 0.119, 0.146, 0.176, 0.216. They come from Kwiatkowski et al. (1992).
Q10. How do I choose the lag in the KPSS test?
The common default is trunc(4(T/100)^0.25), used by R tseries. The long rule trunc(12(T/100)^0.25) is safer with strong autocorrelation. Report the lag you used and check that the decision does not flip between rules.
Q11. Why does the KPSS statistic change with the lag?
The lag controls how many autocovariances enter the long-run variance. Positive autocorrelation increases it as the lag grows, which lowers the statistic. Negative autocorrelation does the opposite.
Q12. How many observations do I need for the KPSS test?
At least 20, preferably 50 or more. The critical values are asymptotic, so short series tend to over-reject stationarity.
Q13. Can the KPSS test detect seasonality?
No. A seasonal cycle can pass KPSS because it averages out. Check the ACF at the seasonal lag and deseasonalise first, or use a seasonal unit-root test.
Q14. Does a structural break affect the KPSS test?
Yes. A shift in the mean makes the running sums drift, so KPSS rejects stationarity even when each part of the series is stationary. Use a KPSS test with breaks or split the series.
Q15. Do I need to difference my data if KPSS rejects?
Only if the trend version also rejects. If level rejects but trend does not, remove the linear trend instead. Differencing a trend-stationary series adds an unwanted moving-average term.
Q16. Is the KPSS test a unit root test?
It is a stationarity test whose alternative is a unit root, so it is often grouped with unit-root tests. It complements ADF and Phillips-Perron rather than replacing them.
Q17. How do I report a KPSS test in APA style?
Example: A KPSS test for level stationarity did not reject the null hypothesis, KPSS = 0.086, lag = 2, p > .10. Always state the version, lag and kernel.
Q18. How do I do a KPSS test in Excel?
Excel has no KPSS function. Compute residuals from the mean, take running sums with =SUM($B$2:B2), get autocovariances with SUMPRODUCT, combine them with Bartlett weights, then divide SUMSQ of the running sums by T squared times the long-run variance. Section 9 shows each step.
Q19. How do I run a KPSS test in R?
Use tseries::kpss.test(y, null = "Level") or "Trend". The default lag is trunc(4(T/100)^0.25). urca::ur.kpss(y, type = "mu", lags = "short") gives the same statistic with critical values.
Q20. How do I run a KPSS test in Python?
Use statsmodels: from statsmodels.tsa.stattools import kpss; kpss(y, regression="c", nlags=2). Use "ct" for trend. Set nlags yourself to match R, because the default nlags="auto" uses a different rule.
📑 20. Cite This Tool
StatsUnlock. (2026). KPSS test calculator [Interactive statistical tool]. https://statsunlock.com/kpss-test-calculator/@misc{statsunlock_kpss_2026, title={KPSS Test Calculator}, author={{StatsUnlock}}, year={2026}, note={Interactive statistical tool}, url={https://statsunlock.com/kpss-test-calculator/}}Stationarity was assessed with the KPSS test (Kwiatkowski et al., 1992) using the StatsUnlock KPSS Test Calculator (2026), with a Bartlett kernel and lag truncation trunc(4(T/100)^0.25) (Schwert, 1989). Results were cross-checked with the augmented Dickey-Fuller test.🔗 21. Related Tools
The opposite-null partner to KPSS. Run both and read the decision table in 4.6.
Open the tool →Fit an ARIMA model once KPSS tells you how many differences d you need.
Open the tool →For monthly or quarterly series with a seasonal cycle that KPSS cannot see.
Open the tool →Model a stationary series with autoregressive terms chosen from the ACF.
Open the tool →Moving-average models for stationary series with short memory.
Open the tool →Combine AR and MA terms for a series that passes the KPSS test.
Open the tool →Fit and remove a linear trend when the series is trend-stationary.
Open the tool →Mean, SD and shape of your series before and after differencing.
Open the tool →📖 22. Glossary of Terms
| Term | Plain-English meaning |
|---|---|
| ACF | Autocorrelation function: the correlation of a series with itself at each lag. |
| ADF test | Augmented Dickey-Fuller test; its null hypothesis is a unit root. |
| Autocovariance | How much a value and the value j steps earlier move together. |
| Bandwidth | Another word for the lag used in the long-run variance. |
| Bartlett kernel | Weights that fall in a straight line from 1 toward 0 as the lag grows. |
| Critical value | The cut-off the statistic must pass to reject the null. |
| Detrending | Removing a fitted straight line from the series. |
| Differencing | Replacing each value with its change from the previous value. |
| Kernel | The weighting rule applied to autocovariances. |
| KPSS statistic | η; the scaled size of the running sums of residuals. |
| Lag | The number of time steps between two values. |
| Level stationarity | Stationary around a fixed mean. |
| Long-run variance | Variance adjusted for autocorrelation over time. |
| Null hypothesis | The claim the test assumes true; stationarity for KPSS. |
| Partial sum | A running total of residuals, written St. |
| Residual | The data value minus the fitted mean or trend. |
| Stationary series | A series whose mean, variance and correlation pattern do not change over time. |
| Structural break | A sudden lasting change in the level or trend. |
| Trend stationarity | Stationary around a straight-line trend. |
| Unit root | A property that makes shocks permanent, so the series wanders without returning. |
📚 23. References
Peer-reviewed sources behind the methods in this KPSS test calculator: stationarity and unit-root testing, long-run variance estimation, lag selection and structural breaks.
- Kwiatkowski, D., Phillips, P. C. B., Schmidt, P., & Shin, Y. (1992). Testing the null hypothesis of stationarity against the alternative of a unit root. Journal of Econometrics, 54(1–3), 159–178. https://doi.org/10.1016/0304-4076(92)90104-Y
- Dickey, D. A., & Fuller, W. A. (1979). Distribution of the estimators for autoregressive time series with a unit root. Journal of the American Statistical Association, 74(366), 427–431. https://doi.org/10.1080/01621459.1979.10482531
- Phillips, P. C. B., & Perron, P. (1988). Testing for a unit root in time series regression. Biometrika, 75(2), 335–346. https://doi.org/10.1093/biomet/75.2.335
- Newey, W. K., & West, K. D. (1987). A simple, positive semi-definite, heteroskedasticity and autocorrelation consistent covariance matrix. Econometrica, 55(3), 703–708. https://doi.org/10.2307/1913610
- Newey, W. K., & West, K. D. (1994). Automatic lag selection in covariance matrix estimation. Review of Economic Studies, 61(4), 631–653. https://doi.org/10.2307/2297912
- Andrews, D. W. K. (1991). Heteroskedasticity and autocorrelation consistent covariance matrix estimation. Econometrica, 59(3), 817–858. https://doi.org/10.2307/2938229
- Schwert, G. W. (1989). Tests for unit roots: A Monte Carlo investigation. Journal of Business & Economic Statistics, 7(2), 147–159. https://doi.org/10.1080/07350015.1989.10509723
- Hobijn, B., Franses, P. H., & Ooms, M. (2004). Generalizations of the KPSS-test for stationarity. Statistica Neerlandica, 58(4), 483–502. https://doi.org/10.1111/j.1467-9574.2004.00272.x
- Leybourne, S. J., & McCabe, B. P. M. (1994). A consistent test for a unit root. Journal of Business & Economic Statistics, 12(2), 157–166. https://doi.org/10.1080/07350015.1994.10510004
- Caner, M., & Kilian, L. (2001). Size distortions of tests of the null hypothesis of stationarity: Evidence and implications for the PPP debate. Journal of International Money and Finance, 20(5), 639–657. https://doi.org/10.1016/S0261-5606(01)00011-0
- Lee, D., & Schmidt, P. (1996). On the power of the KPSS test of stationarity against fractionally-integrated alternatives. Journal of Econometrics, 73(1), 285–302. https://doi.org/10.1016/0304-4076(95)01741-0
- Lee, J., Huang, C. J., & Shin, Y. (1997). On stationary tests in the presence of structural breaks. Economics Letters, 55(2), 165–172. https://doi.org/10.1016/S0165-1765(97)00065-7
- Carrion-i-Silvestre, J. L., del Barrio-Castro, T., & López-Bazo, E. (2005). Breaking the panels: An application to the GDP per capita. Econometrics Journal, 8(2), 159–175. https://doi.org/10.1111/j.1368-423X.2005.00158.x
- Zivot, E., & Andrews, D. W. K. (1992). Further evidence on the great crash, the oil-price shock, and the unit-root hypothesis. Journal of Business & Economic Statistics, 10(3), 251–270. https://doi.org/10.1080/07350015.1992.10509904
- Elliott, G., Rothenberg, T. J., & Stock, J. H. (1996). Efficient tests for an autoregressive unit root. Econometrica, 64(4), 813–836. https://doi.org/10.2307/2171846
- Ng, S., & Perron, P. (2001). Lag length selection and the construction of unit root tests with good size and power. Econometrica, 69(6), 1519–1554. https://doi.org/10.1111/1468-0262.00256
- Shin, Y. (1994). A residual-based test of the null of cointegration against the alternative of no cointegration. Econometric Theory, 10(1), 91–115. https://doi.org/10.1017/S0266466600008240
- Nelson, C. R., & Plosser, C. I. (1982). Trends and random walks in macroeconomic time series. Journal of Monetary Economics, 10(2), 139–162. https://doi.org/10.1016/0304-3932(82)90012-5
- Granger, C. W. J., & Newbold, P. (1974). Spurious regressions in econometrics. Journal of Econometrics, 2(2), 111–120. https://doi.org/10.1016/0304-4076(74)90034-7
- Ljung, G. M., & Box, G. E. P. (1978). On a measure of lack of fit in time series models. Biometrika, 65(2), 297–303. https://doi.org/10.1093/biomet/65.2.297
