Written and maintained by CASRAI Editorial Board
Last updated
ave() in R computes a statistic within groups and returns the result for every row, so the output has the same length as the input. By default the statistic is the mean, which makes ave(x, g) the shortest base-R way to add a “group mean” column to a data frame without collapsing the rows. It is a base function in the stats package.
This guide covers the syntax from the R manual, a worked example you can paste into a console (including the NA trap that catches most first-time users), and a table of how ave() differs from tapply() and grouped dplyr::mutate().
What ave() is used for in research
Research datasets are usually long: one row per participant, sample, plate well or visit, with a column naming the group (site, treatment arm, batch, subject). Many routine steps need a group-level number sitting next to each row:
- Centering a measurement on its group mean, for example
score - ave(score, group), so each row shows its deviation from its own group. - Comparing each row with its batch or site average when screening for batch effects.
- Numbering rows within each group with
FUN = seq_along(visit number, replicate number). - Inspecting how much a group mean would change if missing values were ignored. For choosing a defensible missing-data method, see MCAR, MAR and MNAR: diagnosing your missing data.
Because the result keeps one value per input row, you can assign it straight back into a column without a merge step.
Syntax and arguments
The R manual page for ave (package stats, R-devel manual, checked 2026-10-29) gives the usage as:
ave(x, ..., FUN = mean)
| Argument | What the manual says |
|---|---|
x |
A vector, typically numeric. |
... |
Grouping variables, typically factors, all of the same length as x. The groups are the interaction of these variables. |
FUN |
The function applied to each combination of factor levels. If supplied, it must be named (FUN = ...). |
Two rules from the Value section matter in practice. First, the result is a vector of the same length as x: element i is FUN applied to the values of x that share the group of row i. Second, if you give no grouping variables at all, every element equals FUN(x), so ave(1:3) returns 2 2 2.
Install or access
ave() is documented in the stats package of R itself, so no add-on package is needed. In a console, ?ave opens the manual page for your installed version.
Step-by-step: add a group-mean column
The example below uses a small made-up data frame with one missing score. Every line was run in R 4.6.1, and the printed values in the next section are that output.
- Create the data.
d = data.frame( group = c("A","A","A","B","B","C"), score = c(4, 6, NA, 10, 20, 7), site = c("x","y","x","y","x","y") ) - Add the default group mean.
d$mean_naive = ave(d$score, d$group) - Add a group mean that ignores NA. Pass a named function that sets
na.rm:d$mean_narm = ave(d$score, d$group, FUN = function(x) mean(x, na.rm = TRUE)) - Add a within-group row number. This is the pattern from the manual’s own examples:
d$idx = ave(seq_along(d$group), d$group, FUN = seq_along) - Compute each row’s deviation from its group mean.
d$dev = d$score - d$mean_narm - Group by more than one variable. Add a second grouping argument; the groups become the combinations of both:
d$mean_by_group_site = ave(d$score, d$group, d$site, FUN = function(x) mean(x, na.rm = TRUE))
The manual also shows a trimmed mean, ave(breaks, tension, FUN = function(x) mean(x, trim = 0.1)), on the built-in warpbreaks data. Any function that takes a vector and returns either one value or a vector of the same length works as FUN.
Output and how to read it
Printing d after steps 1 to 5 gave:
group score site mean_naive mean_narm idx dev
1 A 4 x NA 5 1 -1
2 A 6 y NA 5 2 1
3 A NA x NA 5 3 NA
4 B 10 y 15 15 1 -5
5 B 20 x 15 15 2 5
6 C 7 y 7 7 1 0
- Every row of group A shows the same group value (
5), because the group-level statistic is repeated for each member. That repetition is the point ofave(). mean_naiveisNAfor all of group A, not only for the row with the missing score: the defaultmean()returnsNAwhen any value in the group isNA, andave()hands that result to every row of the group. Groups B and C are unaffected.mean_narmaverages the two observed scores in group A (4 and 6) to get5. TheNArow itself still has a missingdev, because its own score is missing.- With
siteadded as a second grouping variable (step 6), group A splits in two. The rows for A at site x (scores 4 and NA) get4, the mean of the one observed score, and the row for A at site y gets6. The six rows return4, 6, 4, 10, 20, 7.
Troubleshooting
The result is NA for a whole group
One NA in a group makes the default mean NA for every row in that group (see the output above). Supply FUN = function(x) mean(x, na.rm = TRUE). Decide first whether ignoring missing values is appropriate for your analysis; the missing-data guide linked above explains when it is not.
Error: “unique() applies only to vectors”
This appears when the function is passed without its name. Everything after x is read as a grouping variable, so ave(d$score, d$group, function(x) mean(x)) treats the function as a grouping variable and fails; in R 4.6.1 the error message is exactly unique() applies only to vectors. The manual’s rule that FUN must be named, when supplied, is the fix: write FUN = function(x) mean(x).
Grouping variables of the wrong length
The manual states the grouping variables must have the same length as x. Build them from the same data frame (d$group) rather than from a separate vector, so the lengths cannot drift apart.
You wanted one row per group, not one per input row
ave() never collapses rows. If you want a summary table with one value per group, use tapply() or a grouped summary instead; see the table below.
ave() vs tapply() vs dplyr: free alternatives compared
All three tools are free. They differ in the shape of what they return, which is usually the deciding factor. The statements below come from the R manual pages for ave and tapply and the dplyr mutate() reference (all checked 2026-10-29).
| Tool | What it returns | Rows kept? | Group syntax |
|---|---|---|---|
ave(x, g, FUN = mean) (base R, stats) |
A vector the same length as x, with each group’s value repeated per row |
Yes, one value per input row | Grouping variables listed after x; FUN named |
tapply(X, INDEX, FUN) (base R) |
“Apply a function to each cell of a ragged array”: with a single value per cell and simplify = TRUE it returns an array with one entry per group, and NA for empty cells |
No, one value per group | INDEX argument |
group_by(g) |> mutate(m = mean(x)) (dplyr) |
The data frame with a new column computed within groups | Yes | group_by(), or the per-operation .by argument of mutate() |
For the same data, tapply(d$score, d$group, function(x) mean(x, na.rm = TRUE)) returned one named value per group (A 5, B 15, C 7), whereas ave() returned six values.
The same column in dplyr
The dplyr reference describes .by as “a selection of columns to group by for just this operation, functioning as an alternative to group_by()”, and its documented grouped-mutate() example computes an aggregate within group: starwars |> select(name, mass, species) |> group_by(species) |> mutate(mass_norm = mass / mean(mass, na.rm = TRUE)). Following that pattern, the equivalent of step 3 is:
library(dplyr)
d |> group_by(group) |> mutate(mean_narm = mean(score, na.rm = TRUE))
The same reference shows what grouping changes: Luke Skywalker’s normalised mass is 0.947 when grouped by species and 0.791 ungrouped. Choose ave() when you want a dependency-free base-R line; choose dplyr when the rest of your pipeline is already in the tidyverse. For other by-group statistics in base R, see Standard Deviation in R: sd(), na.rm, and by-group calculation.
FAQ
What does ave() do in R?
It applies a function (the mean by default) to x within each group defined by one or more grouping variables, and returns a vector the same length as x in which each element is that group’s result.
Why does ave() need FUN to be named?
Everything after x is treated as a grouping variable, so an unnamed function is read as a grouping variable and the call fails. The manual says FUN, if supplied, must be named.
How do I ignore missing values in ave()?
Pass a named function that handles them: FUN = function(x) mean(x, na.rm = TRUE).
Can ave() group by more than one variable?
Yes. List several grouping variables after x; the groups are the interaction of those variables, so each combination of levels gets its own result.
What is the difference between ave() and tapply()?
ave() returns one value per input row; tapply() returns one value per group. Use ave() to add a column and keep your rows, and tapply() for a per-group summary. Related reading: Descriptive statistics and Z-score (standard score), which standardises a value against a mean and spread.
Sources
- R Core Team. ave: Group Averages Over Level Combinations of Factors, R manual, package stats. https://stat.ethz.ch/R-manual/R-devel/library/stats/html/ave.html (checked 2026-10-29).
- R Core Team. tapply: Apply a Function Over a Ragged Array, R manual, package base. https://stat.ethz.ch/R-manual/R-devel/library/base/html/tapply.html (checked 2026-10-29).
- dplyr reference, Create, modify, and delete columns (mutate). https://dplyr.tidyverse.org/reference/mutate.html (checked 2026-10-29).







