How to make a function skip an argument from a vector - r

Say I have the following function
power<-function (number){
number^2
}
I also have a vector -z
z<- c(1:3, "a", 7:9)
I would like to apply the power function over the vector variables. If everything is a number, the functions works well using this code, which creates a list as I want:
q<-lapply(z, FUN=power)
How do I make the function skip, if it does not find a valid argument? In this case skip "a". Let's say removing the odd argument is not an option for my task. I might also have cases when the function does not find the argument at all (e.g. empty space, missing tag on a web page). Would be nice if the solution could work for these instances as well. Thanks.

Consider creating a list instead of a vector as list can have multiple types whereas vector can have only a single class and if there is a single character element, it returns the whole object as character
z <- list(1:3, "a", 7:9)
lapply(Filter(is.numeric, z), FUN = power)
Or with map_if
library(purrr)
map_if(z, .p = is.numeric, .f = power)
-output
[[1]]
[1] 1 4 9
[[2]]
[1] "a"
[[3]]
[1] 49 64 81

This will try to coerce the elements of the supplied vector to numeric. Values not coercible will have NA returned. Note that your input vector z may not be what you intended, i.e. it resolves to a character vector c("1", "2", "3", "a", ...) and not c(1, 2, 3, "a", 7, 8, 9).
power<-function (number){
result<- as.numeric(number)^2
}
z <- c(1:3, "a", 7:9)
lapply(z, power)
[[1]]
[1] 1
[[2]]
[1] 4
[[3]]
[1] 9
[[4]]
[1] NA
[[5]]
[1] 49
[[6]]
[1] 64
[[7]]
[1] 81

We can also write a custom function that wraps power inside lapply. Basically an equivalent of map_if:
z <- list(1:3, "a", 7:9)
lapply(z, function(x) {
if(all(is.numeric(x))) {
power(x)
} else {
x
}
})
[[1]]
[1] 1 4 9
[[2]]
[1] "a"
[[3]]
[1] 49 64 81

Try the code below
power <- Vectorize(function(number) {
ifelse(is.numeric(number), number^2, number)
})
z <- list(1:3, "a", 7:9)
lapply(z, power)
which gives
> lapply(z, power)
[[1]]
[1] 1 4 9
[[2]]
a
"a"
[[3]]
[1] 49 64 81

Related

mapply on pairs of elements in a lists in R

If I have a symmetric binary operator that I want to apply over the pairs of elements from a list, is there an easy way I can do this in R? I tried:
A <- list(1,2,3)
mapply(function(x,y) x+y, A,A)
but this only gives x[n]+y[n] for all n=1..N but I want x[n]+y[m] for all m=1..n, n=1..N returned as a list. outer(..) does that for m=1..N, n=1..N which involves redundant computation so I want to discount that.
Notice I don't want solution to this simple example. I need a general solution that works for non-numeric input as well. The thing I'm trying to do is like:
mapply(function(set_1, set_2) setequal(intersect(set_1, set_2), set_3), list_of_sets, list_of_sets)
In both cases addition and intersection are symmetric. In the first example, I expect list(3,4,5) from list(1+2,1+3,2+3). For the second case me input list_of_sets is:
> list_of_sets
[[1]]
numeric(0)
[[2]]
[1] 1
[[3]]
[1] 2
[[4]]
[1] 1 2
[[5]]
[1] 3
[[6]]
[1] 1 3
[[7]]
[1] 2 3
[[8]]
[1] 1 2 3
and set_3 being c(1,2) as a simple example.
You may use outer -
values <- c(1, 2, 3)
outer(values, values, `+`)
# [,1] [,2] [,3]
#[1,] 2 3 4
#[2,] 3 4 5
#[3,] 4 5 6
outer also works for non-numeric input. If the function that you want to apply is not vectorised you can use Vectorize. Since OP did not provide an example I have created one of my own.
list_of_sets_1 <- list(c('a', 'b', 'c'), c('a'))
list_of_sets_2 <- list(c('a', 'c'), c('a', 'b'))
fun <- function(x, y) intersect(x, y)
result <- outer(list_of_sets_1, list_of_sets_2, Vectorize(fun))
result
We need combn to do pairwise computation without redundancy
combn(A, 2, FUN = function(x) x[[1]] + x[[2]], simplify = FALSE)
-output
[[1]]
[1] 3
[[2]]
[1] 4
[[3]]
[1] 5
This will also work with non-numeric elements
list_of_sets <- list(c('a', 'b', 'c'), "a", c("a", "c"))
combn(list_of_sets, 2, FUN = function(x) Reduce(intersect, x), simplify = FALSE)
-output
[[1]]
[1] "a"
[[2]]
[1] "a" "c"
[[3]]
[1] "a"
We may also do
combn(list_of_sets, 2, FUN = function(x)
setequal(intersect(x[[1]], x[[2]]), set_3), simplify = FALSE)

