【问题标题】:Changing variable value based on condition根据条件更改变量值
【发布时间】:2020-01-02 02:02:22
【问题描述】:

我有数据框:

a<-c(1,2,3,4)
b<-c(1988,1970,1999,2000)
years_practicing<-rep(NA,4)
df<-data.frame("ID"=a, "grad_year"=b, "years_practicing"=years_practicing)

看起来像:

ID   grad_year    years_practicing
1     1988           NA
2     1970           NA
3     1999           NA
4     2000           NA

现在我想做这个(它是伪代码!):

if (ID=1 || ID=2) 
{
   years_practicing[corresponding cell]<-2017-grad_year
}

if (ID=3 || ID=4) 
{
   years_practicing[corresponding cell]<-2018-grad_year
}

实现这一目标:

ID   grad_year    years_practicing
1     1988           29
2     1970           47
3     1999           19
4     2000           18

我知道如何以程序方式进行(使用while 循环和if 语句),但我想以矢量化方式进行。

我试过这个(以及类似的变体):

year_2017_start<-c(1, 2)
year_2018_start<-c(3,4)
df$years_practicing[any(df$ID == year_2017_start)]<- 2017-df$grad_yr
df$years_practicing[any(df$ID == year_2018_start)]<- 2018-df$grad_yr

但是接收错误:

Error in df$years_practicing[any(df$ID == year_2017_start)] <- 2017 -  : 
  replacement has length zero
> df$years_practicing[any(df$ID == year_2018_start)]<- 2018-df$grad_yr
Error in df$years_practicing[any(df$ID == year_2018_start)] <- 2018 -  : 
  replacement has length zero

问题:

  1. 如何改进我的代码以使其正常工作。 (需要回答)

  2. 有没有更快的方法来获得类似的结果? (可选)

【问题讨论】:

  • df$years_practicing[which(df$ID == year_2017_start)]grad_year
  • @Cuan 你的代码就是我想要的。如果你想写答案,我会排除它。

标签: r dataframe vectorization data-cleaning


【解决方案1】:

此单行代码仅使用基数 R。如果 ID 为 1 或 2,则显示的 %in% 表达式计算结果为 TRUE,否则计算结果为 FALSE。从 2018 年减去时,它们分别转换为 1 和 0,然后我们从中减去 grad_year

transform(df, years_practicing = 2018 - (ID %in% 1:2) - grad_year)

给予:

  ID grad_year years_practicing
1  1      1988               29
2  2      1970               47
3  3      1999               19
4  4      2000               18

【讨论】:

    【解决方案2】:

    你可以使用命名向量

    v1 = c(`1` = 2017,
           `2` = 2017,
           `3` = 2018,
           `4` = 2018)
    
    v1[df$ID] - df$grad_year
    # 1  2  3  4 
    #29 47 19 18 
    

    【讨论】:

      【解决方案3】:

      不确定您必须使用矢量化方法来更新值的动机;但是一些矢量化函数,例如ifelse() 在这里可能会有更好的帮助。无论如何,以下是您想要的矢量化解决方案:

      df$years_practicing[which(df$ID == year_2017_start)]<- 2017-df$grad_year[which(df$ID == year_2017_start)] 
      
      

      【讨论】:

      • 谢谢。据我所知,矢量化的速度更好。您能否也提供ifelse() 版本并解释为什么它可以更好?
      【解决方案4】:

      你可以使用dplyr:

      library(dplyr)
      df %>% 
        mutate(years_practicing = ifelse(ID == 1 | ID == 2,
                                         2017-grad_year,
                                         2018-grad_year))
      

      如果您有两个以上的条件要测试(例如,如果您有 3 年 - 2017 年、2018 年和 2019 年),您可以使用 case_when:

      df %>% 
        mutate(years_practicing = case_when(
          ID == 1 | ID == 2 ~ 2017-grad_year,
          ID == 3 ~ 2018-grad_year,
          TRUE ~ 2019-grad_year)
          )
      

      编辑:比较给定答案的表现

      我很好奇每种解决方案的速度。我比较了截至 2019 年 8 月 29 日建议的解决方案。@Chuan 的答案获胜!那很有趣...

      library(microbenchmark)
      library(dplyr)
      
      a<-sample(c(1,2,3,4), 20000, replace = TRUE)
      b<-sample(c(1988:2015), 20000, replace = TRUE)
      years_practicing<-rep(NA, 20000)
      df<-data.frame("ID"=a, "grad_year"=b, "years_practicing"=years_practicing)
      year_2017_start<-c(1, 2)
      year_2018_start<-c(3,4)
      v1 = c(`1` = 2017,
             `2` = 2017,
             `3` = 2018,
             `4` = 2018)
      
      mb <- microbenchmark(
        df$years_practicing[which(df$ID == year_2017_start)]<- 2017-df$grad_year[which(df$ID == year_2017_start)], 
        transform(df, years_practicing = 2018 - (ID %in% 1:2) - grad_year),
        df %>% 
          mutate(years_practicing = ifelse(ID == 1 | ID == 2,
                                           2017-grad_year,
                                           2018-grad_year)),
        v1[match(df$ID, names(v1))] - df$grad_year,
        times=500)
      
      ggplot2::autoplot(mb) 
      

      【讨论】:

      • 这太棒了!谢谢。
      猜你喜欢
      • 2012-11-12
      • 1970-01-01
      • 2020-02-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-29
      • 2021-03-10
      相关资源
      最近更新 更多