This is a toy example of something I always end up writing a loop for. I'm having trouble figuring out the one liner. I'm sure I've seen it, but it didn't stick.
smallFrame <- data.frame(colA = c('A', 'B', 'C' ,'D'), colB = c(1,1,1,1))
someList <- list(A=20, B=30, C=40, D=50)
for(letter in names(someList)){
smallFrame[smallFrame$colA==letter, 'newColumn'] <- someList[[letter]]
}
how do I do the loop in one line? This won't do it.
lapply(names(someList), function(x) {smallFrame[smallFrame$colA==x, 'newColumn'] <- someList[[x]]})
Ugly, but works:
lapply(names(someList), function(x) {smallFrame[smallFrame$colA==x, 'newColumn'] <<- someList[[x]]})
Note the <<-
. The reason that it won't work with <-
is that a copy of someList is modified within the function.
Take "Ugly" here to mean that you should never use this syntax, for two reasons. First, functions with side effects are prone to error. Second, the return value of lapply
is ignored. Either of these suggest that an explicit loop is best.
Less ugly, and almost stolen from @thelatemail:
smallFrame$newColumn <- unlist(someList[match(smallFrame$colA, names(someList))])
Example:
smallFrame <- data.frame(colA = c('A', 'B', 'C' ,'D', 'A'), colB = c(1,1,1,1,1))
> smallFrame
> smallFrame
colA colB
1 A 1
2 B 1
3 C 1
4 D 1
5 A 1
smallFrame$newColumn <- unlist(someList[match(smallFrame$colA, names(someList))])
> smallFrame
colA colB newColumn
1 A 1 20
2 B 1 30
3 C 1 40
4 D 1 50
5 A 1 20
This is a simple merge, if you reshape your smallList
appropriately
# reshape2 for melt.
library(reshape2)
# slightly expanded version with duplicate colA == A
smallFrame <- data.frame(colA = c('A', 'A', 'B', 'C' ,'D'), colB = c(1,2,1,1,1))
someList <- list(A=20, B=30, C=40, D=50)
merge(smallFrame, melt(someList), by.x = 'colA', by.y = 'L1')
colA colB value
1 A 1 20
2 A 2 20
3 B 1 30
4 C 1 40
5 D 1 50
or, if you are really keen on assigning within smallFrame and efficiently, use data.table
library(data.table)
smallDT <- data.table(smallFrame, key = 'colA')
someDT <- data.table(melt(someList), key = 'L1')
# left join smallDT and someDT, assigning the `value` column by reference
# within smallDT as the column `newColumn`
smallDT[someDT, newColumn := value]
smallDT
colA colB newColumn
1: A 1 20
2: A 2 20
3: B 1 30
4: C 1 40
5: D 1 50
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With