recursively change names in nested lists in R

I have data in a nested list structure in R and I'd like to use a lookup table to change names no matter where they are in the structure.
Example
# build up an example
x <- as.list(c("a" = NA))
x[[1]] <- vector("list", 4)
names(x[[1]]) <- c("b","c","d","e")
x$a$b <- vector("list", 2)
names(x$a$b) <- c("d","f")
x$a$c <- 3
x$a$d <- 27
x$a$e <- "d"
x$a$b$d <- "data"
x$a$b$f <- "more data"
# make a lookup table for names I want to change from; to
lkp <- data.frame(matrix(data = c("a","z","b","bee","d","dee"),
ncol = 2,
byrow = TRUE), stringsAsFactors = FALSE)
names(lkp) <- c("from","to")
Output from the above
> x
$a
$a$b
$a$b$d
[1] "data"
$a$b$f
[1] "more data"
$a$c
[1] 3
$a$d
[1] 27
$a$e
[1] "d"
> lkp
from to
1 a z
2 b bee
3 d dee
Here is what I came up with to do this for only the first level:
> for(i in 1:nrow(lkp)){
+ names(x)[names(x) == lkp$from[[i]]] <- lkp$to[[i]]
+ }
> x
$z
$z$b
$z$b$d
[1] "data"
$z$b$f
[1] "more data"
$z$c
[1] 3
$z$d
[1] 27
$z$e
[1] "d"
So that works fine but uses a loop and only gets at the first level. I've tried various versions of the *apply world but have not yet been able to get something useful.
Thanks in advance for any thoughts
EDIT:
Interestingly rapply fails miserably (or, I fail miserably in my attempt!) when trying to access and modify names. Here's an example of just trying to change all names the same
> namef <- function(x) names(x) <- "z"
> rapply(x, namef, how = "list")
$a
$a$b
$a$b$d
[1] "z"
$a$b$f
[1] "z"
$a$c
[1] "z"
$a$d
[1] "z"
$a$e
[1] "z"
I used a character vector for look-up instead of you data.frame, but it will be easy to change it if you really want a data.frame.
lkp2 <- lkp$to
names(lkp2) <- lkp$from
rename <- function(nested_list) {
found <- names(nested_list) %in% names(lkp2)
names(nested_list)[found] <- lkp2[names(nested_list)[found]]
nested_list %>% map(~{
if (is.list(.x)) {
rename(.x)
} else {
.x
}
})
}
rename(x)
# $z
# $z$bee
# $z$bee$dee
# [1] "data"
#
# $z$bee$f
# [1] "more data"
#
#
# $z$c
# [1] 3
#
# $z$dee
# [1] 27
#
# $z$e
# [1] "d"
I am not sure this is the best way to do it, but it seems to do the job, and if you're only working with small lists (like XML documents) then there is no need to worry much about performance.
You might want to name the function with a better name.
Using an external package you can also do this with rrapply in the rrapply-package (extension of base rapply):
library(rrapply) ## v1.2.1
rrapply(list(x),
classes = "list",
f = function(x) {
newnames <- lkp$to[match(names(x), lkp$from)]
names(x)[!is.na(newnames)] <- newnames[!is.na(newnames)]
return(x)
},
how = "recurse"
)[[1]]
#> $z
#> $z$bee
#> $z$bee$dee
#> [1] "data"
#>
#> $z$bee$f
#> [1] "more data"
#>
#>
#> $z$c
#> [1] 3
#>
#> $z$dee
#> [1] 27
#>
#> $z$e
#> [1] "d"
Here, the f function achieves essentially the same as OP's for-loop. how = "recurse" tells the function to continue recursion after the application of f.
Note that the input is wrapped as list(x) so that the f function also modifies the name(s) of the list itself.
Update
rrapply v1.2.5 contains a dedicated option how = "names" to replace names in a nested list, which is a bit less convoluted:
rrapply(
x,
f = function(x, .xname) {
newname <- lkp$to[match(.xname, lkp$from)]
return(ifelse(is.na(newname), .xname, newname))
},
how = "names"
)
#> $z
#> $z$bee
#> $z$bee$dee
#> [1] "data"
#>
#> $z$bee$f
#> [1] "more data"
#>
#>
#> $z$c
#> [1] 3
#>
#> $z$dee
#> [1] 27
#>
#> $z$e
#> [1] "d"

