Skip to main content
v2026.11,772 entries · CC-BY 4.0

Wilcoxon Test in R: wilcox.test(), the W Statistic, and the Silent Exact/Approximate Switch

One R function runs both Wilcoxon tests, and three of its defaults routinely trip people up: the statistic it names W is not the one SPSS reports, it drops to the normal approximation without being asked, and its confidence interval does not estimate a difference in medians.

Ask CASRAI · included with Regulatory Radar

Ask about Wilcoxon Test in R: wilcox.test(), the W Statistic, and the Silent Exact/Approximate Switch

Ask CASRAI answers research-administration questions and cites the passages behind every claim — and says so when the corpus does not cover something, instead of guessing. It comes with a Regulatory Radar subscription at $29 a month, alongside the daily digest of regulatory changes and the dashboard of what changed.

150 questions a day, on this site, over the API, or inside your own tools through the CASRAI MCP server.

Everything CASRAI publishes — this page, the dictionary, the guides and the news — stays free to read, with no account and no card.

Written and maintained by CASRAI Editorial Board

Last updated

In R, both Wilcoxon tests live in one function, wilcox.test() in the stats package, and a single argument decides which one you get. That convenience hides three defaults that cause most of the trouble people run into: the statistic R prints is not the one SPSS or a journal template expects, R switches from the exact test to a normal approximation on its own and only tells you in a warning, and the confidence interval it can produce does not estimate a difference in medians — R’s own documentation calls that “a common misconception.”

The two tests, and the one argument that separates them

The signature is:

wilcox.test(x, y = NULL,
            alternative = c("two.sided", "less", "greater"),
            mu = 0, paired = FALSE, exact = NULL, correct = TRUE,
            conf.int = FALSE, conf.level = 0.95,
            tol.root = 1e-4, digits.rank = Inf, ...)

With paired = FALSE (the default) and two samples, you get the Wilcoxon rank-sum test — equivalent to the Mann–Whitney U test. With paired = TRUE, or with a single vector x, you get the Wilcoxon signed-rank test. There is no separate mannwhitney() function to look for; R treats the two names as the same procedure.

# Two independent groups — rank-sum / Mann-Whitney
wilcox.test(value ~ group, data = df)
wilcox.test(x, y)                 # same thing, vectors

# Paired / one-sample — signed-rank
wilcox.test(before, after, paired = TRUE)
wilcox.test(differences)          # equivalent, if you pre-computed them

Two mechanical traps in the formula interface. The grouping variable must resolve to exactly two levels, so a factor carrying an unused third level from a subset — a level with zero rows still counts — will stop the call; droplevels() on the subsetted data frame clears it. And paired = TRUE pairs by position, not by any ID column: x[1] is matched to y[1] and so on, so if the two vectors were pulled from separately sorted or separately filtered frames, R will happily test the wrong pairs and report a perfectly clean p-value. Reshape from a wide frame where the pairing is structural, rather than assembling two vectors and trusting the order.

What W and V actually are

This is the single most common reporting error, because R does not print the statistic other packages print.

For the two-sample test, R labels the statistic W, and it is the Mann–Whitney U statistic. R computes the rank sum of the first sample and subtracts its minimum possible value, m(m+1)/2, for a first sample of size m. The documentation gives the equivalent combinatorial definition: R’s value “can also be computed as the number of all pairs (x[i], y[j]) for which y[j] is not greater than x[i], the most common definition of the Mann-Whitney test.” So R’s W is U — despite the letter. Note that it is U for the sample you passed first; swap the arguments and you get the complementary value, mn − U.

For the paired/one-sample test, R labels the statistic V, and it is the sum of the ranks of the absolute differences that were positive.

What this means in practice: a journal asking for “U” wants R’s W; a journal or a supervisor asking for the W of the classical Wilcoxon rank-sum formulation wants the raw rank sum, which is R’s W plus m(m+1)/2. And R does not print a Z score at all, whereas SPSS reports U and Z together. If you need Z — and you do, if you want the conventional r effect size — you have to get it from the coin package rather than from base R. Analysts crossing between packages should also note that Stata’s ranksum command reports z rather than U, so all three environments print a different number for the same test.

The exact/approximate switch happens without you asking

The exact argument defaults to NULL, which means “decide for me.” The documentation states the rule: “By default (if exact is not specified), an exact p-value is computed if the samples contain less than 50 finite values and there are no ties.”

Both clauses matter, and the second one is where real data lands. Ties are close to guaranteed in the kinds of data people reach for a Wilcoxon test for — Likert items, ordinal scores, rounded lab values, anything with a floor at zero. The moment a tie appears, R abandons the exact permutation distribution, falls back to the normal approximation, and tells you only through a warning:

Warning message:
In wilcox.test.default(x, y) : cannot compute exact p-value with ties

The paired test has a second version of this. The signed-rank test has nothing to do with pairs whose difference is exactly zero, so those pairs are discarded — which also blocks the exact calculation and produces cannot compute exact p-value with zeroes. Both warnings are frequently silenced by adding exact = FALSE, which suppresses the message without changing anything about the computation. That is a reasonable thing to do once you know that you are reporting an approximate p-value; it is not a fix, and it should not be the reflex.

