Indexing named list with vector in R - r

How would you index the second element of a vector which is stored as a value in a named list?
I start with this:
hi <- list("1" = c("a","b"),
"2" = c("dog","cat"),
"3" = c("sister","brother")
)
and would like to end up with a named list with the key plus the 2nd element of the vector i.e:
list("1" = "b",
"2" = "cat",
"3" = "brother"
)

You can do:
lapply(hi, `[`, 2)
$`1`
[1] "b"
$`2`
[1] "cat"
$`3`
[1] "brother"

We can use map
library(purrr)
map(hi, pluck, 2)
#$`1`
#[1] "b"
#$`2`
#[1] "cat"
#$`3`
#[1] "brother"

Related

Find max value in nested list in R

I have a nested list as follows:
results <- list()
c <- 1
outcomes <- c("Value1", "Value2", "Value3")
values <- c(2, 11, 20)
for(i in outcomes){
for(p in 1:length(values)){
results[[c]] <- c(outcomes[p], values[p])
c <- c + 1
}
}
results <- results[1:3]
>results
[[1]]
[1] "Value1" "2"
[[2]]
[1] "Value2" "11"
[[3]]
[1] "Value3" "20"
I want to find a way to return the pair that has the highest value as follows: "Value3" "20"
How can I do this only using base r?
Maybe this can be helpful:
#Code
results[which(unlist(lapply(results,function(x) as.numeric(x[2])))==max(unlist(lapply(results,function(x) as.numeric(x[2])))))]
Output:
[[1]]
[1] "Value3" "20"
We can extract the second element in the list, unlist, convert it to numeric find the index of the max element with which.max to extract the list
results[which.max(as.numeric(unlist(sapply(results, `[`, 2))))]
#[[1]]
#[1] "Value3" "20"
Or slightly compact with
results[which.max(as.numeric(do.call(rbind, results)[,2]))]
#[[1]]
#[1] "Value3" "20"

Creating several new vectors from an original vector with separators

I'm trying to create several vectors from an original vector.
I read some posts but couldn't find something to solve my problem.
My original vector is looking like this:
> orig_vec
[1] "A" "B" "C" "D;" "1" "2;" "a1" "a2" "a3"
I want vectors that look like this:
> vector1
[1] "A" "B" "C" "D"
> vector2
[1] "1" "2"
> vector3
[1] "a1" "a2" "a3"
So what I need is a code which recognizes the semicolons as separators and creates new vectors depending on the number of separated values in "orig_vec".
I also have the problem that the "orig_vec" can change.
When it looks like this:
> orig_vec
[1] "A" "B" "C" "D" "E;" "1" "2;" "a1" "a2" "a3;" "b1"
I need to get automatically these vectors:
> vector1
[1] "A" "B" "C" "D" "E"
> vector2
[1] "1" "2"
> vector3
[1] "a1" "a2" "a3"
> vector4
[1] "b1"
I'm sorry that I can't provide more code or any idea of a solution.
This should work:
x <- c("A", "B", "C", "D;", "1", "2;", "a1", "a2", "a3")
sapply(split(x, c(0, cumsum(grepl(";", x))[-length(x)])), function(x) gsub(";", "", x))
$`0`
[1] "A" "B" "C" "D"
$`1`
[1] "1" "2"
$`2`
[1] "a1" "a2" "a3"
We use the cumsum() of condition grepl(";", x) to create a vector for subsetting with split(), then remove the semicolons by sapply()ing gsub().
I like #LAP's as well, here's another option:
vec <- c("A", "B", "C", "D;", "1", "2;", "a1", "a2", "a3;", "b1")
ix <- grep(";", vec)
mapply(function(x, ix1, ix2) x[ix1:ix2],
x = list(sub(";", "", vec)),
ix1 = c(1, ix + 1),
ix2 = c(ix, length(vec)))
[[1]]
[1] "A" "B" "C" "D"
[[2]]
[1] "1" "2"
[[3]]
[1] "a1" "a2" "a3"
[[4]]
[1] "b1"
You'll notice most people are giving you answers that result in a list of vectors, rather than a handful of vectors assigned to variable names. It's generally much cleaner and easier to work with lists of objects rather than objects scattered around in your namespace. Just an added $.02.
Here is one way, based on the idea of first joining on a space then successively splitting, first on ; and then on a space:
s <- c("A", "B", "C", "D;", "1" , "2;" ,"a1", "a2", "a3")
s <- paste0(s,collapse = ' ')
s <- unlist(strsplit(s, ';'))
vectors <- lapply(s,function(x) unlist(strsplit(trimws(x),' ')))
> vectors
[[1]]
[1] "A" "B" "C" "D"
[[2]]
[1] "1" "2"
[[3]]
[1] "a1" "a2" "a3"
Just throwing in a tidyverse approach that works in a single pipe.
Similar to other answers, collapse the vector into a single string, then split that string on each ;. I'm using a space as the collapse so I can use str_trim easily later on.
library(tidyverse)
x %>%
paste(collapse = " ") %>%
strsplit(split = ";", fixed = T)
#> [[1]]
#> [1] "A B C D E" " 1 2" " a1 a2 a3" " b1"
Since strsplit gives you a list and, at least in this scenario, you're only interested in the first list entry, pull it out with [[ and trim the beginning and trailing spaces of those vectors. The map gives you a list of vectors of one string each.
x %>%
paste(collapse = " ") %>%
strsplit(split = ";", fixed = T) %>%
`[[`(1) %>%
map(str_trim)
#> [[1]]
#> [1] "A B C D E"
#>
#> [[2]]
#> [1] "1 2"
#>
#> [[3]]
#> [1] "a1 a2 a3"
#>
#> [[4]]
#> [1] "b1"
Then split each vector by the spaces, and flatten into one list of vectors.
All in one pipe:
x %>%
paste(collapse = " ") %>%
strsplit(split = ";", fixed = T) %>%
`[[`(1) %>%
map(str_trim) %>%
map(str_split, " ") %>%
flatten()
#> [[1]]
#> [1] "A" "B" "C" "D" "E"
#>
#> [[2]]
#> [1] "1" "2"
#>
#> [[3]]
#> [1] "a1" "a2" "a3"
#>
#> [[4]]
#> [1] "b1"
Created on 2019-02-13 by the reprex package (v0.2.1)

