Related
TLDR:
I need a simple way to transform c(a = 1, a = 3, a = 6) into list(c(a = 1), c(a = 3), c(a = 6)).
Longer version:
I am using the function purrr::accumulate(), where the output of each element is an atomic vector of length greater or equal to one. When the length is one, purrr::accumulate() simplifies the whole output to an atomic vector, instead of a list.
Is there a simple way to undo or avoid this? Unfortunately, as.list() does not give me what I want.
Simple example to illustrate:
purrr::accumulate(2:3, `+`, .init = c(a=1, b=2))
gives me
list(c(a = 1, b = 2), c(a = 3, b = 4), c(a = 6, b = 7))
as expected. However,
purrr::accumulate(2:3, `+`, .init = c(a=1))
gives me
c(a = 1, a = 3, a = 6)
when I instead want
list(c(a = 1), c(a = 3), c(a = 6))
You could try
c(a = 1, a = 3, a = 6) %>% map(~setNames(.x, nm = "a"))
$a
a
1
$a
a
3
$a
a
6
or you can also remove the list names with set_names()
c(a = 1, a = 3, a = 6) %>% map(~setNames(.x, nm = "a")) %>%
set_names("")
[[1]]
a
1
[[2]]
a
3
[[3]]
a
6
I have a list of list like ll:
ll <- list(a = list(data.frame(c = 1, d = 2), data.frame(h = 3, j = 4)), b = list(data.frame(c = 5, d = 6), data.frame(h = 7, j = 9)))
I want to unnest/unlist the last level of the structure (the interior list). Note that every list contains the same structure. I want to obtain lj:
lj <- list(a = (data.frame(c = 1, d = 2, h = 3, j = 4)), b = data.frame(c = 5, d = 6, h = 7, j = 9))
I have tried the following code without any success:
lj_not_success <- unlist(ll, recursive = F)
However, this code unlists the FIRST level, not the LAST one.
Any clue?
We may need to cbind the inner list elements instead of unlisting as the expected output is a also a list of data.frames
ll_new <- lapply(ll, function(x) do.call(cbind, x))
-checking
> identical(lj, ll_new)
[1] TRUE
I have a tuple:
my_tup=(a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9)
I would like to modify a value in this tuple. Because tuples are immutable, the obvious route doesn't work:
my_tup[:a]=50 #Raises an error, as expected
So to perform the modification, I'd like to copy the tuple and its elements while changing the target element.
My current solution is as follows:
my_tup=(a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9)
args = keys(my_tup)
NamedTuple{args}(i!=:a ? getfield(my_tup, i) : 50 for i in args)
But this seems verbose or as though there should already be a function for it in the standard library.
Is there a better way?
You can merge two named tuples:
julia> xs = (a = 1, b = 2, c = 3)
(a = 1, b = 2, c = 3)
julia> ys = merge(xs, (; a = 50))
(a = 50, b = 2, c = 3)
David Varela has the correct answer, "use merge". It is an important technique.
To make this solution more easily available to others, here are some examples.
on creating plain Tuples (unnamed Tuples) and NamedTuples
Both Tuples and NamedTuples are constructed using parentheses:
julia> a_tuple = (1, 2)
(1, 2)
julia> a_namedtuple = (a = 1, b = 2)
(a = 1, b = 2)
If we try construct a Tuple or a NamedTuple with exactly one entry that same way, it does not work; instead the values are assigned named variables directly.
julia> not_a_tuple = ("xyz");
julia> not_a_tuple, typeof(not_a_tuple)
("xyz", String)
julia> not_a_namedtuple = (abc = "xyz");
julia> not_a_namedtuple, typeof(not_a_namedtuple)
("xyz", String)
To construct Tuples and NamedTuples that have a single value, we let Julia know that we are working with [Named]Tuples. An easy way to do this, and one that works for both kinds of Tuples, is to add a comma ',' before the closing parenthesis. For visual emphasis, I separate the commas from the values; it is not necessary to do that in your source code.
julia> a_tuple = (0.5 ,)
(0.5,)
julia> a_namedtuple = (onehalf = 0.5 ,)
(onehalf = 0.5,)
on substituting values in NamedTuples
julia> namedtuple = (a = 1, b = 2, c = 3); # the initial NamedTuple
julia> changes_to_make = (b = 0 ,); # the modifications intended
julia> changed_namedtuple = merge(namedtuple, changes_to_make)
(a = 1, b = 0, c = 3)
julia> namedtuple = (a = 1, b = 2, c = 3); # the initial NamedTuple
julia> changes_to_make = (b = 0, c = 7); # the modifications intended
julia> changed_namedtuple = merge(namedtuple, changes_to_make)
(a = 1, b = 0, c = 7)
julia> my_tup=(a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9)
(a = 1, b = 2, c = 3,d = 4, e = 5, f = 6, g = 7, h = 8, i = 9)
julia> new_one = (my_tup..., a=50)
(a = 50, b = 2, c = 3, d = 4, e = 5, f = 6, g = 7, h = 8, i = 9)
Here is the list I have
example <- list(list(vals = list(1, 2, 3), param = list(4,5,6), p.val = 0.5),
list(vals = list(1, 2, 3), param = list(4,5,6), p.val = 0.2),
list(vals = list(1, 2, 3), param = list(4,5,6), p.val = 1.2),
list(vals = list(1, 2, 3), param = list(4,5,6), p.val = 0.9))
How can I sort this list in ascending order by their p-val?
My main issue is that the only solution I seem to see as possible is a for loop running through this entire list, which in reality is 100k elements.
EDIT: I changed my example list to be more realistic to my actual data.
Since your "first level" list values are actually lists as well you need to first extract them and then run order on the results. For the second version you just need to subsitute 'p.val' as teh extraction index
dput(example[ order( sapply(example, "[[", 'p.val')) ] )
list(list(vals = list(1, 2, 3), param = list(4, 5, 6), p.val = 0.2),
list(vals = list(1, 2, 3), param = list(4, 5, 6), p.val = 0.5),
list(vals = list(1, 2, 3), param = list(4, 5, 6), p.val = 0.9),
list(vals = list(1, 2, 3), param = list(4, 5, 6), p.val = 1.2))
That won't change the value of example, so to make it "stick", you would need to assign the result to a name, possibly the same name, example.
I have the following list that I wish to unpack (aka expand) using only base R.
For example, I want to turn this:
b <- list(a = c(1, 2), b = 1, d = c(5, 7))
into the equivalent of:
list(a = 1, a = 2, b = 1, d = 5, d = 7)
I have this function that works if only one named element has length > 1 but not if there are multiple elements:
expand_list <- function(listx){
long_elements <- as.numeric(which(lapply(listx, length) > 1))
short_elements <- as.numeric(which(lapply(listx, length) == 1))
res <- lapply(long_elements, function(x){
as.list(setNames(listx[[x]], rep(names(listx)[x], length(listx[[x]]))))
})
expanded_elements <- res[[1]]
c(listx[short_elements], expanded_elements)
}
expand_list(b)
You can use stack followed by setNames to achieve that
y <- list(a = c(1, 2), b = 1, c = 2, d = c(5, 7))
x <- stack(y)
as.list(setNames(x$values, x$ind))