【问题标题】:Replace a value in a data frame based on a conditional (`if`) statement根据条件 (`if`) 语句替换数据框中的值
【发布时间】:2011-08-15 00:21:30
【问题描述】:

在下面编码的 R 数据帧中,我想替换 B 的所有时间 出现b

junk <- data.frame(x <- rep(LETTERS[1:4], 3), y <- letters[1:12])
colnames(junk) <- c("nm", "val")

这提供了:

   nm val
1   A   a
2   B   b
3   C   c
4   D   d
5   A   e
6   B   f
7   C   g
8   D   h
9   A   i
10  B   j
11  C   k
12  D   l

我最初的尝试是像这样使用forif 语句:

for(i in junk$nm) if(i %in% "B") junk$nm <- "b"

但我相信您可以看到,这会将junk$nm 的所有值替换为b。我可以理解为什么这样做,但我似乎无法让它仅替换原始值为 B 的那些 junk$nm 情况。

注意:我设法用gsub 解决了这个问题,但为了学习 R,我仍然想知道如何让我的原始方法发挥作用(如果可能的话)

【问题讨论】:

  • 您可能希望将 stringsAsFactors = FALSE 添加到原始 data.frame 构造中。
  • @jimmyb 为什么?如果使用 R 的大部分建模代码进行建模,因子是有用且必要的。处理这个问题的正确方法是承认数据是一个因素。如果您不想要/不需要这种转换,那么您可以按您说的做。如果您确实想要这个因素,那么有一些简单的方法可以进行@Kenny 想要执行的操作。
  • 因此因素过去因为性能而更受欢迎,但是,现在字符串是不可变的并且散列的因素的值不太明显,因为大多数基本 R 功能只会转换它们(尽管使用警告)直接。我认为因素会导致我在人们的 R 代码中发现大量错误。

标签: r recode


【解决方案1】:

简短的回答是:

junk$nm[junk$nm %in% "B"] <- "b"

看看Index vectors in R Introduction(如果你还没有阅读的话)。


编辑。正如在 cmets 中注意到的那样,此解决方案适用于字符向量,因此您的数据会失败。

因素最好的方法是改变水平:

levels(junk$nm)[levels(junk$nm)=="B"] <- "b"

【讨论】:

  • 简短补充:只有在右侧有一个集合时,%in% 的使用才真正有帮助,例如c("B","C")。做junk$nm[junk$nm == "B"] 是更好的方法。
  • 哦,另一个重要的补充:这样做需要首先将因子级别 b 添加到因子 nm。如果您想使用角色而不是因素,那么 diliop 的版本实际上是更好的版本。 (始终首先考虑变量的类型!)
  • 这不适用于@Kenny 创建的数据,因为数据是因素。您是否忘记了一个步骤,或者您是否有停止将字符转换为因子的全局设置?
  • @Thilo %in%== 之间的重要区别之一是NA 处理:c(1,2,NA)==1 给出TRUE, FALSE, NA,但c(1,2,NA) %in% 1 给出TRUE, FALSE, FALSE。是的,我忘了检查这是否有效:/
  • 这帮助了我,因为我专门寻找如何对字符向量中的所有值执行此操作。谢谢。
【解决方案2】:

更容易将 nm 转换为字符,然后进行更改:

junk$nm <- as.character(junk$nm)
junk$nm[junk$nm == "B"] <- "b"

编辑:如果您确实需要将 nm 作为因素进行维护,请在最后添加:

junk$nm <- as.factor(junk$nm)

【讨论】:

  • as.character() 在处理因子时让生活变得更加轻松。 +1
  • 如果你有多个列怎么办?
  • @diliop :谢谢你:如果 junk$nm 取值 "B", "Y", "Z", ... 我想改变怎么办?
【解决方案3】:

您已经在nm 中创建了一个因子变量,因此您要么需要避免这样做,要么为因子属性添加一个额外的级别。您还应该避免在 data.frame() 的参数中使用 &lt;-

选项 1:

junk <- data.frame(x = rep(LETTERS[1:4], 3), y =letters[1:12], stringsAsFactors=FALSE)
junk$nm[junk$nm == "B"] <- "b"

选项 2:

levels(junk$nm) <- c(levels(junk$nm), "b")
junk$nm[junk$nm == "B"] <- "b"
junk