Create a list containing a variable number of lists

I need to create a list from rows of a dataframe in the following format:
df <- data.frame(y1 = c("a", "d"), y2 = c("b", "e"), y3 = c("c", "f"))
df$y1 <- as.character(df$y1)
df$y2 <- as.character(df$y2)
df$y3 <- as.character(df$y3)
x <- list(
list(y1 = df$y1[1],
y2 = df$y2[1],
y3 = df$y3[1]),
list(y1 = df$y1[2],
y2 = df$y2[2],
y3 = df$y3[2])
)
> x
[[1]]
[[1]]$`y1`
[1] "a"
[[1]]$y2
[1] "b"
[[1]]$y3
[1] "c"
[[2]]
[[2]]$`y1`
[1] "d"
[[2]]$y2
[1] "e"
[[2]]$y3
[1] "f"
This is an example when there are two rows in the dataframe. How can I achieve this when the number of rows in the dataframe is variable? So for every row in the dataframe, there should be a list.
We may also use apply by going over the rows and applying as.list to each:
apply(df, 1, as.list)
[[1]]
[[1]]$y1
[1] "a"
[[1]]$y2
[1] "b"
[[1]]$y3
[1] "c"
[[2]]
[[2]]$y1
[1] "d"
[[2]]$y2
[1] "e"
[[2]]$y3
[1] "f"
We first split every row of the dataframe and then for every row we convert each element into separate list element using as.list
lapply(split(df, 1:nrow(df)), as.list)
#$`1`
#$`1`$y1
#[1] "a"
#$`1`$y2
#[1] "b"
#$`1`$y3
#[1] "c"
#$`2`
#$`2`$y1
#[1] "d"
#$`2`$y2
#[1] "e"
#$`2`$y3
#[1] "f"
We can use transpose from purrr
library(purrr)
transpose(df)
#[1]]
#[[1]]$y1
#[1] "a"
#[[1]]$y2
#[1] "b"
#[[1]]$y3
#[1] "c"
#[[2]]
#[[2]]$y1
#[1] "d"
#[[2]]$y2
#[1] "e"
#[[2]]$y3
#[1] "f"

Creating strings from dataframe

My dataframe
x1 <- data.frame(C1 = letters[1:4], C3=1:4, C3=letters[11:14])
I need something a list where each listelement are two values from a row
x2 <- list(c("a", "1"), c("b", "2"), c("c", "3"), c("d", "4"))
Basically each two values from a row need to be a listelement so that I can process them later on!
I tried
lapply(X = x2, MARGIN = 1, FUN = paste, collapse = "")
But that did not give me the desired output!
Is this what you want?
paste0(x1[,1], x1[,2])
# [1] "a1" "b2" "c3" "d4"
How about:
as.list(paste0(x1[,1], x1[,2]))
# [[1]]
# [1] "a1"
#
# [[2]]
# [1] "b2"
#
# [[3]]
# [1] "c3"
#
# [[4]]
# [1] "d4"
It doesn't matter how many rows you have. You just need to specify the columns you want pasted into a string.
Here is a method using lapply:
lapply(1:nrow(x1), function(i) c(x1[i,1], x1[i,2]))
The result is
[[1]]
[1] "a" "1"
[[2]]
[1] "b" "2"
[[3]]
[1] "c" "3"
[[4]]
[1] "d" "4"
data
x1 <- data.frame(C1 = letters[1:4], C3=1:4, C3=letters[11:14],
stringsAsFactors = F)
Note that I used the stringsAsFactors = F argument to construct the data. If I didn't do this, then C1 and C3 would be factors, so I'd have to wrap x[i, 1] in as.character.
If there are multiple columns, we can use do.call
as.list(do.call(paste0, x1[-3]))

R remove an object from a list of vectors

I have a list of vectors and i would like to remove a specific object. Any ideas hot to achieve that?
Lets say i would like to remove the object F. How can i do that?
blocks <- list(
c("A", "B"),
c("C"),
c("D","E", "F")
)
We could also use setdiff with Map
Map(setdiff, blocks, 'F')
#[[1]]
#[1] "A" "B"
#[[2]]
#[1] "C"
#[[3]]
#[1] "D" "E"
or with lapply
lapply(blocks, setdiff, 'F')
#[[1]]
#[1] "A" "B"
#[[2]]
#[1] "C"
#[[3]]
#[1] "D" "E"
If you wanted to remove the third element of the third element of your list, you could try:
blocks[[3]] <- blocks[[3]][-3]
blocks
# [[1]]
# [1] "A" "B"
#
# [[2]]
# [1] "C"
#
# [[3]]
# [1] "D" "E"
If you wanted to remove all elements equal to "F", you could use lapply and a user-defined function to process each vector in the list, removing all "F" elements.
lapply(blocks, function(x) x[x != "F"])
# [[1]]
# [1] "A" "B"
#
# [[2]]
# [1] "C"
#
# [[3]]
# [1] "D" "E"

Resources