There a several ways to access a specific element in a data frame, using various combinations of brackets ([ ]
), and dollar signs ($
). In time-sensitive functions, which one to use can be important?
Benchmarking some of the possible combinations:
library(microbenchmark)
df <- data.frame(a=1:6,b=1:6,c=1:6,d=1:6,e=1:6,f=1:6)
microbenchmark(df$c[3],
df[3,]$c,
df[3,3],
df[3,][3],
df[3,][[3]],
df[,3][3],
times=1e3)
yields these timings:
Unit: microseconds
expr min lq mean median uq max neval
df$c[3] 9.836 11.4505 14.03068 12.2015 12.9280 1252.854 1000
df[3, ]$c 77.204 89.5750 100.18752 92.2445 98.6395 1351.521 1000
df[3, 3] 15.719 18.9850 21.04074 19.6010 20.7400 82.519 1000
df[3, ][3] 88.599 100.5920 110.59009 104.0415 110.5435 409.050 1000
df[3, ][[3]] 75.856 87.2200 98.67104 89.9360 96.1695 1391.299 1000
df[, 3][3] 11.639 13.4225 14.77493 13.9510 14.6905 55.172 1000
Where we see that df$c[3]
is fastest, closely followed by df[,3][3]
. Others are much slower.
In time sensitive appplications, I often use data tables rather than frames, because sorting and subsetting operations are typically much faster. However, addressing operations can be much slower, as we see if we repeat the above for a data.table
:
library(data.table)
dt <- as.data.table(df)
microbenchmark(dt$c[3],
dt[3,]$c,
dt[3,3],
dt[3,][[3]],
times=1e3)
Unit: microseconds
expr min lq mean median uq max neval
dt$c[3] 9.503 11.4020 14.90066 12.6820 13.8950 1336.407 1000
dt[3, ]$c 417.756 437.0495 480.26532 448.8625 463.6350 2909.038 1000
dt[3, 3] 205.115 218.9590 238.78000 227.9575 239.1265 1554.503 1000
dt[3, ][[3]] 414.378 435.2115 470.76853 447.1505 461.3310 1906.432 1000
My question is this: Is $[ ]
guaranteed to always be the fastest addressing method, or can this depend on factors such as the types of data in the data frame (or table), the platform (OS), or the build version? If anyone can explain the reasons underlying differences in timing, and/or the pros/cons of various approaches, that would be also useful.
UPDATE
Following the suggestions in the answer from 42- the test is repeated here using more rows and with the additional syntax options from both 42- and also in the comment by A.Webb who suggested df[[3,3]] as the fastest. (note: I also tried the same test but accessing higher row numbers, but timing seems to be independent of which row is selected).
df <- data.frame(a=1:1000,b=1:1000,c=1:1000,d=1:1000,e=1:1000,f=1:1000)
Unit: microseconds
expr min lq mean median uq max neval
df$c[3] 8.314 9.7610 12.870667 10.6260 12.0950 1250.339 1000
df[["c"]][3] 6.932 8.0670 9.652672 8.7075 9.9445 26.512 1000
(df[3, ])$c 72.395 77.2390 90.893724 79.8320 95.8540 256.082 1000
df[3, 3] 14.871 16.2625 19.377482 17.1180 20.1720 47.720 1000
df[3, ][3] 82.446 86.7680 102.462603 89.9660 107.7965 232.685 1000
df[3, ][[3]] 70.559 75.2140 93.581394 78.3385 93.4235 1507.933 1000
df[, 3][3] 9.933 11.4770 13.430309 12.1090 14.0900 38.213 1000
df[[3, 3]] 6.465 7.8355 9.236773 8.4500 9.6355 29.833 1000
So it looks like df[[i,j]] is fastest, followed extremely closely by df[["colname"]][j]. Which of these to use would probably depend on whether you need to use column names or numbers.
The question is still open if we can assume that this is always the case on all platforms and for all data types.