Reputation: 23
I've been trying unsuccessfully to replicate in R the following Stata loop:
forvalues i=1/10 {
replace var`i'= a if other_var`i'==b
}
So far I've got this as the closest attempt:
for(i in 1:10) {
df <- df %>%
mutate(get(paste("var",i,sep="")) =
ifelse(get(paste("other_var",i,sep=""))==b
,a
,get(paste("var",i,sep=""))))
}
But I get the following error:
Error: unexpected '=' in:
"survey_data <- survey_data %>%
mutate(paste("offer",i,"_accepted",sep="") ="
If I change the variable to be mutated to a simple variable name, it works, so I'm guessing my code is OK for the "right-hand side of the mutation", but for some reason it's not OK for the "left-hand side".
Upvotes: 2
Views: 2005
Reputation: 1926
You can do something like the following:
df <- structure(list(var1 = c(1, 2, 3, 4),
var2 = c(1, 2, 3, 4),
var3 = c(1,2, 3, 4),
var4 = c(1, 2, 3, 4),
other_var1 = c(1, 0, 1, 0),
other_var2 = c(0,1, 0, 1),
other_var3 = c(1, 1, 0, 0),
other_var4 = c(0, 0, 1,1)),
class = "data.frame",
row.names = c(NA, -4L))
# var1 var2 var3 var4 other_var1 other_var2 other_var3 other_var4
# 1 1 1 1 1 1 0 1 0
# 2 2 2 2 2 0 1 1 0
# 3 3 3 3 3 1 0 0 1
# 4 4 4 4 4 0 1 0 1
## Values to replace based on OP original question
a <- 777
b <- 1
## Iter along all four variables avaible in df
for (i in 1:4) {
df <- within(df, {
assign(paste0("var",i), ifelse(get(paste0("other_var",i)) %in% c(b), ## Condition
a, ## Value if Positive
get(paste0("var",i)) )) ## Value if Negative
})
}
which results in the following output:
# var1 var2 var3 var4 other_var1 other_var2 other_var3 other_var4
# 1 777 1 777 1 1 0 1 0
# 2 2 777 777 2 0 1 1 0
# 3 777 3 3 777 1 0 0 1
# 4 4 777 4 777 0 1 0 1
The solution doesn't look like a one-line-solution, but it actually is one, a quite dense one tho; hence let's see how it works by its foundation components.
within()
: I don't want to repeat what other people have excellently explained, so for the within()
usage, I gently refer you here.
assign(paste0("var",i), X)
part.Here I am following that @han-tyumi did in his answer, meaning recover the name of the variables using paste0()
and assign them the value of X
(to be explained) using the assign()
function.
X
.Before I referenced assign(paste0("var",i), X)
. Where, indeed, X
is equal to ifelse(get(paste0("other_var",i)) %in% c(b), a, get(paste0("var",i)) )
.
ifelse()
:First, I recover the values of variable other_var(i)
(with i = 1,2,3,4) combining the function get()
with paste0()
while looping. Then, I use the %in%
operator to check whether the value assigned to variable b
(on my example, the number 1) was contained on variable other_var(i)
or not; this generates a TRUE or FALSE depending if the condition is met.
TRUE
part of the ifelse()
function.This is the simplest part if the condition is met then assign, a
(which in my example is equal to 777
).
FALSE
part of the ifelse()
function.get(paste0("var",i))
: which is the value of the variable itself (meaning, if the condition is not meet, then keep the variable unaltered).
Upvotes: 0
Reputation: 245
This solution is very inelegant, but I think does exactly what you want.
var1 <- "x"
var2 <- "y"
var3 <- "z"
other_var1 <- 1
other_var2 <- 0
other_var3 <- 1
df <- data.frame(var1, other_var1, var2, other_var2, var3, other_var3)
for(i in 1:3){
var_name <- paste("df$var", i, sep = "")
other_var_name <- paste("df$other_var", i, sep = "")
if (eval(parse(text = other_var_name)) == 1){
assign(var_name, "a")
}
}
There are three key ingredients here. First the paste()
function to create the names of the variables in the current iteration of the loop. Second, the eval(parse(foo))
combo to reference the actual variable whose name is stored as string in foo
. Third, using assign()
to assign values to a variable (as opposed to using <-
).
Upvotes: 2
Reputation: 49650
This looks like FAQ 7.21.
The most important part of that answer is at the end where is says to use a list instead.
Trying to work on a group of global variables in R leads to complicated code that is hard to read and even harder to debug.
If you instead put those variables into a single list, then you can access them by name or position and use tools like lapply
or the purrr
package (part of tidyverse) to process everything in the list (or some of the things in the list using map_at
or map_if
from purrr
).
If tell us more about what you are trying to accomplish, we may be able to give a much simpler example of how to do it.
Upvotes: 1