Whether you are familiar with the concept of functional programming (FP) or not, you have probably came across the Reduce function in R at some point. In one context, we may think about FP as a way to execute a function over a vector or list simultaneously. Of course, one would argue why should I care about it? My quick answer is merging data.
Let’s consider two ETFs (SPY and XLF) and one stock (TSLA). The stock prices date back to June 2010, whereas the SPY (respectively XLF) prices date back to January 1993 (December 1998).
library(quantmod)
v <- c("SPY","XLF","TSLA")
P.list <- lapply(v, function(v_i) get(getSymbols(v_i, from = "1990-01-01")) )
lapply(P.list, start)
[[1]]
[1] "1993-01-29"
[[2]]
[1] "1998-12-22"
[[3]]
[1] "2010-06-29"
If one is interested in the adjusted prices of all three underlying assets combined altogether, merging xts elements is straightforward:
P.list2 <- lapply(P.list, function(p) p[,6] ) # adjusted prices located in the sixth column
t1 <- system.time({
P <- merge(merge(P.list2[[1]],P.list2[[2]]),P.list2[[3]])
})
head(P)
SPY.Adjusted XLF.Adjusted TSLA.Adjusted
1993-01-29 26.95485 NA NA
1993-02-01 27.14656 NA NA
1993-02-02 27.20405 NA NA
1993-02-03 27.49164 NA NA
1993-02-04 27.60668 NA NA
1993-02-05 27.58747 NA NA
Alternatively, one may consider running a loop:
t2 <- system.time({
P2 <- P.list2[[1]]
for(i in 2:length(P.list2)) {
P2 <- merge(P2,P.list2[[i]])
}
})
identical(P2,P)
[1] TRUE
In either case, we get an identical result. Nonetheless, the latter option is much slower, while the former is less tractable to implement - especially when the number of the underlying assets is large.
t2[3]/t1[3]
elapsed
5
As we observe above, it takes the loop to 8 times longer than the first option. However, writing the code for P1 is not the best way to go. This is where the Reduce function comes into the picture:
t3 <- system.time(P3 <- Reduce(merge,P.list2))
identical(P3,P2)
[1] TRUE
As its first input, Reduce takes a specific function (in our case merge), whereas the second input takes a list (in our case list of adjusted prices). In terms of operations, the function executes the merging in the same fashion as in P1. First, Reduce takes the first two time series and merge them altogether. Second, it takes the combined result and merges it with the third time series. To see this, consider the following example
Reduce(`-`,1:3)
[1] -4
The Reduce(-,1:3) command is equivalent to the following
(1-2)-3
[1] -4
where the function applies the operator (-) on the first two (1-2= -1), and then run the operator on the result and the next element, i.e. (-1-3= -4).
Summary
Clearly, one can easily utilize the Reduce function to merge multiple time series rather than using a loop or doing so manually as discussed above. I would highly recommend this excellent vignette by Hadley Wickham for those interested in FP.
LS0tCnRpdGxlOiAiVGlwIG9mIHRoZSBNb250aDogYFJlZHVjZWAiCiNvdXRwdXQ6IHJtYXJrZG93bjo6Z2l0aHViX2RvY3VtZW50Cm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0CmF1dGhvcjogTWFqZWVkIFNpbWFhbgpkYXRlOiBBcHJpbCwgMjAxOApmaWdfd2lkdGg6IDIwCi0tLQpXaGV0aGVyIHlvdSBhcmUgZmFtaWxpYXIgd2l0aCB0aGUgY29uY2VwdCBvZiBmdW5jdGlvbmFsIHByb2dyYW1taW5nIChGUCkgb3Igbm90LCB5b3UgaGF2ZSBwcm9iYWJseSBjYW1lIGFjcm9zcyB0aGUgYFJlZHVjZWAgZnVuY3Rpb24gIGluIFIgYXQgc29tZSBwb2ludC4gSW4gb25lIGNvbnRleHQsIHdlIG1heSB0aGluayBhYm91dCBGUCBhcyBhIHdheSB0byBleGVjdXRlIGEgZnVuY3Rpb24gb3ZlciBhIHZlY3RvciBvciBsaXN0IHNpbXVsdGFuZW91c2x5LiBPZiBjb3Vyc2UsIG9uZSB3b3VsZCBhcmd1ZSB3aHkgc2hvdWxkIEkgY2FyZSBhYm91dCBpdD8gTXkgcXVpY2sgYW5zd2VyIGlzIG1lcmdpbmcgZGF0YS4KCkxldCdzIGNvbnNpZGVyIHR3byBFVEZzIChTUFkgYW5kIFhMRikgYW5kIG9uZSBzdG9jayAoVFNMQSkuIFRoZSBzdG9jayBwcmljZXMgZGF0ZSBiYWNrIHRvIEp1bmUgMjAxMCwgd2hlcmVhcyB0aGUgU1BZIChyZXNwZWN0aXZlbHkgWExGKSBwcmljZXMgZGF0ZSBiYWNrIHRvIEphbnVhcnkgMTk5MyAoRGVjZW1iZXIgMTk5OCkuIApgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHF1YW50bW9kKQp2IDwtIGMoIlNQWSIsIlhMRiIsIlRTTEEiKQpQLmxpc3QgPC0gbGFwcGx5KHYsIGZ1bmN0aW9uKHZfaSkgZ2V0KGdldFN5bWJvbHModl9pLCBmcm9tID0gIjE5OTAtMDEtMDEiKSkgKSAgCmxhcHBseShQLmxpc3QsIHN0YXJ0KQpgYGAKSWYgb25lIGlzIGludGVyZXN0ZWQgaW4gdGhlIGFkanVzdGVkIHByaWNlcyBvZiBhbGwgdGhyZWUgdW5kZXJseWluZyBhc3NldHMgY29tYmluZWQgYWx0b2dldGhlciwgbWVyZ2luZyBgeHRzYCBlbGVtZW50cyBpcyBzdHJhaWdodGZvcndhcmQ6CmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9ClAubGlzdDIgPC0gbGFwcGx5KFAubGlzdCwgZnVuY3Rpb24ocCkgcFssNl0gKSAjIGFkanVzdGVkIHByaWNlcyBsb2NhdGVkIGluIHRoZSBzaXh0aCBjb2x1bW4KdDEgPC0gc3lzdGVtLnRpbWUoewogIFAgPC0gbWVyZ2UobWVyZ2UoUC5saXN0MltbMV1dLFAubGlzdDJbWzJdXSksUC5saXN0MltbM11dKQogIH0pCmhlYWQoUCkKYGBgCkFsdGVybmF0aXZlbHksIG9uZSBtYXkgY29uc2lkZXIgcnVubmluZyBhIGxvb3A6CmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnQyIDwtIHN5c3RlbS50aW1lKHsKICBQMiA8LSBQLmxpc3QyW1sxXV0KICBmb3IoaSBpbiAyOmxlbmd0aChQLmxpc3QyKSkgewogICAgUDIgPC0gbWVyZ2UoUDIsUC5saXN0MltbaV1dKQogIH0KfSkKaWRlbnRpY2FsKFAyLFApCmBgYApJbiBlaXRoZXIgY2FzZSwgd2UgZ2V0IGFuIGlkZW50aWNhbCByZXN1bHQuIE5vbmV0aGVsZXNzLCB0aGUgbGF0dGVyIG9wdGlvbiBpcyBtdWNoIHNsb3dlciwgd2hpbGUgdGhlIGZvcm1lciBpcyBsZXNzIHRyYWN0YWJsZSB0byBpbXBsZW1lbnQgLSBlc3BlY2lhbGx5IHdoZW4gdGhlIG51bWJlciBvZiB0aGUgdW5kZXJseWluZyBhc3NldHMgaXMgbGFyZ2UuCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnQyWzNdL3QxWzNdCmBgYApBcyB3ZSBvYnNlcnZlIGFib3ZlLCBpdCB0YWtlcyB0aGUgbG9vcCB0byA4IHRpbWVzIGxvbmdlciB0aGFuIHRoZSBmaXJzdCBvcHRpb24uIEhvd2V2ZXIsIHdyaXRpbmcgdGhlIGNvZGUgZm9yIGBQMWAgaXMgbm90IHRoZSBiZXN0IHdheSB0byBnby4gVGhpcyBpcyB3aGVyZSB0aGUgYFJlZHVjZWAgZnVuY3Rpb24gY29tZXMgaW50byB0aGUgcGljdHVyZToKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KdDMgPC0gc3lzdGVtLnRpbWUoUDMgPC0gUmVkdWNlKG1lcmdlLFAubGlzdDIpKQppZGVudGljYWwoUDMsUDIpCmBgYApBcyBpdHMgZmlyc3QgaW5wdXQsIGBSZWR1Y2VgIHRha2VzIGEgc3BlY2lmaWMgZnVuY3Rpb24gKGluIG91ciBjYXNlIGBtZXJnZWApLCB3aGVyZWFzIHRoZSBzZWNvbmQgaW5wdXQgdGFrZXMgYSBsaXN0IChpbiBvdXIgY2FzZSBsaXN0IG9mIGFkanVzdGVkIHByaWNlcykuIEluIHRlcm1zIG9mIG9wZXJhdGlvbnMsIHRoZSBmdW5jdGlvbiBleGVjdXRlcyB0aGUgbWVyZ2luZyBpbiB0aGUgc2FtZSBmYXNoaW9uIGFzIGluIGBQMWAuIEZpcnN0LCBgUmVkdWNlYCB0YWtlcyB0aGUgZmlyc3QgdHdvIHRpbWUgc2VyaWVzIGFuZCBtZXJnZSB0aGVtIGFsdG9nZXRoZXIuIFNlY29uZCwgaXQgdGFrZXMgdGhlIGNvbWJpbmVkIHJlc3VsdCBhbmQgbWVyZ2VzIGl0IHdpdGggdGhlIHRoaXJkIHRpbWUgc2VyaWVzLiBUbyBzZWUgdGhpcywgY29uc2lkZXIgdGhlIGZvbGxvd2luZyBleGFtcGxlCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9ClJlZHVjZShgLWAsMTozKQpgYGAKVGhlIGBSZWR1Y2UoYC1gLDE6MylgIGNvbW1hbmQgaXMgZXF1aXZhbGVudCB0byB0aGUgZm9sbG93aW5nCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CigxLTIpLTMKYGBgCndoZXJlIHRoZSBmdW5jdGlvbiBhcHBsaWVzIHRoZSBvcGVyYXRvciAoYC1gKSBvbiB0aGUgZmlyc3QgdHdvIChgMS0yPSAtMWApLCBhbmQgdGhlbiBydW4gdGhlIG9wZXJhdG9yIG9uIHRoZSByZXN1bHQgYW5kIHRoZSBuZXh0IGVsZW1lbnQsIGkuZS4gKGAtMS0zPSAtNGApLiAKCiMjIyBTdW1tYXJ5CkNsZWFybHksIG9uZSBjYW4gZWFzaWx5IHV0aWxpemUgdGhlIGBSZWR1Y2VgIGZ1bmN0aW9uIHRvIG1lcmdlIG11bHRpcGxlIHRpbWUgc2VyaWVzIHJhdGhlciB0aGFuIHVzaW5nIGEgbG9vcCBvciBkb2luZyBzbyBtYW51YWxseSBhcyBkaXNjdXNzZWQgYWJvdmUuIEkgd291bGQgaGlnaGx5IHJlY29tbWVuZCBbdGhpc10oaHR0cDovL2Fkdi1yLmhhZC5jby5uei9GdW5jdGlvbmFscy5odG1sI2Z1bmN0aW9uYWxzLWZwKSBleGNlbGxlbnQgdmlnbmV0dGUgYnkgSGFkbGV5IFdpY2toYW0gZm9yIHRob3NlIGludGVyZXN0ZWQgaW4gRlAuCg==