Carlos
Carlos

Reputation: 49

lapply to extract rows from list

I have the following question: I have a list (L1) with two parts and each 4 identical variables. The variable 4 is also the name of the part of the list. e.g. $a = a

a <- data.frame(V1=c("a","b","c"), V2=c(4,7,9), V3=1:3, V4=c("a","a","a"))
b <- data.frame(V1=c("d","e","f"), V2=c(10,14,16), V3=1:3, V4=c("b","b","b"))
L1 <- list(a=a, b=b)
L1

$a
V1    V2    V3   V4
a     4     1    a
b     7     2    a
c     9     3    a
$b
V1    V2    V3   V4
d     10     1    b
e     14     2    b
f     16     3    b

I would like to extract the rows of each part of the list with V3==2. If there is no row in the list with this value V1 to V3 should be extracted with NA and V4 should contain the name of the part of the list.

In the example the outcome should look like this:

V1   V2    V3   V4
b    7     2    a
e    14    2    b

If I select a value e.g. V3==4 then my result should look like this:

V1   V2    V3   V4
<NA>  <NA>  <NA>    a
<NA>  <NA>  <NA>    b

I can extract a column with unlist(lapply(L1, "[",3)) but I can't figure out how to extract rows which have a certain value in a variable. I also tried to combine lapply with the subset function, but this didn't work for me. Thank's for your help!

Upvotes: 0

Views: 2764

Answers (3)

bramtayl
bramtayl

Reputation: 4024

You can also bind_rows with dplyr

list(a = a, b = b) %>%
  bind_rows(.id = "source") %>%
  filter(V2 == 2)

Upvotes: 2

akrun
akrun

Reputation: 887118

We could create a function using data.table. We rbind the list elements with rbindlist, grouped by 'V4', if the 'V3' is not equal to the given value, we return the NA elements (.SD[.N+1]) or else return the Subset of Data.table (.SD[tmp]).

library(data.table)
f1 <- function(lst, val){
        rbindlist(lst)[, {tmp <- V3==val
                   if(!any(tmp)) .SD[.N+1]
                   else .SD[tmp]},
                           by = V4][, names(lst[[1]]), with=FALSE]
  }

f1(L1, 4)
#   V1 V2 V3 V4
#1: NA NA NA  a
#2: NA NA NA  b

f1(L1, 3)
#   V1 V2 V3 V4
#1:  c  9  3  a
#2:  f 16  3  b

f1(L1, 2)
#   V1 V2 V3 V4
#1:  b  7  2  a
#2:  e 14  2  b

Upvotes: 2

toldo
toldo

Reputation: 416

This should work. The first command returns a list, the second one converts it to a data frame. If the value is not in the data, it returns NA (for the list) or a row of NAs (for the df).

l <- lapply(L1, function(x) {i <- which(x$V3 == 2)
                         if (length(i) > 0) x[i, ]
                         else NA })

df <- rbind(l[[1]], l[[2]])

Upvotes: 4

Related Questions