8 October 2025
When we run functions in R, we often have output that we want to use. What we should not do is copy the values from the screen and paste them where we need them: if our data changes, these hard-coded values will not update. We need a way to extract them from our results. I’ll show how to do this with two examples.
Suppose we ran a t-test on iron to get the confidence interval on the mean of iron.
> t.test(iron) One Sample t-test data: iron t = 6.4178, df = 116, p-value = 3.134e-09 alternative hypothesis: true mean is not equal to 0 95 percent confidence interval: 1.554541 2.942352 sample estimates: mean of x 2.248447
Suppose we wanted to add these as annotations on a plot. We should not hard-code those values by copying and pasting (or typing) them into a command like this:
label1 <- "1.55" label2 <- "2.94"
If the data changes or we adapt our code for another data set, these values will not change unless we remember to modify them. It would be far better to extract them so that they will be updated automatically. The first steps would be to save our object and inspect its structure. This will give us clues on how to get the values we need.
> ironTest <- t.test(iron) > str(ironTest) List of 10 $ statistic : Named num 6.42 ..- attr(*, "names")= chr "t" $ parameter : Named num 116 ..- attr(*, "names")= chr "df" $ p.value : num 3.13e-09 $ conf.int : num [1:2] 1.55 2.94 ..- attr(*, "conf.level")= num 0.95 $ estimate : Named num 2.25 ..- attr(*, "names")= chr "mean of x" $ null.value : Named num 0 ..- attr(*, "names")= chr "mean" $ stderr : num 0.35 $ alternative: chr "two.sided" $ method : chr "One Sample t-test" $ data.name : chr "iron" - attr(*, "class")= chr "htest"
The first line tells us that the results are a list of 10 objects, and that each of them is named (statistic, parameter, etc.). We see one labeled “conf.int”, so we realize that we could use dollar-sign notation to retrieve what we need.
> ironTest$conf.int [1] 1.554541 2.942352 attr(,"conf.level") [1] 0.95
This gave us the confidence limits, but it also gave us something else, something called an attribute. Here, the attribute is a tag that says these reflect a 95% confidence level. We would like to remove them, and there are two ways to go about this.
First, we could set the attributes to have a value of NULL, which deletes them. To do that, we need to add a step to create an object that holds the confidence interval (ironCI). If we do all this and display the result, we see that we now have just the confidence interval.
> ironCI <- ironTest$conf.int > attributes(ironCI) <- NULL > ironCI [1] 1.554541 2.942352
This is just a two-element vector, so we can easily extract the lower and upper limits and round them to the two decimal places as we wanted them above.
label1 <- round(ironCI[1], 2) label2 <- round(ironCI[2], 2)
The second way we could get is even easier: we could use bracket notation to get the first two elements of the conf.int vector.
> ironTest$conf.int[1:2] [1] 1.554541 2.942352
As before, we could easily use these rounded to two decimal places.
label1 <- round(ironTest$conf.int[1], 2) label2 <- round(ironTest$conf.int[2], 2)
Although code like this initially looks complicated, read what is inside round() from left to right: run the test, extract the confidence interval, and get the first element. The second line is almost the same: run the test, extract the confidence interval, and get the second element. In each case, we “wrap” the results in the round() function to round them to two decimal places. Doing this leads to compact code that automatically updates itself.
In this second example, suppose we want to calculate η2 (eta squared) from an ANOVA. This requires extracting the explained sum of squares and the total sum of squares from an ANOVA table. Here is the ANOVA and its summary table we will work with.
> ironModel <- aov(logIron ~ mine) > summary(ironModel) Df Sum Sq Mean Sq F value Pr(>F) mine 1 0.15 0.1504 0.112 0.738 Residuals 78 104.29 1.3370
We can see the results we want from the summary table (0.15 and 104.29), but we should extract them, not hard-code them. The first step is to examine the structure of that summary table.
> str(summary(ironModel)) List of 1 $ :Classes 'anova' and 'data.frame': 2 obs. of 5 variables: ..$ Df : num [1:2] 1 78 ..$ Sum Sq : num [1:2] 0.15 104.29 ..$ Mean Sq: num [1:2] 0.15 1.34 ..$ F value: num [1:2] 0.112 NA ..$ Pr(>F) : num [1:2] 0.738 NA - attr(*, "class")= chr [1:2] "summary.aov" "listof"
The first line of the output tells us that the summary is a list containing one object. That may sound strange, but for some problems, this would be a list of multiple tables. Regardless, we need to extract the first object from the list using double-bracket notation. To simplify the next steps, we will save that to its own object, then we will display the structure of that object.
> anovaTable <- summary(ironModel)[[1]] > str(anovaTable) Classes 'anova' and 'data.frame': 2 obs. of 5 variables: $ Df : num 1 78 $ Sum Sq : num 0.15 104.29 $ Mean Sq: num 0.15 1.34 $ F value: num 0.112 NA $ Pr(>F) : num 0.738 NA
This object belongs to two classes, but the important one for us is that it is a data frame, something we are familiar with. This data frame has five columns ( Df, Sum Sq, etc.), and we need the values for the sum of squares. There are at least two ways to get these values.
> anovaTable[ , 'Sum Sq'] [1] 0.1503894 104.2878320 > anovaTable$'Sum Sq' [1] 0.1503894 104.2878320
To calculate η2, we need the explained sum of squares (0.150 here) and the total sum of squares (the sum of these two values). Because we are now dealing with a vector, we could combine this with either of the previous steps to get the values we need. We will start with the explained sum of squares; use the approach that seems easiest to you.
> anovaTable[1 , 'Sum Sq'] [1] 0.1503894 > anovaTable$'Sum Sq'[1] [1] 0.1503894
In each case, we are just getting the first element. Getting the total sum of squares is just as easy; we get all the values of the sum of squares and add them by wrapping them in the sum() function.
> sum(anovaTable[ , 'Sum Sq']) [1] 104.4382 > sum(anovaTable$'Sum Sq') [1] 104.4382
Now we can see that we could calculate η2 without making any objects besides our original ANOVA model.
> explained <- summary(ironModel)[[1]][1, 'Sum Sq'] > total <- sum(summary(ironModel)[[1]][ , 'Sum Sq']) > round(explained/total, 3) [1] 0.001
We have our answer: η2 is a tiny 0.001; it explains only 0.1% of the variance. If our data changes, or if we apply this code to other data, this calculation will automatically update itself.
The last code snippet looks complicated at first, but read that first line from left to right: run summary() on the ANOVA model to view the table, extract the first item from that list, then extract the first row of the sum of squares. For the second line, it is almost the same: run summary() on the ANOVA model to get the ANOVA table, extract the first item from the list, get all the values for the sum of squares, and use sum() to add them. Again, you have made compact code that will update itself.
You can always create additional objects to make your intent clear and to make your code self-commenting, such as making an object called etaSquared in that last line. Avoid making too many intermediate one-use objects, as they can unnecessarily complicate your code; finding good names for such intermediate objects can also be difficult.
When you need to extract some bit of information from an object, investigate it with str() and use what you know about extracting parts of lists, data frames, and vectors. Sometimes, you may have to dig in several layers to get what you need. If there are attributes set on the object you need, such as a name, you may have to remove those by setting NULL to attributes() or using unname(). Always remember that if you can see it, there is a way to extract it.