【问题标题】:Iterating through data frame and changing values on condition [R]遍历数据框并根据条件更改值 [R]
【发布时间】:2020-01-19 16:58:57
【问题描述】:

不得不记账,因为这个 for 循环序列已经让我烦恼了很长一段时间。

我在 R 中有一个数据框,有 1000 行和 10 列,每个值的范围为 1:3。我想重新编码每个条目,以便:1==3、2==2、3==1。我知道有更简单的方法可以做到这一点,例如对每一列进行子设置和对条件进行硬编码,但这并不总是理想的,因为我使用的许多数据集最多有 100 列。

我想使用嵌套循环来完成这项任务——这就是我目前所拥有的:

for(i in 1:nrow(dat_trans)){
  for(j in length(dat_trans)){
    if(dat_trans[i,j] == 1){
      dat_trans[i,j] <- 3
    } else if(dat_trans[i,j] == 2){
      dat_trans[i,j] <- 2
    } else{
      dat_trans[i,j] <- 1
    }
  }
}

所以我遍历第一列,获取每个值并根据 if/else 的条件更改它,我仍在学习 R,所以如果您在我的代码中有任何指针,请随时指出。

编辑:代码

【问题讨论】:

  • 如果值已经是2,为什么要换成2
  • 我想我可以使用跳过/使用它来做其他事情。

标签: r loops if-statement


【解决方案1】:

R 是一种矢量化语言,因此您真的不需要内部循环。
此外,如果您注意到 4-"old value" = "new value",则可以消除 if 语句。

for(i in 1:ncol(dat_trans)){
        dat_trans[,i] <- 4-dat_trans[,i]
}

外部循环现在只对列进行 10 次迭代,而不是对所有行进行 1000 次迭代。这将大大提高性能。

【讨论】:

  • 甚至更好地将数据框转换为矩阵并完全摆脱循环:dat_trans &lt;- 4-as.matrix(dat_trans)
  • @GordonShumway 你能详细说明为什么这种方法会更好吗?
  • 您避免一起使用循环,并在整个矩阵上更快地执行 1 个大型向量操作。使用矩阵的缺点是所有列都将转换为单一类型。例如,如果第一列是名称列表和 99 列数字,当转换为矩阵时,它将成为所有字符对象的矩阵。
  • 我明白了。还有一个问题-您最初发布的代码按预期工作,但是,我对该代码如何知道将 1 重新编码为 3 和 3 重新编码为 1 感到困惑。我不太清楚 4- 的操作暗示。
  • 您说您想将 1 重新编码为 3 (1+3=4)、将 2 重新编码为 2 (2+2=4) 并将 3 重新编码为 1 (3+1 =4)。在这种情况下,“旧 + 新”= 4。因此,通过一些基本的数学运算,结果是:4 - “旧值”=“新值”。一个袖手旁观的小窍门。
【解决方案2】:

这种类型的操作是交换操作。不使用 for 循环交换值的方法很多。

设置一个简单的数据框:

df <- data.frame(
  col1 = c(1,2,3),
  col2 = c(2,3,1),
  col3 = c(3,1,2)
)

使用虚拟值:

df[df==1] <- 4
df[df==3] <- 1
df[df==4] <- 3

使用临时变量:

dftemp <- df
df[dftemp==1] <- 3
df[dftemp==3] <- 1

使用乘法/除法和加法/减法:

df <- 4 - df

使用布尔运算:

df <- (df==1) * 3 + (df==2) * 2 + (df==3) * 1

使用按位异或(如果您真的需要速度):

df[df!=2] <- sapply(df, function(x){bitwXor(2,x)})[df!=2]

如果需要嵌套的 for 循环,switch 函数是一个不错的选择。

for(i in seq(ncol(df))){
  for(j in seq(nrow(df))){
    df[j,i] <- switch(df[j,i],3,2,1)
  }
}

如果值的索引不如 1、2 和 3 好,则可以使用文本。

for(i in seq(ncol(df))){
  for(j in seq(nrow(df))){
    df[j,i] <- switch(as.character(df[j,i]),
                      "1" = 3,
                      "2" = 2,
                      "3" = 1)
  }
}

【讨论】:

  • 谢谢!我很欣赏各种方法。
【解决方案3】:

这听起来像是 merge/join 操作。

set.seed(42)
dat_trans <- as.data.frame(
  setNames(lapply(1:3, function(ign) sample(1:3, size=10, replace=TRUE)),
           c("V1", "V2", "V3"))
)
dat_trans
#    V1 V2 V3
# 1   3  2  3
# 2   3  3  1
# 3   1  3  3
# 4   3  1  3
# 5   2  2  1
# 6   2  3  2
# 7   3  3  2
# 8   1  1  3
# 9   2  2  2
# 10  3  2  3

newvals <- data.frame(old = c(1, 3), new = c(3, 1))
newvals
#   old new
# 1   1   3
# 2   3   1

使用dplyrtidyr

library(dplyr)
library(tidyr) # gather, spread
dat_trans %>%
  mutate(rn = row_number()) %>%
  gather(k, v, -rn) %>%
  left_join(newvals, by = c("v" = "old")) %>%
  mutate(v = if_else(is.na(new), v, new)) %>%
  select(-new) %>%
  spread(k, v) %>%
  select(-rn)
#    V1 V2 V3
# 1   1  2  1
# 2   1  1  3
# 3   3  1  1
# 4   1  3  1
# 5   2  2  3
# 6   2  1  2
# 7   1  1  2
# 8   3  3  1
# 9   2  2  2
# 10  1  2  1

(对rn 的需求可能是因为我使用了旧版本的tidyr:我现在是0.8.2,虽然最近发布了1.0.0。那个版本做了很多增强/在spread/gather 上工作,并引入了pivot_* 函数,这可能会更流畅。如果您有更新的版本,请尝试不使用rn 部分。)


或者使用“重新编码”心态的更直接的方法:

dat_trans[,c("V1", "V2", "V3")] <- lapply(dat_trans[,c("V1", "V2", "V3")], car::recode, "1=3; 3=1")
# or
dat_trans[,c("V1", "V2", "V3")] <- lapply(dat_trans[,c("V1", "V2", "V3")], dplyr::recode, '1' = 3L, '3' = 1L)

【讨论】:

  • 谢谢。将尝试一下并研究这些功能。
【解决方案4】:

您可以使用分配矩阵ammatch()df1 的属性的每个值与am 的第 1 列但选择第 2 列,然后将其分配给 df1。当然是lapply()

df1
#   V1 V2 V3
# 1  1  2  1
# 2  1  2  1
# 3  1  1  2
# 4  1  3  2
# 5  2  3  2

am <- matrix(c(1, 2, 3, 3, 2, 1), 3)
am
#      [,1] [,2]
# [1,]    1    3
# [2,]    2    2
# [3,]    3    1

df1[] <- lapply(df1, function(x) am[match(x, am[,1]), 2])
df1
#   V1 V2 V3
# 1  3  2  3
# 2  3  2  3
# 3  3  3  2
# 4  3  1  2
# 5  2  1  2

数据

df1 <- structure(list(V1 = c(1L, 1L, 1L, 1L, 2L), V2 = c(2L, 2L, 1L, 
3L, 3L), V3 = c(1L, 1L, 2L, 2L, 2L)), class = "data.frame", row.names = c(NA, 
-5L))

【讨论】:

    猜你喜欢
    • 2020-04-21
    • 1970-01-01
    • 1970-01-01
    • 2020-07-08
    • 2021-03-31
    • 2022-01-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多