If you genuinely need an exact p-value in the presence of ties, base R cannot give you one, but two packages can. The coin package’s wilcox_test() performs exact inference using the conditional (permutation) distribution given the observed ranks, via an implementation of the Streitberg–Röhmel shift algorithm; exactRankTests offers wilcox.exact() for the same purpose. Alternatively, sidestep the ranking question entirely with a permutation test on the raw values.

# Exact p-value even with ties
library(coin)
wilcox_test(value ~ factor(group), data = df, distribution = "exact")

Note the factor(): coin requires the grouping variable to be a factor and will not silently coerce a character or numeric column.

One caution in the other direction. The help page carries an explicit warning that wilcox.test “can use large amounts of memory and stack (and even crash R if the stack limit is exceeded) if exact = TRUE and one sample is large.” Forcing exact = TRUE on a big sample to be safe is not the conservative choice it appears to be.

Spurious ties from floating point

A subtler problem: values that are mathematically distinct can differ only in the last bits after a computation, and R will treat them as tied. The digits.rank argument exists for exactly this. The documentation advises that “for stability reasons, it may be advisable to use rounded data or to set digits.rank = 7, say, such that determination of ties does not depend on very small numeric differences.” If your ties warning appears on data you believed had no exact repeats, this is usually why.

You are not testing medians, and the CI is not a difference in medians

Two separate claims here, both routinely garbled.

First, the null hypothesis. R states it as “the distributions of x and y differ by a location shift of mu.” That is a location-shift null, and it assumes the two distributions have the same shape and spread. When shapes differ — unequal variances, one group skewed and the other not — a significant rank-sum result tells you the groups are stochastically different, not that their medians differ. It can reject when the medians are identical. This is the point people most often carry away wrong, and it is worth reading up on the full set of assumptions the Mann–Whitney test actually makes before writing a median claim into a results section. The parallel issue for the paired test — symmetry of the differences, not normality — is covered in the signed-rank assumptions guide.

Second, the confidence interval. conf.int is FALSE by default, so most people never request one and report a bare p-value with no estimate of magnitude at all. Turn it on:

wilcox.test(value ~ group, data = df, conf.int = TRUE)

Now read what you get carefully. R’s documentation is unusually blunt about it: “in the two-sample case the estimator for the difference in location parameters does not estimate the difference in medians (a common misconception) but rather the median of the difference between a sample from x and a sample from y.” That quantity is the Hodges–Lehmann estimator — the median of all pairwise differences between the groups. In the one-sample and paired case the target is the pseudomedian of the differences, again not the median of the differences. The two coincide when the location-shift model holds, and can diverge when it does not.

When exact p-values are available, R produces an exact interval by the Bauer (1972) algorithm and uses the Hodges–Lehmann estimator. Otherwise the interval and point estimate rest on normal approximations, and per the documentation these “are continuity-corrected for the interval but not the estimate” — so a point estimate can sit fractionally off-centre in its own interval. That is expected behaviour, not a bug. The correct = TRUE default applies the continuity correction to the normal approximation of the p-value.

Effect size

The commonest convention is the rank-biserial correlation or the effect size r. The rstatix package computes r as Z/√N, taking Z from coin::wilcox_test() for independent samples and coin::wilcoxsign_test() for paired ones. For the paired case, N is the number of pairs — equivalently the number of difference scores — not the total number of observations, because the paired test reduces to a one-sample test on the differences. Halving that denominator by mistake inflates r substantially.

library(rstatix)
df %>% wilcox_effsize(value ~ group)
df %>% wilcox_effsize(value ~ time, paired = TRUE)

rstatix documents the usual interpretation bands for r: 0.10 to < 0.30 small, 0.30 to < 0.50 moderate, ≥ 0.50 large. Treat these as conventions rather than as anything derived from your measurement context — they are the standard Cohen-style thresholds, and what counts as a meaningful shift in your outcome is a domain judgement, not a statistical one.

The zero-handling discrepancy nobody mentions

If you run a paired test in base R and again in coin, and your data contains pairs with zero difference, you can get two different p-values from the same data. This is not a bug in either package; they use different published conventions.

Base R’s wilcox.test uses the classical Wilcoxon approach: discard the zero differences, then rank the remaining absolute differences. coin’s wilcoxsign_test() exposes a zero.method argument and defaults to "Pratt", which “first rank-transforms the absolute differences (including zeros) and then discards the ranks corresponding to the zero-differences.” Setting zero.method = "Wilcoxon" reproduces the base R convention, which “first discards the zero-differences and then rank-transforms the remaining absolute differences.”

Pratt’s method keeps the zeros in the ranking, so the surviving ranks are larger, and it is generally the more conservative of the two. If you have a non-trivial number of exact ties between paired measurements — common with coarse ordinal scales — say which convention you used. Otherwise a reanalysis in a different package will not reproduce your number and neither of you will know why.

When to use Brunner–Munzel instead

