R:如何使用数字索引循环变量名(就像在 Stata 中一样)

R: How to loop over variable names using number indexes (like in Stata)

我一直试图在 R 中复制以下 Stata 循环,但没有成功:

forvalues i=1/10 {
    replace var`i'= a if other_var`i'==b
}

到目前为止,我认为这是最接近的尝试:

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=""))))
}

但我收到以下错误:

Error: unexpected '=' in:
"survey_data <- survey_data %>%
                      mutate(paste("offer",i,"_accepted",sep="") ="

如果我将要变异的变量更改为一个简单的变量名,它就可以工作,所以我猜我的代码对于“变异的右侧”是可行的,但由于某种原因它不可行对于“左侧”。

这个解决方案非常不优雅,但我认为它完全符合您的要求。

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")
  }
}

这里有三个关键成分。首先 paste() 函数在循环的当前迭代中创建变量的名称。其次,eval(parse(foo)) 组合引用名称作为字符串存储在 foo 中的实际变量。第三,使用 assign() 为变量赋值(与使用 <- 相反)。

这看起来像 FAQ 7.21。

该答案最重要的部分是在结尾处说要改用列表。

尝试在 R 中处理一组全局变量会导致难以阅读甚至更难调试的复杂代码。

如果您改为将这些变量放入一个列表中,那么您可以按名称或位置访问它们,并使用 lapplypurrr 包(tidyverse 的一部分)等工具来处理所有内容在列表中(或使用 map_atmap_if 来自 purrr 的列表中的某些内容)。

如果告诉我们更多关于您想要完成的事情,我们可能会给出一个更简单的例子来说明如何做。

您可以执行以下操作:

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
  })
}

这导致以下输出:

# 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

解决方案看起来不像单行解决方案,但它实际上一个,一个相当密集的解决方案;因此,让我们看看它的基础组件是如何工作的。

within():我不想重复别人解释的很好,所以对于within()的用法,我轻轻地推荐给你here

第:assign(paste0("var",i), X)部分。

这里我遵循 在他的回答中所做的,这意味着使用 paste0() 恢复变量的名称并为它们分配 X 的值(待解释)使用assign() 函数。

我们来谈谈X

在我引用 assign(paste0("var",i), X) 之前。实际上,X 等于 ifelse(get(paste0("other_var",i)) %in% c(b), a, get(paste0("var",i)) ).

里面ifelse():

条件:

首先,我在循环时结合函数 get()paste0() 恢复变量 other_var(i) 的值(i = 1,2,3,4)。然后,我使用 %in% 运算符检查分配给变量 b 的值(在我的示例中,数字 1)是否包含在变量 other_var(i) 中;这会根据条件是否满足生成 TRUE 或 FALSE。

ifelse() 函数的 TRUE 部分。

这是最简单的部分,如果满足条件则赋值,a(在我的例子中等于777)。

ifelse() 函数的 FALSE 部分。

get(paste0("var",i)):是变量本身的值(意思是如果不满足条件,则保持变量不变)。