I want to substract one from a particular element of a nested list.
How should I do that?
This is my nested list
I want to one from positive values of bar vector (check first is it is positive)
x <- list(a = list(foo = 1:8, bar = 3:4), b = list(baz = 5:6))
I was thinking about map function.
Thank you!
You could write a recursive function:
mod_at <- function(x, name, FUN){
if(name %in% names(x)){
x[[name]] <- FUN(x[[name]])
x
}
else if(is.list(x))lapply(x, mod_at, name, FUN)
else x
}
Now use that to modify the list:
mod_at(x, "bar", function(x)ifelse(x>0, x-1, x))
$a
$a$foo
[1] 1 2 3 4 5 6 7 8
$a$bar
[1] 2 3
$b
$b$baz
[1] 5 6
You could use the function the way you want:
mod_at(x, "baz", sqrt)
$a
$a$foo
[1] 1 2 3 4 5 6 7 8
$a$bar
[1] 3 4
$b
$b$baz
[1] 2.236068 2.449490
Related
I wonder if there is an effective (without loops) method of joining two lists by names of their elements.
I checked here but this did not help me as well as tried to access names from sapply() as on the answer from here.
Say I have the following lists:
mylist1 <- list(region1 = 1:3, region2 = 5:7)
> mylist1
$region1
[1] 1 2 3
$region2
[1] 5 6 7
and
mylist2 <- list(region1 = "#E8C506", region2 = "#F3B508")
$region1
[1] "#E8C506"
$region2
[1] "#F3B508"
How can I get joined list by element names:
mylist3 <- list(region1 = list(1:3, "#E8C506"), region2 = list(5:7, "#F3B508"))
> mylist3
$region1
$region1[[1]]
[1] 1 2 3
$region1[[2]]
[1] "#E8C506"
$region2
$region2[[1]]
[1] 5 6 7
$region2[[2]]
[1] "#F3B508"
Relying heavily on the answers from #Ajar and #Flodel to Combine/merge lists by elements names, this maps into list's instead of a vectors (c) to achieve your end:
Using base:
keys <- unique(c(names(mylist1), names(mylist2)))
setNames(mapply(list, mylist1[keys], mylist2[keys], SIMPLIFY = FALSE), keys)
Using purrr:
library(purrr)
cat_lists <- function(list1, list2) {
keys <- unique(c(names(list1), names(list2)))
map2(list1[keys], list2[keys], list) |>
set_names(keys)
}
reduce(list(mylist1, mylist2), cat_lists)
Output:
$region1
$region1[[1]]
[1] 1 2 3
$region1[[2]]
[1] "#E8C506"
$region2
$region2[[1]]
[1] 5 6 7
$region2[[2]]
[1] "#F3B508"
If the elements are identical in both lists you can use purrr::transpose():
library(purrr)
transpose(list(mylist1, mylist2))
$region1
$region1[[1]]
[1] 1 2 3
$region1[[2]]
[1] "#E8C506"
$region2
$region2[[1]]
[1] 5 6 7
$region2[[2]]
[1] "#F3B508"
Note tranpose() uses the first list as a template and matches subsequent list elements by name, so if the elements are different across lists, you need to ensure all names are first present in the first list, e.g.:
mylist3 <- list(region1 = 1:3, region2 = 5:7, region3 = 5:7)
mylist4 <- list(region1 = "#E8C506", region2 = "#F3B508", region4 = "#F00008")
l <- list(mylist3, mylist4)
l_names <- unique(unlist(lapply(l, names)))
mylist3[l_names] <- mylist3[l_names]
transpose(list(mylist3, mylist4))
$region1
$region1[[1]]
[1] 1 2 3
$region1[[2]]
[1] "#E8C506"
$region2
$region2[[1]]
[1] 5 6 7
$region2[[2]]
[1] "#F3B508"
$region3
$region3[[1]]
[1] 5 6 7
$region3[[2]]
NULL
$region4
$region4[[1]]
NULL
$region4[[2]]
[1] "#F00008"
I hope this works.
list12 <- mapply(list, mylist1 , mylist2 , SIMPLIFY=FALSE)
Im trying to create a function that returns characteristic symbol to a defined value like this
"a" to 1
"b" to 2
"c" to 3
And where there is only one input argument (one of "a", "b" or "c") in the function. Like this: function(x), for example function("a") returns 1.
We can convert with matching to the default Constant vector letters
f1 <- function(arg1){
match(arg1, letters)
}
f1('a')
#[1] 1
f1('b')
#[1] 2
f1(c('a', 'b', 'c'))
#[1] 1 2 3
letterToNumber <- function(x){
which(x == letters)}
sapply(letters[1:10], letterToNumber)
a b c d e f g h i j
1 2 3 4 5 6 7 8 9 10
You can create a dictionary like structure by using a named vector.
f <- function(x)
{
dict <- setNames(seq_along(letters),letters)
unname(dict[x])
}
f("a")
[1] 1
f("g")
[1] 7
f(c("a","z"))
[1] 1 26
This will be faster than other solutions but won't fail if you don't feed a lower case letter :
foo <- function(x) utf8ToInt(x) - 96L
foo("m")
#> [1] 13
I'm trying to find the unique elements in the variables listed as x.
The only constraint is that I want to first find the variable (here either a, b, or c) in the list whose max element is smallest, and keep that variable untouched at the top of the output?
I have tried something but can't implement the constraint above:
P.S. My goal is to achieve a function/looping structure to handle larger lists.
x = list(a = 1:5, b = 3:7, c = 6:9) ## a list of 3 variables; variable `a` has the smallest
## max among all variables in the list, so keep `a`
## untouched at the top of the output.
x[-1] <- Map(setdiff, x[-1], x[-length(x)]) ## Now, take the values of `b` not shared
## with `a`, AND values of `c` not shared
## with `b`.
x
# Output: # This output is OK now, but if we change order of `a`, `b`,
# and `c` in the initial list the output will change.
# This is why the constraint above is necessary?
$a
[1] 1 2 3 4 5
$b
[1] 6 7
$c
[1] 8 9
#Find which element in the list has smallest max.
smallest_max <- which.min(sapply(x, max))
#Rearrange the list by keeping the smallest max in first place
#followed by remaining ones
new_x <- c(x[smallest_max], x[-smallest_max])
#Apply the Map function
new_x[-1] <- Map(setdiff, new_x[-1], new_x[-length(new_x)])
new_x
#$a
#[1] 1 2 3 4 5
#$b
#[1] 6 7
#$c
#[1] 8 9
We can wrap this up in a function and then use it
keep_smallest_max <- function(x) {
smallest_max <- which.min(sapply(x, max))
new_x <- c(x[smallest_max], x[-smallest_max])
new_x[-1] <- Map(setdiff, new_x[-1], new_x[-length(new_x)])
new_x
}
keep_smallest_max(x)
#$a
#[1] 1 2 3 4 5
#$b
#[1] 6 7
#$c
#[1] 8 9
I want to remove certain vectors from a list. I have for example this:
a<-c(1,2,5)
b<-c(1,1,1)
c<-c(1,2,3,4)
d<-c(1,2,3,4,5)
exampleList<-list(a,b,c,d)
exampleList returns of course:
[[1]]
[1] 1 2 5
[[2]]
[1] 1 1 1
[[3]]
[1] 1 2 3 4
[[4]]
[1] 1 2 3 4 5
Is there a way to remove certain vectors from a list in R. I want to remove all vectors in the list exampleList which contain both 1 and 5(so not only vectors which contain 1 or 5, but both). Thanks in advance!
Use Filter:
filteredList <- Filter(function(v) !(1 %in% v & 5 %in% v), exampleList)
print(filteredList)
#> [[1]]
#> [1] 1 1 1
#>
#> [[2]]
#> [1] 1 2 3 4
Filter uses a functional style. The first argument you pass is a function that returns TRUE for an element you want to keep in the list, and FALSE for an element you want to remove from the list. The second argument is just the list itself.
We can use sapply on every list element and remove those elements where both the values 1 and 5 are present.
exampleList[!sapply(exampleList, function(x) any(x == 1) & any(x == 5))]
#[[1]]
#[1] 1 1 1
#[[2]]
#[1] 1 2 3 4
Here a solution with two steps:
exampleList<-list(a=c(1,2,5), b=c(1,1,1), c=c(1,2,3,4), d=c(1,2,3,4,5))
L <- lapply(exampleList, function(x) if (!all(c(1,5) %in% x)) x)
L[!sapply(L, is.null)]
# $b
# [1] 1 1 1
#
# $c
# [1] 1 2 3 4
Here is a one-step variant without any definition of a new function
exampleList[!apply(sapply(exampleList, '%in%', x=c(1,5)), 2, all)]
(... but it has two calls to apply-functions)
I want to remove part of the list where it is a complete set of the other part of the list. For example, B intersect A and E intersect C, therefore B and E should be removed.
MyList <- list(A=c(1,2,3,4,5), B=c(3,4,5), C=c(6,7,8,9), E=c(7,8))
MyList
$A
[1] 1 2 3 4 5
$B
[1] 3 4 5
$C
[1] 6 7 8 9
$E
[1] 7 8
MyListUnique <- RemoveSubElements(MyList)
MyListUnique
$A
[1] 1 2 3 4 5
$C
[1] 6 7 8 9
Any ideas ? Any know function to do it ?
As long as your data is not too huge, you can use an approach like the following:
# preparation
MyList <- MyList[order(lengths(MyList))]
idx <- vector("list", length(MyList))
# loop through list and compare with other (longer) list elements
for(i in seq_along(MyList)) {
idx[[i]] <- any(sapply(MyList[-seq_len(i)], function(x) all(MyList[[i]] %in% x)))
}
# subset the list
MyList[!unlist(idx)]
#$C
#[1] 6 7 8 9
#
#$A
#[1] 1 2 3 4 5
Similar to the other answer, but hopefully clearer, using a helper function and 2 sapplys.
#helper function to determine a proper subset - shortcuts to avoid setdiff calculation if they are equal
is.proper.subset <- function(x,y) !setequal(x,y) && length(setdiff(x,y))==0
#double loop over the list to find elements which are proper subsets of other elements
idx <- sapply(MyList, function(x) any(sapply(MyList, function(y) is.proper.subset(x,y))))
#filter out those that are proper subsets
MyList[!idx]
$A
[1] 1 2 3 4 5
$C
[1] 6 7 8 9