Subsetting list from another list in R - r

I learnt how to subset a list based on the values of another list. Am however finding a a challenge when I try to replicate the code in a different context: ( I only need to retain the elements with ":" i.e. a, b and e) How would I have gone about it?
library(stringr)
list1 <- list("a" = "Variable label a: Docket",
"b" = "Variable label b: Boset",
"c" = "Variable label c",
"d" = "Variable label d: Kamba",
"e" = "Variable label e"
)
list2 <- vector("list")
for (i in list1){
if(str_detect(i, ":")){
list2[[i]] <- i
}
}
list1 |> purrr::keep(names(list1) %in% (names(list2) |> stringr::str_sub(-1,-1))) # Thanks to Julian

How about
> Filter(Negate(is.na),lapply(list1,function(x){ifelse(grepl(":",x),x,NA)}))
$a
[1] "Variable label a: Docket"
$b
[1] "Variable label b: Boset"
$d
[1] "Variable label d: Kamba"

Related

Combining vector elements with paste

I have two vectors:
old <- c("a", "b", "c")
new <- c("1", "2", "3")
I want to combine them, so that the elements of the new vector var_names are 'a' = '1', 'b' = '2', 'c' = '3'.
I tried something like this:
for (i in length(new)){
var_names[i] <- paste(old[i], "=", new[i])
}
But it is not working properly. What am I doing wrong here?
EDIT
I was a bit unclear about this. But what I am trying to achieve is;
var_names<- c('a' = '1',
'b' = '2',
'c' = '3')
Reason: https://vincentarelbundock.github.io/modelsummary/articles/modelsummary.html#coef-map
Specifically if you want quotes around a and b
paste0("'",old,"'"," = ","'",new,"'")
[1] "'a' = '1'" "'b' = '2'" "'c' = '3'"
If you want it all in one string
paste0("'",old,"'"," = ","'",new,"'",collapse=", ")
[1] "'a' = '1', 'b' = '2', 'c' = '3'"
Edit: regarding your edit, did you mean this?
names(new)=old
new
a b c
"1" "2" "3"
Update
According to your update, you can use setNames
> setNames(new, old)
a b c
"1" "2" "3"
There are two places you have syntax/logic errors:
You didn't initialize a vector var_name
In for loop, you should use 1:length(new)
Below is a correction and it works
var_names <- c()
for (i in 1:length(new)) {
var_names[i] <- paste(old[i], "=", new[i])
}
and you will see
> var_names
[1] "a = 1" "b = 2" "c = 3"
A more efficient way to achieve the same result is using paste or paste0, e.g.,
> paste(old, new, sep = " = ")
[1] "a = 1" "b = 2" "c = 3"
> paste0(old, " = ", new)
[1] "a = 1" "b = 2" "c = 3"

Assign values with same name in a nested list in R

