Why Summarize Several Columns at Once?
A data set can contain many numeric measurements, and checking them one at a time is slow and makes it easy to apply inconsistent choices. In dplyr, across() lets a summary operation apply the same function to several selected columns. It is useful when an initial exploration calls for a consistent statistic, such as a mean or median, across multiple measures.
The result is a compact table, but it still needs interpretation. Each statistic describes one column in that column’s units. A mean number of claims, a mean number of exposure months, and a mean premium in dollars are not directly comparable just because they appear side by side.
across(.cols, .fns) applies the function or functions in .fns separately to each column selected by .cols. When used inside summarise() on an ungrouped data frame, each selected column contributes a summary value, so the result has one row.
Basic Syntax and Column Selection
The general pattern is summarise(across(columns, function)). For example, summarise(across(c(premium, loss), mean)) computes one mean for premium and another for loss. It does not combine their observations into a single mean.
To apply a statistic to all numeric columns, the tidyselect helper where(is.numeric) can be used: across(where(is.numeric), mean). This selects columns according to their storage type, not what they mean in the analysis. A numeric policy identifier, for instance, is still numeric to R; including it in a mean would usually be unhelpful. When the intended variables are known, selecting them explicitly is often safer.
It is also helpful to name the result columns. The .names argument can add a suffix, with {.col} standing for the original column name. For example, .names = "{.col}_mean" produces names such as premium_mean. Clear names make it easier to track which statistic each value represents.
Identify the numeric measures relevant to the question. Do not select a column solely because R stores it as numeric.
Select a function such as
mean or median, and decide how missing values should be handled.Put
across() inside summarise(), and use informative output names.Check the column represented, its units, and whether the summary is based on the intended observations.
Why the Operation Is Column by Column
Suppose a data frame has \(n\) rows and selected numeric columns \(x_1,\ldots,x_p\). For a mean summary, the result for column \(j\) is
This follows directly from what across() does: it supplies each selected column to the chosen function separately. The mean function receives \(x_1\), calculates \(\bar{x}_1\), then receives \(x_2\), calculates \(\bar{x}_2\), and continues in the same way. No step stacks the columns together.
That distinction matters when columns have different meanings or units. A pooled mean would require a defined set of observations that can sensibly be averaged together. Simply placing several columns in across() does not create such a set.
Worked Example: Mean Summaries for Policy Measures
A hypothetical insurer has four policy records. The data include a numeric record identifier, exposure in months, annual premium in dollars, and claim count. Calculate the mean exposure, mean premium, and mean claim count. Keep the identifier out of the summaries.
| Record ID | Exposure months | Annual premium ($) | Claim count |
|---|---|---|---|
| 501 | 3 | 90 | 0 |
| 502 | 6 | 120 | 1 |
| 503 | 9 | 150 | 0 |
| 504 | 12 | 240 | 3 |
Method. Select the three intended measures explicitly, apply mean to each, and name the output columns. This avoids relying on numeric storage type to decide what belongs in the summary.
R expression: policy_data |> dplyr::summarise(dplyr::across(c(exposure_months, annual_premium, claim_count), mean, .names = "{.col}_mean"))
Exposure calculation: \((3+6+9+12)/4=30/4=7.5\) months.
Premium calculation: \((90+120+150+240)/4=600/4=150\) dollars.
Claim-count calculation: \((0+1+0+3)/4=4/4=1\) claim.
The resulting one-row table has exposure_months_mean = 7.5, annual_premium_mean = 150, and claim_count_mean = 1. The calculations check against the totals in the source data. The common mistake this method avoids is including the identifier because it is stored as a number; its mean, \( (501+502+503+504)/4=502.5 \), is not a useful summary of policy experience.
Missing Values and the Statistic Chosen
Many R summary functions return NA if a selected column contains a missing value and the function is not told to remove missing values. For a mean, na.rm = TRUE means to omit missing observations from that calculation. The same argument is available for median. It does not fill in missing data, and it does not guarantee that every column’s summary uses the same number of observations.
For a consistent median calculation across selected variables, one can use across(c(premium, claim_count), median, na.rm = TRUE, .names = "{.col}_median"). The summaries remain separate by column; each median is based on the available values in its own column.
Worked Example: Medians with a Missing Premium
A hypothetical service plan file has four records. Calculate the median premium and median reported-claim count, omitting missing values. Do not include the numeric record number.
| Record number | Premium ($) | Reported claims |
|---|---|---|
| 71 | 100 | 0 |
| 72 | NA | 1 |
| 73 | 200 | 1 |
| 74 | 300 | 5 |
Method. Select only the two measures, apply median with na.rm = TRUE, and label the result. This avoids the common mistake of letting the missing premium make the whole premium summary NA.
R expression: plan_data |> dplyr::summarise(dplyr::across(c(premium, reported_claims), median, na.rm = TRUE, .names = "{.col}_median"))
Premium values: after omitting the missing value, sort the observed values: \(100, 200, 300\). With three values, the median is the middle value, \(200\) dollars.
Claim-count values: sort all four observed counts: \(0, 1, 1, 5\). With four values, the median is the average of the two middle values: \((1+1)/2=1\) claim.
The result is premium_median = 200 and reported_claims_median = 1. These values check against the ordered observations. Notice that the premium median uses three records, while the claim-count median uses four; the output table does not show that difference unless it is checked separately.
Interpreting the Resulting Table
A summary table is a compact comparison of column summaries, not evidence that the columns measure the same thing. Before comparing magnitudes, check units and scales. Means of two dollar amounts may be directly interpretable side by side, while a mean in thousands of dollars and a mean count are not.
Even when two columns use the same units, their means alone do not reveal the full distribution or establish a relationship between the measures. A column with a larger mean need not be larger for every observation. The table is a useful first inspection, after which the question determines what additional summaries or displays are appropriate.
Worked Example: Comparing Measures on a Common Scale
A hypothetical insurer records three policy measures, all in thousands of dollars, for four product groups: written premium, incurred claims, and operating expense. Calculate the mean for each and decide which comparisons the table supports.
| Group | Written premium ($000) | Incurred claims ($000) | Operating expense ($000) | Policy count |
|---|---|---|---|---|
| A | 1.2 | 0.4 | 0.2 | 10 |
| B | 1.5 | 0.9 | 0.3 | 15 |
| C | 1.8 | 1.1 | 0.5 | 20 |
| D | 2.1 | 1.6 | 0.6 | 25 |
Method. Select the three dollar measures and summarize their means. The policy count is numeric but has a different unit, so it is excluded from this comparison.
R expression: group_data |> dplyr::summarise(dplyr::across(c(written_premium, incurred_claims, operating_expense), mean, .names = "{.col}_mean"))
Written premium: \((1.2+1.5+1.8+2.1)/4=6.6/4=1.65\) thousand dollars.
Incurred claims: \((0.4+0.9+1.1+1.6)/4=4.0/4=1.00\) thousand dollars.
Operating expense: \((0.2+0.3+0.5+0.6)/4=1.6/4=0.40\) thousand dollars.
The resulting means are 1.65, 1.00, and 0.40, all in thousands of dollars. The calculations check against the column totals. Because the three measures share units, their average magnitudes can be compared descriptively. The policy count should not be included in that comparison: its mean is \((10+15+20+25)/4=17.5\) policies, a different type of quantity. The common mistake avoided here is treating every numeric column as if its scale and meaning were interchangeable.
Common Mistakes
- Pooled instead of column-wise calculation.
across()calculates a separate statistic for each selected variable; it does not average all their values together. - Including numeric identifiers. A column’s storage type does not determine whether its summary is meaningful. Select the intended measures rather than blindly using all numeric columns.
- Forgetting missing-value behavior. A summary function may return
NAif a selected column contains missing values and missing values are not removed. - Comparing incompatible units. A dollar mean and a count mean are both numbers, but their magnitudes cannot be interpreted as a direct comparison.
- Assuming equal sample sizes. If missing values differ by column, the summaries can be based on different numbers of observed rows.
summarise(across(...)) to apply a chosen statistic separately to several selected columns. Choose the columns deliberately, handle missing values explicitly, and interpret each result using that variable’s units and available observations.
Check Your Understanding
For each problem, determine the appropriate column-wise summary and interpret the result in context.
- A hypothetical sensor file contains daily readings of 10, 20, 30, and 40 units in one column, and 12, 14, 16, and 18 units in another. What means should an across-based summary return? (Answer: 25 units and 15 units, respectively.)
- A file contains response times of 4, 8, and 12 minutes and a missing value. What is the median when missing values are removed? (Answer: 8 minutes.)
- A numeric contract key has values 801, 802, and 803. Why should it normally be excluded from a mean across numeric measures? (Answer: Its mean is 802, but the key is an identifier, so that arithmetic summary has no useful measurement interpretation.)
- A hypothetical four-row data set has premiums of 100, 150, 250, and 300 dollars, and claim counts of 0, 1, 1, and 2. What column means should be returned, and why is a single pooled mean inappropriate? (Answer: Premium mean \(=200\) dollars and claim-count mean \(=1\) claim; the columns represent different units and must be summarized separately.)
- A grouped data frame has two regions, each with three observed policy durations. If it is grouped by region before summarising the mean duration, how many summary rows should be returned? (Answer: Two rows, one mean for each region.)