Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assigning values within lapply

Tags:

merge

r

lapply

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]]})
like image 844
Ben Hunter Avatar asked Jan 10 '13 23:01

Ben Hunter


2 Answers

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
like image 165
Matthew Lundberg Avatar answered Nov 04 '22 20:11

Matthew Lundberg


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
like image 23
mnel Avatar answered Nov 04 '22 21:11

mnel