Iterating over the named and unnamed elements separately in an R list

In R, I have a list that contains both named elements and unnamed elements. I want to iterate over the whole list and apply a function that only takes the element for unnamed elements and apply a different function to both the name and the attached elements for named elements.
In pseudo code, the function would look something like if(named) f(name, list[[name]]) else g(element), where list[[name]] is the element stored with that name.
For example, if the list element is unnamed then the element is returned. If the list element is named, then I want to return the name repeated a number of times equal to the element (assume that the element is an integer).
If I had ll = list(a = 5, b = 3, 9, 18) then the function should return a a list with elements
[[1]]
[1] "aaaaa"
[[2]]
[1] "bbb"
[[3]]
[1] 9
[[4]]
[1] 18
I thought about iterating over names(ll) but this cannot access the unnamed elements. Both names[[3]] and names[[4]] are equal to "" and ll[[""]] = NULL.
The process doesn't need to be very efficient so I could use
for(i in 1:length(names(ll)))
if(names(ll)[[i]] == "")
g(ll[[i]])
else
f(names(ll)[[i]], ll[[names(ll)[[i]])
but I was looking for a neater solution.
ll = list(a = 5, b = 3, 9, 18)
lapply(seq_along(ll), function(x){
if(names(ll[x]) == ""){
ll[[x]]
}else{
paste0(rep(names(ll[x]), times =ll[[x]]), collapse = '')
}
}
)
[[1]]
[1] "aaaaa"
[[2]]
[1] "bbb"
[[3]]
[1] 9
[[4]]
[1] 18
You could make a little named function
named<-function(x) names(x)!=''
and then use it on your list:
ll <- list(a = 5, b = 3, 9, 18)
ll[named(ll)]
# $a
# [1] 5
#
# $b
# [1] 3
Or the other way:
ll[!named(ll)]
# [[1]]
# [1] 9
#
# [[2]]
# [1] 18
But this will fail if you have no names on the list at all, so you could modify named to handle that case:
named<-function(x) if (is.null(names(x))) rep(FALSE,length(x)) else names(x)!=''

How to automate making a list of lists in R

I can make this list by hand:
list( list(n=1) , list(n=2), list(n=3) )
But how do I automate this, for instance if I want n to go up to 10? I tried as.list(1:10), which firstly is a different type of data structure, and secondly I couldn't work out how to specify n.
I'm hoping the answer can be expanded to multiple element lists, e.g. all combinations of 1:3 and c('A','B'):
list( list(n=1,z='A') , list(n=2,z='A'), list(n=3,z='A'),
list(n=1,z='B') , list(n=2,z='B'), list(n=3,z='B') )
Background: I'll be using it along the lines of: lapply( outer_list, function(params) do.call(FUN,params) )
UPDATE:
It was difficult to choose which answer to give the tick to. I went with the expand.grid approach as it can scale to more than two parameters more easily; the use of mapply as shown in the comment makes the two examples above look reasonably compact and readable:
outer_list=with( expand.grid(n=1:10,stringsAsFactors=F),
mapply(list, n=n, SIMPLIFY=F)
)
outer_list=with( expand.grid(n=1:3,z=c('A','Z'), stringsAsFactors=F),
mapply(list, n=n, z=z, SIMPLIFY=F)
)
They violate the DRY principle, by repeating the parameter names in the mapply() call, which bothers me a little. So, when it bothers me enough I will use the alply call as shown in Sebastian's answer.
You don't need to expand using expand.grid.
L <- mapply(function(x, y) list("n"=x,"z"=y),
rep(1:10, each=10), LETTERS[1:10],
SIMPLIFY=FALSE)
EDIT (see comment below)
L <- mapply(function(x, y) list("n"=x,"z"=y),
rep(1:10, each=length(LETTERS[1:10])), LETTERS[1:10],
SIMPLIFY=FALSE)
vals <- expand.grid(n=1:3, z=c("A", "B"),
KEEP.OUT.ATTRS=FALSE, stringsAsFactors=FALSE)
library(plyr)
alply(vals, 1, as.list)
$`1`
$`1`$n
[1] 1
$`1`$z
[1] "A"
$`2`
$`2`$n
[1] 2
$`2`$z
[1] "A"
$`3`
$`3`$n
[1] 3
$`3`$z
[1] "A"
$`4`
$`4`$n
[1] 1
$`4`$z
[1] "B"
$`5`
$`5`$n
[1] 2
$`5`$z
[1] "B"
$`6`
$`6`$n
[1] 3
$`6`$z
[1] "B"
attr(,"split_type")
[1] "array"
attr(,"split_labels")
n z
1 1 A
2 2 A
3 3 A
4 1 B
5 2 B
6 3 B

How can I remove an element from a list?

I have a list and I want to remove a single element from it. How can I do this?
I've tried looking up what I think the obvious names for this function would be in the reference manual and I haven't found anything appropriate.
If you don't want to modify the list in-place (e.g. for passing the list with an element removed to a function), you can use indexing: negative indices mean "don't include this element".
x <- list("a", "b", "c", "d", "e"); # example list
x[-2]; # without 2nd element
x[-c(2, 3)]; # without 2nd and 3rd
Also, logical index vectors are useful:
x[x != "b"]; # without elements that are "b"
This works with dataframes, too:
df <- data.frame(number = 1:5, name = letters[1:5])
df[df$name != "b", ]; # rows without "b"
df[df$number %% 2 == 1, ] # rows with odd numbers only
I don't know R at all, but a bit of creative googling led me here: http://tolstoy.newcastle.edu.au/R/help/05/04/1919.html
The key quote from there:
I do not find explicit documentation for R on how to remove elements from lists, but trial and error tells me
myList[[5]] <- NULL
will remove the 5th element and then "close up" the hole caused by deletion of that element. That suffles the index values, So I have to be careful in dropping elements. I must work from the back of the list to the front.
A response to that post later in the thread states:
For deleting an element of a list, see R FAQ 7.1
And the relevant section of the R FAQ says:
... Do not set x[i] or x[[i]] to NULL, because this will remove the corresponding component from the list.
Which seems to tell you (in a somewhat backwards way) how to remove an element.
I would like to add that if it's a named list you can simply use within.
l <- list(a = 1, b = 2)
> within(l, rm(a))
$b
[1] 2
So you can overwrite the original list
l <- within(l, rm(a))
to remove element named a from list l.
Here is how the remove the last element of a list in R:
x <- list("a", "b", "c", "d", "e")
x[length(x)] <- NULL
If x might be a vector then you would need to create a new object:
x <- c("a", "b", "c", "d", "e")
x <- x[-length(x)]
Work for lists and vectors
Removing Null elements from a list in single line :
x=x[-(which(sapply(x,is.null),arr.ind=TRUE))]
Cheers
If you have a named list and want to remove a specific element you can try:
lst <- list(a = 1:4, b = 4:8, c = 8:10)
if("b" %in% names(lst)) lst <- lst[ - which(names(lst) == "b")]
This will make a list lst with elements a, b, c. The second line removes element b after it checks that it exists (to avoid the problem #hjv mentioned).
or better:
lst$b <- NULL
This way it is not a problem to try to delete a non-existent element (e.g. lst$g <- NULL)
Use - (Negative sign) along with position of element, example if 3rd element is to be removed use it as your_list[-3]
Input
my_list <- list(a = 3, b = 3, c = 4, d = "Hello", e = NA)
my_list
# $`a`
# [1] 3
# $b
# [1] 3
# $c
# [1] 4
# $d
# [1] "Hello"
# $e
# [1] NA
Remove single element from list
my_list[-3]
# $`a`
# [1] 3
# $b
# [1] 3
# $d
# [1] "Hello"
# $e
[1] NA
Remove multiple elements from list
my_list[c(-1,-3,-2)]
# $`d`
# [1] "Hello"
# $e
# [1] NA
my_list[c(-3:-5)]
# $`a`
# [1] 3
# $b
# [1] 3
my_list[-seq(1:2)]
# $`c`
# [1] 4
# $d
# [1] "Hello"
# $e
# [1] NA
There's the rlist package (http://cran.r-project.org/web/packages/rlist/index.html) to deal with various kinds of list operations.
Example (http://cran.r-project.org/web/packages/rlist/vignettes/Filtering.html):
library(rlist)
devs <-
list(
p1=list(name="Ken",age=24,
interest=c("reading","music","movies"),
lang=list(r=2,csharp=4,python=3)),
p2=list(name="James",age=25,
interest=c("sports","music"),
lang=list(r=3,java=2,cpp=5)),
p3=list(name="Penny",age=24,
interest=c("movies","reading"),
lang=list(r=1,cpp=4,python=2)))
list.remove(devs, c("p1","p2"))
Results in:
# $p3
# $p3$name
# [1] "Penny"
#
# $p3$age
# [1] 24
#
# $p3$interest
# [1] "movies" "reading"
#
# $p3$lang
# $p3$lang$r
# [1] 1
#
# $p3$lang$cpp
# [1] 4
#
# $p3$lang$python
# [1] 2
Don't know if you still need an answer to this but I found from my limited (3 weeks worth of self-teaching R) experience with R that, using the NULL assignment is actually wrong or sub-optimal especially if you're dynamically updating a list in something like a for-loop.
To be more precise, using
myList[[5]] <- NULL
will throw the error
myList[[5]] <- NULL : replacement has length zero
or
more elements supplied than there are to replace
What I found to work more consistently is
myList <- myList[[-5]]
Just wanted to quickly add (because I didn't see it in any of the answers) that, for a named list, you can also do l["name"] <- NULL. For example:
l <- list(a = 1, b = 2, cc = 3)
l['b'] <- NULL
In the case of named lists I find those helper functions useful
member <- function(list,names){
## return the elements of the list with the input names
member..names <- names(list)
index <- which(member..names %in% names)
list[index]
}
exclude <- function(list,names){
## return the elements of the list not belonging to names
member..names <- names(list)
index <- which(!(member..names %in% names))
list[index]
}
aa <- structure(list(a = 1:10, b = 4:5, fruits = c("apple", "orange"
)), .Names = c("a", "b", "fruits"))
> aa
## $a
## [1] 1 2 3 4 5 6 7 8 9 10
## $b
## [1] 4 5
## $fruits
## [1] "apple" "orange"
> member(aa,"fruits")
## $fruits
## [1] "apple" "orange"
> exclude(aa,"fruits")
## $a
## [1] 1 2 3 4 5 6 7 8 9 10
## $b
## [1] 4 5
Using lapply and grep:
lst <- list(a = 1:4, b = 4:8, c = 8:10)
# say you want to remove a and c
toremove<-c("a","c")
lstnew<-lst[-unlist(lapply(toremove, function(x) grep(x, names(lst)) ) ) ]
#or
pattern<-"a|c"
lstnew<-lst[-grep(pattern, names(lst))]
You can also negatively index from a list using the extract function of the magrittr package to remove a list item.
a <- seq(1,5)
b <- seq(2,6)
c <- seq(3,7)
l <- list(a,b,c)
library(magrittr)
extract(l,-1) #simple one-function method
[[1]]
[1] 2 3 4 5 6
[[2]]
[1] 3 4 5 6 7
There are a few options in the purrr package that haven't been mentioned:
pluck and assign_in work well with nested values and you can access it using a combination of names and/or indices:
library(purrr)
l <- list("a" = 1:2, "b" = 3:4, "d" = list("e" = 5:6, "f" = 7:8))
# select values (by name and/or index)
all.equal(pluck(l, "d", "e"), pluck(l, 3, "e"), pluck(l, 3, 1))
[1] TRUE
# or if element location stored in a vector use !!!
pluck(l, !!! as.list(c("d", "e")))
[1] 5 6
# remove values (modifies in place)
pluck(l, "d", "e") <- NULL
# assign_in to remove values with name and/or index (does not modify in place)
assign_in(l, list("d", 1), NULL)
$a
[1] 1 2
$b
[1] 3 4
$d
$d$f
[1] 7 8
Or you can remove values using modify_list by assigning zap() or NULL:
all.equal(list_modify(l, a = zap()), list_modify(l, a = NULL))
[1] TRUE
You can remove or keep elements using a predicate function with discard and keep:
# remove numeric elements
discard(l, is.numeric)
$d
$d$e
[1] 5 6
$d$f
[1] 7 8
# keep numeric elements
keep(l, is.numeric)
$a
[1] 1 2
$b
[1] 3 4
Here is a simple solution that can be done using base R. It removes the number 5 from the original list of numbers. You can use the same method to remove whatever element you want from a list.
#the original list
original_list = c(1:10)
#the list element to remove
remove = 5
#the new list (which will not contain whatever the `remove` variable equals)
new_list = c()
#go through all the elements in the list and add them to the new list if they don't equal the `remove` variable
counter = 1
for (n in original_list){
if (n != ){
new_list[[counter]] = n
counter = counter + 1
}
}
The new_list variable no longer contains 5.
new_list
# [1] 1 2 3 4 6 7 8 9 10
How about this? Again, using indices
> m <- c(1:5)
> m
[1] 1 2 3 4 5
> m[1:length(m)-1]
[1] 1 2 3 4
or
> m[-(length(m))]
[1] 1 2 3 4
You can use which.
x<-c(1:5)
x
#[1] 1 2 3 4 5
x<-x[-which(x==4)]
x
#[1] 1 2 3 5
if you'd like to avoid numeric indices, you can use
a <- setdiff(names(a),c("name1", ..., "namen"))
to delete names namea...namen from a. this works for lists
> l <- list(a=1,b=2)
> l[setdiff(names(l),"a")]
$b
[1] 2
as well as for vectors
> v <- c(a=1,b=2)
> v[setdiff(names(v),"a")]
b
2

Resources