Repeat a block of code once per element, fill a vector you made in advance, and total a portfolio the long way and the short way.
A for loop runs the same block of code once for each element of a vector. The loop variable holds one element on each pass.
In this lesson I print prices one at a time, fill a result vector position by position, and total a portfolio in a loop before doing the same thing in one line.
Step 1. One pass per element
for (p in closes) reads as: give p the first element, run the block, then the second, and so on until the vector runs out. The block is everything inside the braces.
closes <-c(185.40, 187.20, 184.90) # three closing pricesfor (p in closes) { # p takes each element in turnprint(p) # this line runs three times}
[1] 185.4
[1] 187.2
[1] 184.9
# -> [1] 185.4# -> [1] 187.2# -> [1] 184.9
The body can do anything. Here it values ten shares at each price.
for (p in closes) { # same three pricesprint(p *10) # ten shares at that price}
[1] 1854
[1] 1872
[1] 1849
# -> [1] 1854# -> [1] 1872# -> [1] 1849print(p) # -> [1] 184.9 <- p keeps its last value
[1] 184.9
p still exists after the loop finishes, holding whatever it held on the final pass.
Step 2. Fill a vector you made first
Printing throws the numbers away. To keep them, make the answer vector before the loop and write into it. numeric(n) gives you a vector of n zeros.
closes <-c(185.40, 187.20, 184.90, 188.10) # four prices this timedoubled <-numeric(length(closes)) # four zeros, ready to be filledprint(doubled) # -> [1] 0 0 0 0
[1] 0 0 0 0
for (i inseq_along(closes)) { # i = 1, 2, 3, 4 doubled[i] <- closes[i] *2# write the answer into slot i}print(doubled) # -> [1] 370.8 374.4 369.8 376.2
[1] 370.8 374.4 369.8 376.2
Notice the loop variable is now i, a position rather than a value. closes[i] reads slot i, and doubled[i] <- writes into slot i.
The alternative, starting with an empty vector and pasting one element on the end each pass, makes R copy the whole vector every time. On four prices you will not notice. On four thousand you will.
Step 3. Why seq_along and not 1:length
seq_along(x) and 1:length(x) give the same positions for any vector that has some.
print(seq_along(closes)) # -> [1] 1 2 3 4
[1] 1 2 3 4
print(1:length(closes)) # -> [1] 1 2 3 4
[1] 1 2 3 4
They part company when the vector is empty.
empty <-numeric(0) # a vector with nothing in itprint(length(empty)) # -> [1] 0
[1] 0
print(seq_along(empty)) # -> integer(0)
integer(0)
print(1:length(empty)) # -> [1] 1 0
[1] 1 0
1:0 counts down from 1 to 0, so it hands the loop two positions that do not exist.
for (i in1:length(empty)) { # counts down: 1, then 0print(paste("body ran with i =", i)) # runs twice on an empty vector}
[1] "body ran with i = 1"
[1] "body ran with i = 0"
# -> [1] "body ran with i = 1"# -> [1] "body ran with i = 0"for (i inseq_along(empty)) { # no positions, so no passesprint(paste("body ran with i =", i)) # nothing prints}
The second loop printed nothing, which is what an empty vector should give you. Use seq_along.
Step 4. Value a portfolio
Three tickers, three prices, three share counts. The loop fills one position value per holding.
tickers <-c("AAA", "CCC", "DDD") # three holdingsprices <-c(185.40, 410.20, 128.75) # one price eachshares <-c(10, 4, 20) # one share count eachvalues <-numeric(length(prices)) # three zeros to fillfor (i inseq_along(prices)) { # i = 1, 2, 3 values[i] <- prices[i] * shares[i] # price times shares for holding i}print(values) # -> [1] 1854.0 1640.8 2575.0
[1] 1854.0 1640.8 2575.0
Because i indexes every vector, one loop can read the ticker and the value together.
for (i inseq_along(tickers)) { # walk both vectors at onceprint(paste(tickers[i], "is worth", values[i])) # label and amount on one line}
[1] "AAA is worth 1854"
[1] "CCC is worth 1640.8"
[1] "DDD is worth 2575"
# -> [1] "AAA is worth 1854"# -> [1] "CCC is worth 1640.8"# -> [1] "DDD is worth 2575"
Adding the positions up is another loop, this time over values rather than positions. The running total starts at 0 because 0 is neutral for adding.
total <-0# the running totalfor (v in values) { # v takes each position value total <- total + v # add it on}print(total) # -> [1] 6069.8
[1] 6069.8
Step 5. The same thing in one line
Lesson 1 showed that arithmetic in R runs element by element, so prices * shares already does what the first loop did. sum() does what the second loop did.
print(sum(prices * shares)) # -> [1] 6069.8 <- both loops in one line
[1] 6069.8
print(total) # -> [1] 6069.8
[1] 6069.8
Same number. Reach for the vectorised line when it exists, and for a loop when each pass depends on the one before it or when there is no vectorised form.
Your turn
You hold 3 shares at 50.00, 10 shares at 55.00, and 25 shares at 44.00. Fill a values vector in a loop, total it, and check the total against sum(prices * shares).
TipShow answer
prices <-c(50.00, 55.00, 44.00) # three pricesshares <-c(3, 10, 25) # the holding for eachvalues <-numeric(length(prices)) # three zeros to fillfor (i inseq_along(prices)) { # i = 1, 2, 3 values[i] <- prices[i] * shares[i] # write into slot i}print(values) # -> [1] 150 550 1100print(sum(values)) # -> [1] 1800print(sum(prices * shares)) # -> [1] 1800