In the below reproducible code, the custom balTransit() function correctly populates a values transition table using a for-loop, while the custom balTransit_1() function is supposed to do the same using lapply() but it doesn't work. What am I doing wrong in my implementation of lapply()? Run the code and you'll see results of:
balTransit (correct results):
> test
X1 X0 X2
X1 0 0 3
X0 0 50 0
X2 5 0 0
balTransit_1 (incorrect, all 0's):
> test_1
X1 X0 X2
X1 0 0 0
X0 0 0 0
X2 0 0 0
Enhanced explanation:
My main objective here is to learn how to use the apply() family of functions, for their perceived benefits. I’ve been going through simple tutorials. A secondary objective is the generation of a transition matrix from a base data frame. Once I figure this out with lapply() (or another apply() function that is most suitable), I’m going to run the various options (for-loop(), data.table(), lapply(), etc.) against the actual data set of 2.5m rows for speed testing.
What I’m doing is creating a transition matrix (technically here a data frame) showing the flow of values (balances) from one “Flags” category to another “Flags” category, over the periods specified by the user. So, in my “for-loop” reproducible example which works correctly, the user has specified a “From” period of 1 and a “To” period of 3. The transition matrix is then generated as shown in the image now posted at the bottom.
A related post yesterday, How to convert a for-loop to lapply function for parallel testing purposes?, addresses this issue for transition counts. This post addresses transition values.
Reproducible code:
# Set up data frame:
data <-
data.frame(
ID = c(1,1,1,2,2,2,3,3,3),
Period = c(1, 2, 3, 1, 2, 3, 1, 2, 3),
Values = c(5, 10, 15, 50, 2, 4, 3, 6, 9),
Flags = c("X1","X0","X2","X0","X2","X0", "X2","X1","X1")
)
# Function to set-up base transition table:
transMat <- function(data){
DF <- data.frame(matrix(0, ncol=length(unique(data$Flags)), nrow=length(unique(data$Flags))))
row.names(DF) <- unique(data$Flags)
names(DF) <- unique(data$Flags)
return(DF)
}
# Function to populate cells of transition table, using for-loop:
balTransit <- function(data, from=1, to=3){
DF <- transMat(data)
for (i in unique(data$ID)){
id_from <- as.character(data$Flags[(data$ID == i & data$Period == from)])
id_to <- as.character(data$Flags[data$ID == i & data$Period == to])
column <- which(names(DF) == id_from)
row <- which(row.names(DF) == id_to)
val <- (data$Values[(data$ID == i & data$Period == from)])
DF[row, column] <- val + DF[row,column]
}
return(DF)
}
# Function to populate cells of transition table, using lapply:
balTransit_1 <- function(data, from=1, to=3){
DF_1 <- transMat(data)
lapply(seq_along(unique(data$ID)), function(i){
id_from <- as.character(data$Flags[(data$ID == i & data$Period == from)])
id_to <- as.character(data$Flags[data$ID == i & data$Period == to])
column <- which(names(DF_1) == id_from)
row <- which(row.names(DF_1) == id_to)
val <- (data$Values[(data$ID == i & data$Period == from)])
DF_1[row, column] <- DF_1[row, column] + val
})
return(DF_1)
}
# Run the 2 functions:
test <- balTransit(data,1,3)
test
test_1 <- balTransit_1(data,1,3)
test_1
