The idea to make the set of the papers for R script enhancement and advanced performance have arisen due to:
Content
- vectorization
- fast data reading
- fast data writing
- script enhancement
fast reading data
The overview of fast reading options see here
fast writing data
The overview of fast reading options see here
R script enhancements
Vectorise and pre-allocate data structures
Examples are taken from here
# Create the data frame
col1 <- runif (12^5, 0, 2)
col2 <- rnorm (12^5, 0, 2)
col3 <- rpois (12^5, 3)
col4 <- rchisq (12^5, 2)
df <- data.frame (col1, col2, col3, col4)
The logic we are about to optimise:
For every row on this data frame (df), check if the sum of all values is greater than 4.
If it is, a new 5th variable gets the value “greater_than_4”, else, it gets “lesser_than_4”.
# Original R code: Before vectorization and pre-allocation
system.time({
for (i in 1:nrow(df)) { # for every row
if ((df[i, 'col1'] + df[i, 'col2'] + df[i, 'col3'] + df[i, 'col4']) > 4) { # check if > 4
df[i, 5] <- "greater_than_4" # assign 5th column
} else {
df[i, 5] <- "lesser_than_4" # assign 5th column
}
}
})
user system elapsed
936.58 62.32 1233.08
df[,5] <- NULL
# after vectorization and pre-allocation
output <- character (nrow(df)) # initialize output vector for vectorization
# loop with preallocated vector
system.time({
for (i in 1:nrow(df)) {
if ((df[i, 'col1'] + df[i, 'col2'] + df[i, 'col3'] + df[i, 'col4']) > 4) {
output[i] <- "greater_than_4" #looping through the vector but not a dataframe
} else {
output[i] <- "lesser_than_4" #looping through the vector but not a dataframe
}
}
# actual vectorization: in place of row-by-row transactions we manipulate the single vector only.
df$output <- output
}
)
user system elapsed
31.77 0.07 42.01
df[,5] <- NULL
Place IF statements (expressions) outside the loop.
Examples are taken from here
# initialize output vector for vectorization
output <- character (nrow(df))
# the expression in IF statement
condition <- (df$col1 + df$col2 + df$col3 + df$col4) > 4 # condition check outside the loop
system.time({
for (i in 1:nrow(df)) {
if (condition[i]) {
output[i] <- "greater_than_4"
} else {
output[i] <- "lesser_than_4"
}
}
df$output <- output
})
user system elapsed
1.61 0.00 2.53
df[,5] <- NULL
The LOOP only for True conditions
Examples are taken from here
# initialize output vector for vectorization
output <- character (nrow(df))
# the expression in IF statement
condition <- (df$col1 + df$col2 + df$col3 + df$col4) > 4 # condition check outside the loop
system.time({
for (i in (1:nrow(df))[condition]) { # run loop only for true conditions
if (condition[i]) {
output[i] <- "greater_than_4"
} else {
output[i] <- "lesser_than_4"
}
}
# actual vectorization: in place of row-by-row transactions we manipulate the single vector only.
df$output <- output
})
user system elapsed
0.39 0.00 0.41
df[,5] <- NULL
Use ifelse() whenever possible
Examples are taken from here
You can make this logic much simpler and faster by using the ifelse() statement.
Looks like this is going to be a highly preferred option to speed up simple loops.
system.time({
output <- ifelse ((df$col1 + df$col2 + df$col3 + df$col4) > 4, "greater_than_4", "lesser_than_4")
df$output <- output
})
user system elapsed
0.17 0.00 0.17
df[,5] <- NULL
Using which()
Examples are taken from here
system.time({
want = which(rowSums(df) > 4)
output = rep("less than 4", times = nrow(df))
output[want] = "greater than 4"
df$output <- output
})
user system elapsed
0.02 0.00 0.02
df[,5] <- NULL
Use apply family of functions instead of for-loops
Examples are taken from here
# apply family
system.time({
myfunc <- function(x) {
if ((x['col1'] + x['col2'] + x['col3'] + x['col4']) > 4) {
"greater_than_4"
} else {
"lesser_than_4"
}
}
output <- apply(df[, c(1:4)], 1, FUN=myfunc) # apply 'myfunc' on every row
df$output <- output
})
user system elapsed
3.01 0.02 3.85
df[,5] <- NULL
Use byte code compilation for functions cmpfun()
We will use initial dataframe as the input and the initial FOR loop (as in the first example).
f <- function(df){
for (i in 1:nrow(df)) { # for every row
if ((df[i, 'col1'] + df[i, 'col2'] + df[i, 'col3'] + df[i, 'col4']) > 4) { # check if > 4
df[i, 5] <- "greater_than_4" # assign 5th column
} else {
df[i, 5] <- "lesser_than_4" # assign 5th column
}
}
}
library(compiler)
g <- cmpfun(f)
system.time(
g(df)
)
user system elapsed
558.29 254.10 872.24
df[,5] <- NULL
LS0tDQp0aXRsZTogIkVuaGFuY2VkIFIgc2NyaXB0Ig0Kb3V0cHV0OiBodG1sX25vdGVib29rDQphdXRob3I6IERlbXlkIER6eXViYW4NCmRhdGU6IDE2LzEyLzIwMTYNCi0tLQ0KDQpUaGUgaWRlYSB0byBtYWtlIHRoZSBzZXQgb2YgdGhlIHBhcGVycyBmb3IgUiBzY3JpcHQgZW5oYW5jZW1lbnQgYW5kIGFkdmFuY2VkIHBlcmZvcm1hbmNlIGhhdmUgYXJpc2VuIGR1ZSB0bzogIA0KDQoNCiogW1IgaW4gYWN0aW9uXShodHRwczovL3d3dy5tYW5uaW5nLmNvbS9ib29rcy9yLWluLWFjdGlvbi1zZWNvbmQtZWRpdGlvbikuDQpJdCBpcyBzdHJvbmdseSByZWNvbW1lbmRlZCBmb3Igd2lkZSByYW5nZSBvZiBuZXdjb21lcnMsIGNvbXB1dGF0aW9uYWwgZ2Vla3MsIGV0Yy4gYXMgYSBzdGFydGluZyBwb2ludCB0byB0cmF2ZWwgdG8gYW1hemluZyBSIGluZmVybmFsIHNob3Jlcy4gIA0KDQoqIFtSIElORkVSTk9dKGh0dHA6Ly93d3cuYnVybnMtc3RhdC5jb20vZG9jdW1lbnRzL2Jvb2tzL3RoZS1yLWluZmVybm8vKS4NClRoaXMgaXMgYW4gZXhjZWxsZW50IHdheSB0byBzdGFydCBpbmNyZWRpYmxlIHRyaXAgdGhyb3VnaCBSIGluZmVybmFsIGNpcmNsZXMgOi0pDQoNCiogW0FkdmFuY2VkIFJdKGh0dHA6Ly9hZHYtci5oYWQuY28ubnovKS4NCk5pY2UgYm9vayB0byByZWFkIGZvciB0aG9zZSB3aG8gc3Vydml2ZWQgcHJldmlvdXMgc3RhZ2VzLg0KDQoqIFtTdGFja292ZXJmbG93LmNvbV0oaHR0cDovL3N0YWNrb3ZlcmZsb3cuY29tLykuDQpUaGUgZW5kbGVzcyBmb3VudGFpbiBvZiB0aGUgZXRlcm5hbCB3aXNkb20gb2YgUiBkd2VsbGVycyA6LSkNCg0KKiBbd3d3LnItYmxvZ2dlcnMuY29tXShodHRwczovL3d3dy5yLWJsb2dnZXJzLmNvbS8pDQpWZXJ5IG5pY2UgcGxhY2Ugb2Ygc21hcnQsIHBsYWluIGFuZCBzaG9ydCBoaW50cywgY29tbWVudHMgYW5kIGV4cGxhbmF0aW9ucy4NCg0KDQoNCiMjIyAqKkNvbnRlbnQqKg0KDQoqIHZlY3Rvcml6YXRpb24NCiogZmFzdCBkYXRhIHJlYWRpbmcgDQoqIGZhc3QgZGF0YSB3cml0aW5nDQoqIHNjcmlwdCBlbmhhbmNlbWVudA0KICANCg0KDQojIyMjICoqdmVjdG9yaXphdGlvbioqDQoNClRoZSBicmllZiBleGFtcGxlIG9mIFtzZWUgaGVyZSAtIDIuMy4yIEV4YW1wbGUgb2YgdmVjdG9yaXphdGlvbl0oaHR0cHM6Ly9ycHVicy5jb20vZGVteWRkLzIxMDU0MSkNCg0KDQojIyMjICoqZmFzdCByZWFkaW5nIGRhdGEqKg0KVGhlIG92ZXJ2aWV3IG9mIGZhc3QgcmVhZGluZyBvcHRpb25zIFtzZWUgaGVyZSBdKGh0dHBzOi8vcnB1YnMuY29tL2RlbXlkZC8yMTA1NDEpDQoNCg0KDQojIyMjICoqZmFzdCB3cml0aW5nIGRhdGEqKg0KVGhlIG92ZXJ2aWV3IG9mIGZhc3QgcmVhZGluZyBvcHRpb25zIFtzZWUgaGVyZSBdKGh0dHBzOi8vcnB1YnMuY29tL2RlbXlkZC8yMTA1NDEpDQoNCg0KIyMjICoqUiBzY3JpcHQgZW5oYW5jZW1lbnRzKioNCg0KDQojIyMjICpWZWN0b3Jpc2UgYW5kIHByZS1hbGxvY2F0ZSBkYXRhIHN0cnVjdHVyZXMqDQpFeGFtcGxlcyBhcmUgdGFrZW4gZnJvbSBbaGVyZV0oaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vc3RyYXRlZ2llcy10by1zcGVlZHVwLXItY29kZS8pDQoNCg0KYGBge3J9DQojIENyZWF0ZSB0aGUgZGF0YSBmcmFtZQ0KY29sMSA8LSBydW5pZiAoMTJeNSwgMCwgMikNCmNvbDIgPC0gcm5vcm0gKDEyXjUsIDAsIDIpDQpjb2wzIDwtIHJwb2lzICgxMl41LCAzKQ0KY29sNCA8LSByY2hpc3EgKDEyXjUsIDIpDQpkZiA8LSBkYXRhLmZyYW1lIChjb2wxLCBjb2wyLCBjb2wzLCBjb2w0KQ0KDQpgYGANCg0KKlRoZSBsb2dpYyB3ZSBhcmUgYWJvdXQgdG8gb3B0aW1pc2U6KiAgDQpGb3IgZXZlcnkgcm93IG9uIHRoaXMgZGF0YSBmcmFtZSAoZGYpLCBjaGVjayBpZiB0aGUgc3VtIG9mIGFsbCB2YWx1ZXMgaXMgZ3JlYXRlciB0aGFuIDQuIA0KDQpJZiBpdCBpcywgYSBuZXcgNXRoIHZhcmlhYmxlIGdldHMgdGhlIHZhbHVlICJncmVhdGVyX3RoYW5fNCIsIGVsc2UsIGl0IGdldHMgImxlc3Nlcl90aGFuXzQiLg0KDQpgYGB7cn0NCg0KIyBPcmlnaW5hbCBSIGNvZGU6IEJlZm9yZSB2ZWN0b3JpemF0aW9uIGFuZCBwcmUtYWxsb2NhdGlvbg0Kc3lzdGVtLnRpbWUoew0KICBmb3IgKGkgaW4gMTpucm93KGRmKSkgeyAjIGZvciBldmVyeSByb3cNCiAgICBpZiAoKGRmW2ksICdjb2wxJ10gKyBkZltpLCAnY29sMiddICsgZGZbaSwgJ2NvbDMnXSArIGRmW2ksICdjb2w0J10pID4gNCkgeyAjIGNoZWNrIGlmID4gNA0KICAgICAgZGZbaSwgNV0gPC0gImdyZWF0ZXJfdGhhbl80IiAjIGFzc2lnbiA1dGggY29sdW1uDQogICAgfSBlbHNlIHsNCiAgICAgIGRmW2ksIDVdIDwtICJsZXNzZXJfdGhhbl80IiAjIGFzc2lnbiA1dGggY29sdW1uDQogICAgfQ0KICB9DQp9KQ0KDQpkZlssNV0gPC0gTlVMTA0KDQpgYGANCg0KYGBge3J9DQojIGFmdGVyIHZlY3Rvcml6YXRpb24gYW5kIHByZS1hbGxvY2F0aW9uDQpvdXRwdXQgPC0gY2hhcmFjdGVyIChucm93KGRmKSkgIyBpbml0aWFsaXplIG91dHB1dCB2ZWN0b3IgZm9yIHZlY3Rvcml6YXRpb24NCg0KIyBsb29wIHdpdGggcHJlYWxsb2NhdGVkIHZlY3Rvcg0Kc3lzdGVtLnRpbWUoew0KICBmb3IgKGkgaW4gMTpucm93KGRmKSkgew0KICAgIGlmICgoZGZbaSwgJ2NvbDEnXSArIGRmW2ksICdjb2wyJ10gKyBkZltpLCAnY29sMyddICsgZGZbaSwgJ2NvbDQnXSkgPiA0KSB7DQogICAgICBvdXRwdXRbaV0gPC0gImdyZWF0ZXJfdGhhbl80IiAjbG9vcGluZyB0aHJvdWdoIHRoZSB2ZWN0b3IgYnV0IG5vdCBhIGRhdGFmcmFtZQ0KICAgIH0gZWxzZSB7DQogICAgICBvdXRwdXRbaV0gPC0gImxlc3Nlcl90aGFuXzQiICNsb29waW5nIHRocm91Z2ggdGhlIHZlY3RvciBidXQgbm90IGEgZGF0YWZyYW1lDQogICAgfQ0KICB9DQogIA0KIyBhY3R1YWwgdmVjdG9yaXphdGlvbjogaW4gcGxhY2Ugb2Ygcm93LWJ5LXJvdyB0cmFuc2FjdGlvbnMgd2UgbWFuaXB1bGF0ZSB0aGUgc2luZ2xlIHZlY3RvciBvbmx5LiAgDQpkZiRvdXRwdXQgPC0gb3V0cHV0IA0KfQ0KKQ0KDQpkZlssNV0gPC0gTlVMTA0KYGBgDQoNCiMjIyMgKlBsYWNlIElGIHN0YXRlbWVudHMgKGV4cHJlc3Npb25zKSBvdXRzaWRlIHRoZSBsb29wLioNCg0KRXhhbXBsZXMgYXJlIHRha2VuIGZyb20gW2hlcmVdKGh0dHBzOi8vd3d3LnItYmxvZ2dlcnMuY29tL3N0cmF0ZWdpZXMtdG8tc3BlZWR1cC1yLWNvZGUvKQ0KDQpgYGB7cn0NCiMgaW5pdGlhbGl6ZSBvdXRwdXQgdmVjdG9yIGZvciB2ZWN0b3JpemF0aW9uDQpvdXRwdXQgPC0gY2hhcmFjdGVyIChucm93KGRmKSkNCg0KIyB0aGUgZXhwcmVzc2lvbiBpbiBJRiBzdGF0ZW1lbnQNCmNvbmRpdGlvbiA8LSAoZGYkY29sMSArIGRmJGNvbDIgKyBkZiRjb2wzICsgZGYkY29sNCkgPiA0ICAjIGNvbmRpdGlvbiBjaGVjayBvdXRzaWRlIHRoZSBsb29wDQpzeXN0ZW0udGltZSh7DQogIGZvciAoaSBpbiAxOm5yb3coZGYpKSB7DQogICAgaWYgKGNvbmRpdGlvbltpXSkgew0KICAgICAgb3V0cHV0W2ldIDwtICJncmVhdGVyX3RoYW5fNCINCiAgICB9IGVsc2Ugew0KICAgICAgb3V0cHV0W2ldIDwtICJsZXNzZXJfdGhhbl80Ig0KICAgIH0NCiAgfQ0KICBkZiRvdXRwdXQgPC0gb3V0cHV0DQp9KQ0KDQpkZlssNV0gPC0gTlVMTA0KYGBgDQoNCg0KIyMjIyAqVGhlIExPT1Agb25seSBmb3IgVHJ1ZSBjb25kaXRpb25zKg0KDQpFeGFtcGxlcyBhcmUgdGFrZW4gZnJvbSBbaGVyZV0oaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vc3RyYXRlZ2llcy10by1zcGVlZHVwLXItY29kZS8pDQoNCmBgYHtyfQ0KIyBpbml0aWFsaXplIG91dHB1dCB2ZWN0b3IgZm9yIHZlY3Rvcml6YXRpb24NCm91dHB1dCA8LSBjaGFyYWN0ZXIgKG5yb3coZGYpKQ0KDQojIHRoZSBleHByZXNzaW9uIGluIElGIHN0YXRlbWVudA0KY29uZGl0aW9uIDwtIChkZiRjb2wxICsgZGYkY29sMiArIGRmJGNvbDMgKyBkZiRjb2w0KSA+IDQgICMgY29uZGl0aW9uIGNoZWNrIG91dHNpZGUgdGhlIGxvb3ANCg0Kc3lzdGVtLnRpbWUoew0KICBmb3IgKGkgaW4gKDE6bnJvdyhkZikpW2NvbmRpdGlvbl0pIHsgICMgcnVuIGxvb3Agb25seSBmb3IgdHJ1ZSBjb25kaXRpb25zDQogICAgaWYgKGNvbmRpdGlvbltpXSkgew0KICAgICAgb3V0cHV0W2ldIDwtICJncmVhdGVyX3RoYW5fNCINCiAgICB9IGVsc2Ugew0KICAgICAgb3V0cHV0W2ldIDwtICJsZXNzZXJfdGhhbl80Ig0KICAgIH0NCiAgfQ0KIyBhY3R1YWwgdmVjdG9yaXphdGlvbjogaW4gcGxhY2Ugb2Ygcm93LWJ5LXJvdyB0cmFuc2FjdGlvbnMgd2UgbWFuaXB1bGF0ZSB0aGUgc2luZ2xlIHZlY3RvciBvbmx5LiAgICANCmRmJG91dHB1dCA8LSBvdXRwdXQNCn0pDQoNCmRmWyw1XSA8LSBOVUxMDQpgYGANCg0KIyMjIyAqVXNlIGlmZWxzZSgpIHdoZW5ldmVyIHBvc3NpYmxlKg0KDQpFeGFtcGxlcyBhcmUgdGFrZW4gZnJvbSBbaGVyZV0oaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vc3RyYXRlZ2llcy10by1zcGVlZHVwLXItY29kZS8pDQoNCllvdSBjYW4gbWFrZSB0aGlzIGxvZ2ljIG11Y2ggc2ltcGxlciBhbmQgZmFzdGVyIGJ5IHVzaW5nIHRoZSBpZmVsc2UoKSBzdGF0ZW1lbnQuIA0KDQpMb29rcyBsaWtlIHRoaXMgaXMgZ29pbmcgdG8gYmUgYSBoaWdobHkgcHJlZmVycmVkIG9wdGlvbiB0byBzcGVlZCB1cCBzaW1wbGUgbG9vcHMuDQoNCmBgYHtyfQ0Kc3lzdGVtLnRpbWUoew0KICBvdXRwdXQgPC0gaWZlbHNlICgoZGYkY29sMSArIGRmJGNvbDIgKyBkZiRjb2wzICsgZGYkY29sNCkgPiA0LCAiZ3JlYXRlcl90aGFuXzQiLCAibGVzc2VyX3RoYW5fNCIpDQogIGRmJG91dHB1dCA8LSBvdXRwdXQNCn0pDQoNCmRmWyw1XSA8LSBOVUxMDQpgYGANCg0KIyMjIyAqVXNpbmcgd2hpY2goKSoNCg0KRXhhbXBsZXMgYXJlIHRha2VuIGZyb20gW2hlcmVdKGh0dHBzOi8vd3d3LnItYmxvZ2dlcnMuY29tL3N0cmF0ZWdpZXMtdG8tc3BlZWR1cC1yLWNvZGUvKQ0KDQpgYGB7cn0NCg0Kc3lzdGVtLnRpbWUoew0KICB3YW50ID0gd2hpY2gocm93U3VtcyhkZikgPiA0KQ0KICBvdXRwdXQgPSByZXAoImxlc3MgdGhhbiA0IiwgdGltZXMgPSBucm93KGRmKSkNCiAgb3V0cHV0W3dhbnRdID0gImdyZWF0ZXIgdGhhbiA0Ig0KICBkZiRvdXRwdXQgPC0gb3V0cHV0DQp9KSANCmRmWyw1XSA8LSBOVUxMDQpgYGANCg0KIyMjIyBVc2UgYXBwbHkgZmFtaWx5IG9mIGZ1bmN0aW9ucyBpbnN0ZWFkIG9mIGZvci1sb29wcw0KDQpFeGFtcGxlcyBhcmUgdGFrZW4gZnJvbSBbaGVyZV0oaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vc3RyYXRlZ2llcy10by1zcGVlZHVwLXItY29kZS8pDQoNCmBgYHtyfQ0KIyBhcHBseSBmYW1pbHkNCnN5c3RlbS50aW1lKHsNCiAgbXlmdW5jIDwtIGZ1bmN0aW9uKHgpIHsNCiAgICBpZiAoKHhbJ2NvbDEnXSArIHhbJ2NvbDInXSArIHhbJ2NvbDMnXSArIHhbJ2NvbDQnXSkgPiA0KSB7DQogICAgICAiZ3JlYXRlcl90aGFuXzQiDQogICAgfSBlbHNlIHsNCiAgICAgICJsZXNzZXJfdGhhbl80Ig0KICAgIH0NCiAgfQ0KICBvdXRwdXQgPC0gYXBwbHkoZGZbLCBjKDE6NCldLCAxLCBGVU49bXlmdW5jKSAgIyBhcHBseSAnbXlmdW5jJyBvbiBldmVyeSByb3cNCiAgZGYkb3V0cHV0IDwtIG91dHB1dA0KfSkNCmRmWyw1XSA8LSBOVUxMDQpgYGANCg0KIyMjIyBVc2UgYnl0ZSBjb2RlIGNvbXBpbGF0aW9uIGZvciBmdW5jdGlvbnMgY21wZnVuKCkNCg0KV2Ugd2lsbCB1c2UgaW5pdGlhbCBkYXRhZnJhbWUgYXMgdGhlIGlucHV0IGFuZCB0aGUgaW5pdGlhbCBGT1IgbG9vcCAoYXMgaW4gdGhlIGZpcnN0IGV4YW1wbGUpLg0KDQpgYGB7cn0NCg0KZiA8LSBmdW5jdGlvbihkZil7DQogICBmb3IgKGkgaW4gMTpucm93KGRmKSkgeyAjIGZvciBldmVyeSByb3cNCiAgICBpZiAoKGRmW2ksICdjb2wxJ10gKyBkZltpLCAnY29sMiddICsgZGZbaSwgJ2NvbDMnXSArIGRmW2ksICdjb2w0J10pID4gNCkgeyAjIGNoZWNrIGlmID4gNA0KICAgICAgZGZbaSwgNV0gPC0gImdyZWF0ZXJfdGhhbl80IiAjIGFzc2lnbiA1dGggY29sdW1uDQogICAgfSBlbHNlIHsNCiAgICAgIGRmW2ksIDVdIDwtICJsZXNzZXJfdGhhbl80IiAjIGFzc2lnbiA1dGggY29sdW1uDQogICAgfQ0KICB9DQp9DQoNCmxpYnJhcnkoY29tcGlsZXIpDQoNCmcgPC0gY21wZnVuKGYpDQoNCnN5c3RlbS50aW1lKA0KICBnKGRmKQ0KKQ0KZGZbLDVdIDwtIE5VTEwNCmBgYA0KDQoNCg0KDQoNCg0K