I want to assign the same value to specific elements in nested lists that have the same name. I would also like to create the element if it didn't exist in the nested list, but this isn't shown in my example.
For example, let's say I have:
ls <- list(a = list(e1 = "value1.1", e2 = "value1.2"),
b = list(e1 = "value2.1", e2 = "value2.2"))
And I want to assign the value "same value" to all the elements in sublists that are named e1 so that I would end up with this desired output:
list(a = list(e1 = "same value", e2 = "value1.2"),
b = list(e1 = "same value", e2 = "value2.2")
> ls
$a
$a$e1
[1] "same value"
$a$e2
[1] "value1.2"
$b
$b$e1
[1] "same value"
$b$e2
[1] "value2.2"
After researches, I found the function modify_depth() in the package purrr that will apply a function to the nested lists, but won't assign a value.
My only other solution was to do this:
ls2 <- list()
for(sublist in ls){
sublist[["e1"]] <- "same value"
ls2 <- c(ls2, list(sublist))
}
names(ls2) <- names(ls)
ls <- ls2
rm(ls2)
Note: after the loop, "same value" are not assigned in ls, so I have to create ls2. I could make this a function, but I'm sure there is a better way to do this, without a for loop.
Thanks for your help!
With map and replace from purrr:
library(purrr)
map(ls, ~replace(., "e1", "same value"))
or with modify_depth:
modify_depth(ls, 1, ~replace(., "e1", "same value"))
replace also works with lapply:
lapply(ls, replace, "e1", "same value")
Output:
$a
$a$e1
[1] "same value"
$a$e2
[1] "value1.2"
$b
$b$e1
[1] "same value"
$b$e2
[1] "value2.2"
The good thing about modify_depth is that you can choose the depth, whereas map and lapply only goes down one level:
ls2 <- list(c = list(a1 = list(e1 = "value1.1", e2 = "value1.2")),
d = list(a1 = list(e1 = "value2.1", e2 = "value2.2")))
modify_depth(ls2, 2, ~replace(., "e1", "same value"))
Output:
$c
$c$a1
$c$a1$e1
[1] "same value"
$c$a1$e2
[1] "value1.2"
$d
$d$a1
$d$a1$e1
[1] "same value"
$d$a1$e2
[1] "value2.2"
Edit: If we want to apply a vector of values to each e1 element of ls2, modify_depth would not work since it does not have a map2 variant. Instead, I would use two layers of map's, map2 on the top layer, and map on the second layer.
vec <- c("newvalue1.1", "newvalue2.1")
map2(ls2, vec, ~map(.x, replace, "e1", .y))
Output:
$c
$c$a1
$c$a1$e1
[1] "newvalue1.1"
$c$a1$e2
[1] "value1.2"
$d
$d$a1
$d$a1$e1
[1] "newvalue2.1"
$d$a1$e2
[1] "value2.2"
We can use lapply to iterate over list elements, then using [ with logical operator for equality == we can locate the values we want to change
> lapply(ls, function(x){
x[names(x)=="e1"] <- "same.value"
x
} )
$`a`
$`a`$`e1`
[1] "same.value"
$`a`$e2
[1] "value1.2"
$b
$b$`e1`
[1] "same.value"
$b$e2
[1] "value2.2"
ls is reserved in R so I suggest to use another name for the list, my alternative is:
y <- list(a = list(e1 = "value1.1", e2 = "value1.2"),
b = list(e1 = "value2.1", e2 = "value2.2"))
lapply(y, function(x) replace(x, list = "e1", values = "new value"))
$a
$a$e1
[1] "new value"
$a$e2
[1] "value1.2"
$b
$b$e1
[1] "new value"
$b$e2
[1] "value2.2"

R loop over two or more vectors simultaneously - paralell

I was looking for method to iterate over two or more character vectors/list in R simultaneously ex. is it some way to do something like:
foo <- c('a','c','d')
bar <- c('aa','cc','dd')
for(i in o){
print(o[i], p[i])
}
Desired result:
'a', 'aa'
'c', 'cc'
'd', 'dd'
In Python we can do simply:
foo = ('a', 'c', 'd')
bar = ('aa', 'cc', 'dd')
for i, j in zip(foo, bar):
print(i, j)
But can we do this in R?
Like this?
foo <- c('a','c','d')
bar <- c('aa','cc','dd')
for (i in 1:length(foo)){
print(c(foo[i],bar[i]))
}
[1] "a" "aa"
[1] "c" "cc"
[1] "d" "dd"
Works under the condition that the vectors are the same length.
In R, you rather iterate based on the indices than on vectors directly:
for (i in 1:(min(length(foo), length(bar)))){
print(foo[i], bar[i])
}
Another option is to use mapply. This wouldn't make a lot of sense for printing, but I'm assuming you have an interest in doing this for something more interesting than print
foo <- c('a','c','d')
bar <- c('aa','cc','dd')
invisible(
mapply(function(f, b){ print(c(f, b))},
foo, bar)
)
Maybe someone arriving based on the title makes good use of this:
foo<-LETTERS[1:10]
bar<-LETTERS[1:3]
i = 0
for (j in 1:length(foo)){
i = i + 1
if (i > length(bar)){
i = 1
}
print(paste(foo[j],bar[i]) )
}
[1] "A A"
[1] "B B"
[1] "C C"
[1] "D A"
[1] "E B"
[1] "F C"
[1] "G A"
[1] "H B"
[1] "I C"
[1] "J A"
which is "equivalent" to: (using for eases assignments)
suppressWarnings(invisible(
mapply(function(x, y){
print(paste(x, y))},
foo, bar)
))

Use to dictionary to relace certain specific values

I have the following ilst
list <- c("AB", "G", "H")
Now I have certain letters that should be replaced. So fe. B and H should be replaced.
So what I have not is:
replace_letter <- c("B", "H")
for(letter in replace_letter){
for (i in list){
print(i)
print(letter)
if(grepl(letter, i)){
new_value <- gsub(letter,"XXX",i)
print("yes")
}
else{
print("no")
}
}
}
However the XXX in my code should be replace by certain lookup values/.
So instead a B -> B+, in stead of H -> H**.
So I need some kind of dictionary function to replace the XXX with something specific.
Does anybody have suggestion how I can include this in the code above?
Data and dictionary
dictionary <- data.frame(From = LETTERS,
To = LETTERS[c(2:length(LETTERS), 1)], stringsAsFactors = F)
set.seed(1234)
data <- LETTERS[sample(length(LETTERS), 10, replace = T)]
Here is the replace-function
replace <- function(input, dictionary){
dictionary[which(input == dictionary$From),]$To
}
Apply it to data:
sapply(data, replace, dictionary = dictionary)
# C Q P Q W Q A G R N
# "D" "R" "Q" "R" "X" "R" "B" "H" "S" "O"
You just have to adjust your dictionary according to your needs.
I use the function plyr::mapvalues to do this. The function takes three arguments, the strings to do the replacement on, and two vectors from and to that define the replacement.
e.g.
plyr::mapvalues(letters[1:3], c("b", "c"), c("x", "y"))
# [1] "a" "x" "y"
I switched to the newer dplyr library, so I'll add another answer here:
In an interactive session I would enter the replacements in dplyr::recode directly:
dplyr::recode(letters[1:3], "b"="x", "c"="y")
# [1] "a" "x" "y"
Using a pre-defined dictionary, you'll have to use UQS to unquote the dictionary due to the tidy-eval semantics of dpylr:
dict <- c("b"="x", "c"="y")
dict
# b c
# "x" "y"
dplyr::recode(letters[1:3], UQS(dict))
# [1] "a" "x" "y"

R: Apostrophes in recode()

I am using the recode() function in the car package to recode an integer class variable in a data frame. I am trying to recode one of the values of the variable to a string that contains a single apostrophe ('). However, this does not work. I imagine it is because the single apostrophe prematurely ends assignment. So, I tried to use \' to exit the function but it doesn't work either.
I would prefer to continue using recode() but if that is not an option, alternatives are welcome.
A working example:
# Load car() and dplyr()
library(car)
library(dplyr)
# Set up df
a <- seq(1:3)
b <- rep(9,3)
df <- cbind(a,b) %>% as.data.frame(.)
# Below works because none of the recoding includes an apostrophe:
recode(df$a, "1 = 'foo'; 2 = 'bar'; 3 = 'foobar'")
# Below doesn't work due to apostrophe in foofoo's:
recode(df$a, "1 = 'foo'; 2 = 'bar'; 3 = 'foofoo's'")
# Exiting doesn't fix it:
recode(df$a, "1 = 'foo'; 2 = 'bar'; 3 = 'foofoo\'s'")
We could escape the quotes to make it work
recode(df$a, "1 = \"foo\"; 2 = \"bar\"; 3 = \"foofoo's\"")
#[1] "foo" "bar" "foobar's"
A base R alternative would be to use the df$a values as numeric index to replace those values
df$a <- c("foo", "bar", "foobar's")[df$a]
df$a
#[1] "foo" "bar" "foobar's"
Suppose if the values are not numeric and not in the sequence.
set.seed(24)
v1 <- sample(LETTERS[1:3], 10, replace=TRUE)
v1
#[1] "A" "A" "C" "B" "B" "C" "A" "C" "C" "A"
as.vector(setNames(c("foo", "bar", "foobar's"), LETTERS[1:3])[v1])
#[1] "foo" "foo" "foobar's" "bar" "bar" "foobar's"
#[7] "foo" "foobar's" "foobar's" "foo"
Here, we replace "A" with "foo", "B" with "bar" and "C" with "foobar's". To do that, create a named key/value vector to replace values in 'v1'.

Resources