Build a table from equal-length vectors, pull rows, columns and blocks out of it, add a computed column, sort it, and turn position values into weights.
A data frame is a table: several equal-length vectors laid side by side, one per column. Lesson 10 ended by saying it is a list whose elements are columns, and that is literally true. The list syntax you already know still works on it.
In this lesson I build a small book of positions as a data frame, take rows, columns and blocks out of it, then add a value column, sort by it, keep the rows I want, and finish with weights that sum to 1.
Step 1. Build one from vectors
data.frame() takes named vectors and stacks them as columns. Every vector must be the same length, because a table cannot have a ragged edge. stringsAsFactors = FALSE keeps the tickers as plain text. Without it, older R turned character columns into factors.
df <-data.frame(Ticker =c("AAA", "CCC", "DDD"), # character columnClose =c(185.40, 410.20, 128.75), # numeric columnShares =c(10, 4, 20), # numeric columnstringsAsFactors =FALSE# keep Ticker as text, not a factor)print(df)
The row names stayed 2 and 3. They label the original rows, so they no longer match the printed order once you subset.
Step 5. Add a column, sort, keep rows
A new column is an assignment to a name that does not exist yet. The arithmetic runs element by element down the two columns, as it does on any pair of vectors.
df$Value <- df$Close * df$Shares # price times shares, row by rowprint(df)
Ticker Close Shares Value
1 AAA 185.40 10 1854.0
2 CCC 410.20 4 1640.8
3 DDD 128.75 20 2575.0
order() does not sort. It returns the row positions in sorted order, and you feed those to the row index. A minus sign in front sorts from large to small.
print(order(-df$Value)) # -> [1] 3 1 2 <- biggest position is row 3
[1] 3 1 2
print(df[order(-df$Value), ]) # rows in that order, every column
Ticker Close Shares Value
3 DDD 128.75 20 2575.0
1 AAA 185.40 10 1854.0
2 CCC 410.20 4 1640.8
The weights sum to 1 because every value was divided by the sum of all of them. Those are the same three numbers Lesson 10 got from a list of lists.
A tibble from the tidyverse is a data frame that prints differently and never converts text to factors. Everything in this lesson works on one. I stay with data.frame here.
Lesson 12 reads a table off disk instead of typing it in.
Your turn
Add a fourth position to df: 30 shares of BBB at 92.50. rbind() stacks a new one-row data frame under the old rows. Rebuild Value and Weight, sort by weight from large to small, and check the weights still sum to 1.
TipShow answer
df2 <-rbind(df[, c("Ticker", "Close", "Shares")], # drop the computed columns firstdata.frame(Ticker ="BBB", Close =92.50, Shares =30))df2$Value <- df2$Close * df2$Shares # value each positiondf2$Weight <- df2$Value /sum(df2$Value) # each as a share of the bookprint(nrow(df2)) # -> [1] 4print(df2[order(-df2$Weight), ]) # heaviest position first# -> Ticker Close Shares Value Weight# -> 4 BBB 92.50 30 2775.0 0.3137437# -> 3 DDD 128.75 20 2575.0 0.2911315# -> 1 AAA 185.40 10 1854.0 0.2096147# -> 2 CCC 410.20 4 1640.8 0.1855101print(sum(df2$Weight)) # -> [1] 1print(df2[df2$Weight >0.25, c("Ticker", "Weight")]) # the two big holdings# -> Ticker Weight# -> 3 DDD 0.2911315# -> 4 BBB 0.3137437