If the concern is that the two groups differ in spread or shape rather than purely in location, the rank-sum test is answering a question you may not want asked. The Brunner–Munzel test is the usual remedy — roughly what Welch’s t-test is to Student’s, it tests stochastic equality without assuming equal distribution shapes, and is robust to heteroscedasticity.

In R it is in the brunnermunzel package, as brunnermunzel.test(), with brunnermunzel.permutation.test() for the permuted version (also reachable via perm = TRUE). It estimates P(X < Y) + 0.5 × P(X = Y), a directly interpretable “relative effect”: the probability that a randomly drawn observation from one group falls below one from the other. Note that the permutation version switches itself back to the standard test when the number of combinations exceeds choose(28, 14), to bound the computation.

Deciding between these families is a design question more than a software question — see parametric versus non-parametric tests for the trade-offs, and the Kruskal–Wallis test if you have three or more groups rather than two.

A reporting template that survives review

Report enough that someone can reproduce the number and judge the size of the effect:

  • Which test, by full name — “Wilcoxon rank-sum” or “Wilcoxon signed-rank”, not just “a Wilcoxon test”.
  • The statistic, labelled the way you computed it. If you print R’s output, say so: R’s W is U.
  • Exact or approximate, and if approximate, why — ties, zeroes, or sample size — and whether a continuity correction was applied.
  • Group sizes, and for paired data the number of pairs actually analysed after zero differences and incomplete pairs were dropped. This is often smaller than your stated n, and it is the number the test used.
  • An effect size with a confidence interval, described accurately — a Hodges–Lehmann location shift, not “the difference in medians”.
  • Descriptive medians and IQRs per group, separately from the test. These are what readers actually want, and reporting them does not commit you to the claim that the test compared them.

Frequently asked questions

What is W in the wilcox.test output?

For a two-sample test, W is the Mann–Whitney U statistic for the first sample you passed: the rank sum of that sample minus m(m+1)/2. It is not the classical Wilcoxon rank sum, which is that value plus m(m+1)/2. For a paired or one-sample test the statistic is labelled V and is the sum of the ranks of the positive differences.

How do I run a Mann-Whitney U test in R?

You already have: wilcox.test(x, y) with paired = FALSE is the Mann–Whitney U test. R’s help page describes the rank-sum test as “equivalent to the Mann-Whitney test”. There is no separate function, and packages advertising one are wrapping the same computation.

Should I just add exact = FALSE to get rid of the ties warning?

It removes the message without changing the result, because R had already switched to the normal approximation before it warned you. So it is honest only if you then report the p-value as approximate. If you need a genuine exact p-value with ties present, use coin::wilcox_test(..., distribution = "exact") or exactRankTests::wilcox.exact(), neither of which is limited the way base R is.

Why does R not give me a confidence interval by default?

conf.int defaults to FALSE. Set conf.int = TRUE. Be aware that the interval and estimate describe a Hodges–Lehmann location shift — the median of pairwise differences between groups — and, in R’s own words, do “not estimate the difference in medians”.

Does the Wilcoxon test require my data to be normally distributed?

No — that is the point of it. But “no assumptions” is equally wrong. The two-sample form assumes the distributions differ only by a location shift if you want to interpret it as a statement about location; the paired form assumes the differences are symmetric about the null value. Both assume independent observations, and the paired form assumes independence between pairs, not within them.

Why do my base R and coin results disagree on paired data?

Almost always zero differences. Base R discards zeros before ranking; coin::wilcoxsign_test() defaults to Pratt’s method, which ranks them first and then drops those ranks. Set zero.method = "Wilcoxon" in coin to match base R. A second, rarer cause is that coin computed an exact p-value where base R fell back to the approximation.

Can I use the Wilcoxon test on Likert items?

It is a common choice, and defensible for a single ordinal item, since the test only uses ranks. Expect heavy ties and therefore an approximate p-value by default. The bigger risk is interpretive: with a coarse scale, distributions in two groups often differ in shape as well as location, which is precisely the situation where a significant rank-sum result does not license a claim about medians.

What sample size do I need?

There is no fixed minimum, but with very small samples the exact test may be unable to reach conventional significance at all — with a two-sided test and tiny groups, the smallest attainable p-value can exceed 0.05 regardless of how separated the data are. Check attainable p-values before concluding a null result means anything, and note that R’s automatic exact/approximate rule turns on sample size as well as ties.

References

Follow CASRAI

Research-administration guidance, standards updates and independent tool reviews.

Referenced across the research world

University of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logoUniversity of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logo
  • University of Cambridge logo
  • Columbia University logo
  • Crossref logo
  • University of Edinburgh logo
  • Harvard University logo
  • University of Oxford logo
  • Princeton University logo
  • Stanford School of Medicine logo
  • University College London logo
  • ORCID logo

View CASRAI adoption →

Regulatory Radar

Stop finding out after the fact

$29/month, cancel anytime. Daily digest updates from our analysis, a dashboard holding the same items, and a cited assistant for everything they raise.

  • Federal Register, Federal Register+, Grants.gov, Regulations.gov, NSF News, UKRI, plus CASRAI’s own published content.
  • 72,264 indexed passages, and every answer cites the ones it drew on.