R remove objects from a list with if else statement

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP



R remove objects from a list with if else statement



I have a list of data frames, and would like to remove those with less than 2 rows off from mylist:


a<-data.frame(x=c(1:4),y=c("m", "n", "o", "p"))
b<-data.frame(x=c(2:6),y=c("q", "w", "e", "r", "t"))
c<-data.frame(x=c(6,7),y=c("j","k"),z=c("$","#"))
d<-data.frame(x="9",y="q",z="+")
mylist<-list(a,b,c,d)

for (i in length(mylist))
if (nrow(mylist[[i]])<=2)
mylist<-mylist[-i]

else
mylist<-myslit



However it only seemed to remove data.frame d. Data frame c is still in "mylist" after running the for loop.





+1 for showing what you already tried, and providing a working example.
– Paul Hiemstra
Apr 23 '13 at 19:44




5 Answers
5



You can do this more easily using an apply loop:


row_lt2 <- which(sapply(mylist, nrow) < 2)
mylist[-row_lt2]
[[1]]
x y
1 1 m
2 2 n
3 3 o
4 4 p

[[2]]
x y
1 2 q
2 3 w
3 4 e
4 5 r
5 6 t

[[3]]
x y z
1 6 j $
2 7 k #



Notice I use negative indexing to remove items instead of selecting them.





Thank you so much. It worked out beautifully. :D
– lamushidi
Apr 23 '13 at 19:39





I really love the expressiveness of this functional solution in favor of a more imperative style using a for loop. It is much shorter, in more understandable in my opinion.
– Paul Hiemstra
Apr 23 '13 at 20:18





@PaulHiemstra I agree about the expressiveness of the functional style. This can be made even for succinct with the Filter function. (See my answer below.)
– Jason Morgan
Apr 23 '13 at 21:04


Filter



To add to the other answers: this is exactly the type of thing the higher-order Filter function is made for:


Filter


> Filter(function(x) nrow(x) >= 2, mylist)
[[1]]
x y
1 1 m
2 2 n
3 3 o
4 4 p

[[2]]
x y
1 2 q
2 3 w
3 4 e
4 5 r
5 6 t

[[3]]
x y z
1 6 j $
2 7 k #





WOW, Filter function is awesome. Thank you!!!
– lamushidi
Apr 23 '13 at 21:30






@lamushidi No problem. Take a look at the other functions available on the same help page as ?Filter. They can be quite useful.
– Jason Morgan
Apr 24 '13 at 0:53


?Filter



You can't do this procedure using for because the indices change. Using for, after removing line 2, you will examine line 3, but you need examine line 2 again (because the line 2 isn't more the same line as before). Change it to repeat or while.


for


for


repeat


while


a<-data.frame(x=c(1:4),y=c("m", "n", "o", "p"))
b<-data.frame(x=c(2:6),y=c("q", "w", "e", "r", "t"))
c<-data.frame(x=c(6,7),y=c("j","k"),z=c("$","#"))
d<-data.frame(x="9",y="q",z="+")
mylist<-list(a,b,c,d)

i <- 1
while (i <= length(mylist))
if (nrow(mylist[[i]])<=2)
mylist<-mylist[-i]

else
i <- i+1




Or just use @Paul solution... :P





I just skipped the OP's suggestion, but your are totally right that a for loop is not really useful here.
– Paul Hiemstra
Apr 23 '13 at 19:41





@PaulHiemstra sapply will always be superior, but for can be used too, see my answer
– Maxim.K
Apr 23 '13 at 19:56



for





Although you take a bit of a different approach than the OP, creating a new one excluding the matches, in stead of deleting the matches from the list.
– Paul Hiemstra
Apr 23 '13 at 20:15



Paul has provided an answer already, but your mistake has not been pointed out.



Your code has two problems. First, you need to supply a range to your loop:


for (i in 1:length(mylist))



or
for (i in seq_along(length(mylist)))



Without this, your initialization looked like for (i in 4) after evaluation, meaning that only one iteration was run, removing element 4 and not even looking at all previous elements.


for (i in 4)



However, if you fix that problem, another one emerges. Namely, your list no longer has 4 elements after removing element 3. It only has 3 elements, while your i index will go up until 4, resulting in subscript out of bounds error.


i


subscript out of bounds



Therefore one can only suggest the approach using apply, as described by @Paul.



Also, opposed to the assertion otherwise, it is possible to achieve the same using for loop, only your approach needs to be slightly different:


for


for (i in 1:length(mylist))
if (nrow(mylist[[i]])>2)

mylist2[i]<-mylist[i]


print(mylist2)



Here you select list elements that are greater than 2, and assign them to a new list. Sapply will be more speedy though.


Sapply





+1 nice solution using a for loop. If mylist is big, you could preallocate it first, saving a lot of memory and time.
– Paul Hiemstra
Apr 23 '13 at 20:16


for


mylist





@PaulHiemstra I don't see how one could specify the size of mylist2 here, except by using your approach, which renders the whole loop obsolete :)
– Maxim.K
Apr 23 '13 at 20:40





Thanks for pointing out the difference between (i in length(mylist)) and (i in 1:length(mylist)). A common mistake I often made.
– lamushidi
Apr 23 '13 at 21:35



(i in length(mylist))


(i in 1:length(mylist))



For a few special cases, other methods mentioned above didn't work, although while could work. I did find that if you use Rcoster's approach while without adding an evaluation of the returned list, it would still return the wrong answer -- since every time when the condition i<=length(mylist) got evaluated, the length of mylist can change, due to the deletion of the mylist[-i]. Therefore, the while evaluation could stop before it reaches all unwanted elements in the list.


while


while


i<=length(mylist)


mylist


mylist[-i]


while



For example, in my case, I wanted to make sure all length of mylist[[i]]<=4. The list is very large (2284879 elements to start with). When the first time I ran the while evaluation, the program stopped before taking out all mylist[[i]]>4 because the length of mylist got smaller. I had to use table(lengths(mylist)) to judge whether all unwanted lists were taken out. If not, then run the while loop again. Luckily it only took two runs. But I thought it is necessary to point it out, in case someone else came here and try to use this while approach.


mylist[[i]]<=4


while


mylist[[i]]>4


mylist


table(lengths(mylist))


while


while



PS. for loop should be usable too, if some evaluation added to judge whether re-run is needed.


for






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

Firebase Auth - with Email and Password - Check user already registered

Dynamically update html content plain JS

How to determine optimal route across keyboard