【讨论】:

  • @DWin 感谢您对问题的输入以及考虑变量类型的需要。我接受了@diliop 的回答,因为它是第一个工作的。我知道 data.frame 一起使用?
  • 您不需要将b添加为级别,只需将B的级别更改为b即可。
  • @KennyPeanuts:列名是一个问题,看a &lt;- data.frame(x&lt;-1:10)。它的列名不是x,而是一个乱七八糟的x....1.10。最好使用 data.frame(x=1:10)。然后你就知道你的列名是什么了。
  • @Gavin:添加比替换更容易,不让它成为一个因素更容易。
  • @Dwin 更容易吗?我不同意 - 请参阅我的答案以获得简单的信息。添加级别可能会让您感到困惑,例如在使用predict() 建模时,如果新数据中的因子级别与用于拟合模型的因子级别不匹配,则会抱怨。从长远来看,比依赖捷径更干净,可以根据需要正确格式化数据。我同意不将其作为一个因素可能更容易,但如果它已经是一个因素,或者需要成为某个建模练习的因素......
【解决方案4】:

由于您显示的数据是因素,它使事情变得有点复杂。 @diliop 的答案通过将 nm 转换为字符变量来解决问题。要回到原始因素,还需要进一步的步骤。

另一种方法是在适当的位置操纵因子的水平。

> lev <- with(junk, levels(nm))
> lev[lev == "B"] <- "b"
> junk2 <- within(junk, levels(nm) <- lev)
> junk2
   nm val
1   A   a
2   b   b
3   C   c
4   D   d
5   A   e
6   b   f
7   C   g
8   D   h
9   A   i
10  b   j
11  C   k
12  D   l

这很简单,我经常忘记levels()有一个替换函数。

编辑:正如@Seth 在 cmets 中所指出的,这可以在单行中完成,而不会失去清晰度:

within(junk, levels(nm)[levels(nm) == "B"] <- "b")

【讨论】:

  • 不错。我不知道levels() 的替换功能。一个班轮junk &lt;- within(junk, levels(nm)[levels(nm)=="B"] &lt;- "b")怎么样?
  • @Marek 拍脑袋 只是表明,当它已经过了就寝时间时,不应该在 SO 上回复 cmets。让我们再试一次...
  • @Seth 确实 - 不错。不知道为什么我分开了这些步骤?也许是为了展览......
【解决方案5】:

在一个命令中执行此操作的最简单方法是使用which 命令,并且也不需要通过这样做将因子更改为字符:

junk$nm[which(junk$nm=="B")]<-"b"

【讨论】:

    【解决方案6】:

    另一种替换值的有用方法

    library(plyr)
    junk$nm <- revalue(junk$nm, c("B"="b"))
    

    【讨论】:

      【解决方案7】:

      如果你正在使用字符变量(注意stringsAsFactors 在这里是假的)你可以使用替换:

      junk <- data.frame(x <- rep(LETTERS[1:4], 3), y <- letters[1:12], stringsAsFactors = FALSE)
      colnames(junk) <- c("nm", "val")
      
      junk$nm <- replace(junk$nm, junk$nm == "B", "b")
      junk
      #    nm val
      # 1   A   a
      # 2   b   b
      # 3   C   c
      # 4   D   d
      # ...
      

      【讨论】:

      • 这适用于所有变量类型。我只是用它在整数向量的某些索引处插入 NA。
      【解决方案8】:
      stata.replace<-function(data,replacevar,replacevalue,ifs) {
        ifs=parse(text=ifs)
        yy=as.numeric(eval(ifs,data,parent.frame()))
        x=sum(yy)
        data=cbind(data,yy)
        data[yy==1,replacevar]=replacevalue
        message=noquote(paste0(x, " replacement are made"))
        print(message)
        return(data[,1:(ncol(data)-1)])
      }
      

      使用下面的行调用这个函数。

      d=stata.replace(d,"under20",1,"age<20")
      

      【讨论】:

        【解决方案9】:

        你也可以使用ifelse,非常简单易懂

        junk$val <- ifelse(junk$nm == "B", "b", junk$val)
        

        如果你还想通过for loop做正确的做法

        for(i in 1:nrow(junk)){
          if(junk[i, "nm"] == "B"){
            junk[i, "val"] <- "b"
          }
        }
        
        junk
        > junk
           nm val
        1   A   a
        2   B   b
        3   C   c
        4   D   d
        5   A   e
        6   B   b
        7   C   g
        8   D   h
        9   A   i
        10  B   b
        11  C   k
        12  D   l
        

        【讨论】:

          【解决方案10】:

          我遇到了同样的问题,你也可以对每一列做同样的事情,

           fix_junk <- function(x){
                #x <- as.character(x)
                x[x == "B"] <- "b"
                x
              }
              junk[] <- lapply(junk, fix_junk); junk # junk[] to get a data frame rather than a list
              junk[1:3] <- lapply(junk[1:3], fix_junk); junk
          

          【讨论】:

            猜你喜欢
            • 2021-04-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-06-19
            • 1970-01-01
            • 2019-01-02
            • 1970-01-01
            相关资源
            最近更新 更多