检查一些条件并在R中输出一个新变量

时间:2016-06-28 00:12:15

标签: r

我的样本数据如下,

main_data = data.frame(var1 = c('abd_cde', 'BPCS', 'POIU', 'CDRD', 'UPqw', 'qwer')),

main_data

     var1
1 abd_cde
2    BPCS
3    POIU
4    CDRD
5    UPqw
6    qwer

我有以下代码,我想检查一个变量中的某些条件并输出另一个变量。以下是我编写的代码,

main_data$var2 = 0

for (i in 1:nrow(main_data)) {
  if(grepl("_", main_data$var1[i]) == TRUE){
    main_data$var2[i] = "1"
  }
  else if((substr(main_data$var1[i], 1, 1) == 'B') | (substr(main_data$var1[i], 1, 1) == 'C') ){
    main_data$var2[i] = "2"
  }
  else if((substr(main_data$var1[i], 1, 1) == 'P') | (substr(main_data$var1[i], 1, 1) == 'U') ){
    main_data$var2[i] = "3"
  }

}

要给出代码的要点,如果一个变量包含“_”,我想输出1,如果它以B或C开头,想输出2,如果它以P或U开头,想要输出3

但是这段代码需要花费大量时间才能运行,因为我有大约200万条记录要运行。有没有办法让我们有效率?

我理想的输出是,

     var1   var2
1 abd_cde     1
2    BPCS     2
3    POIU     3
4    CDRD     2
5    UPqw     3
6    qwer     0

有人可以帮我这么做吗?

由于

4 个答案:

答案 0 :(得分:3)

您可以将它组合成一个表达式(例如,使用多个let viewController: Exercise_Type_ViewController = self.storyboard?.instantiateViewControllerWithIdentifier("Exercise_type") as! Exercise_Type_ViewController self.presentViewController(viewController, animated: true, completion: nil) 语句),但这很容易阅读,并且应该比循环更快:

ifelse

如果您只想测试以单个字母开头的字符串,> x <- c("foo_bar", "x_y", "Bxx", "Cxx", "Pzz", "Uzz") > x[grepl('_', x)] <- 1 > x[grepl('^[BC]', x)] <- 2 > x[grepl('^[PU]', x)] <- 3 > x [1] "1" "1" "2" "2" "3" 函数也可以正常工作。

答案 1 :(得分:3)

如果你需要速度,Rcpp是一个选择。使用Rcpp库和C ++标准库可以实现很多功能。例如,以下是我们如何使用std::strchr()来查找下划线,一些数组索引,字符比较和三元链来实现您的需求:

library(Rcpp);
cppFunction(includes='#include <cstring>','
    IntegerVector f1(CharacterVector x) {
        IntegerVector res(x.size());
        char c;
        for (int i = 0; i < x.size(); ++i)
            res[i] =
                std::strchr(x[i],\'_\') ? 1 :
                (c = x[i][0])==\'B\' || c==\'C\' ? 2 :
                c==\'P\' || c==\'U\' ? 3 :
                0
            ;
        return res;
    }
');

f1(main_data$var1);
## [1] 1 2 3 2 3 0

答案 2 :(得分:3)

dplyr的最新版本(0.5.0)附带了一个新的case_when函数,该函数无需使用令人讨厌的嵌套ifelse结构。您可以使用startsWith(HT @Psidom)或普通的grepl来实现它:

library(dplyr)

              # string functions don't like factors
main_data %>% mutate(var1 = as.character(var1)) %>% 
    # for each case (in order), if the left side is true, return the right side
    mutate(var2 = case_when(grepl('_', .$var1, fixed = TRUE) ~ 1, 
                            startsWith(.$var1, 'B') | startsWith(.$var1, 'C') ~ 2, 
                            startsWith(.$var1, 'P') | startsWith(.$var1, 'U') ~ 3, 
                            TRUE ~ 0))

#      var1 var2
# 1 abd_cde    1
# 2    BPCS    2
# 3    POIU    3
# 4    CDRD    2
# 5    UPqw    3
# 6    qwer    0

# or with grepl (results are identical)
main_data %>% mutate(var1 = as.character(var1)) %>% 
    mutate(var2 = case_when(grepl('_', .$var1, fixed = TRUE) ~ 1, 
                            grepl('^[BC]', .$var1) ~ 2, 
                            grepl('^[PU]', .$var1) ~ 3, 
                            TRUE ~ 0))

请注意.$var1符号,这对于dplyr来说很奇怪,但由于某种原因似乎是必要的。

它看起来也相当活泼; dplyr尝试在C中运行很多,它似乎就在这里。只需在评论中对@ bgoldst的Rcpp和@ Psidom的data.table运行测试集(显然不太现实):

Unit: microseconds
       expr      min       lq     mean   median       uq       max neval
 startsWith 1285.372 1384.862 1593.404 1478.276 1689.269  6547.404   100
      grepl 1260.778 1395.494 1575.984 1509.890 1752.506  2258.982   100
       rcpp 3783.274 4101.735 4562.076 4361.927 4637.183 10818.012   100
 data.table  913.635 1069.397 1180.042 1135.440 1290.791  1567.259   100

重新采样到100,000行(可能仍然没有代表性,取决于引擎盖下发生的事情),Rcpp可以预见地消失:

Unit: milliseconds
       expr       min         lq       mean     median         uq       max neval
 startsWith 21.288367  27.555653  30.904011  29.611775  34.269493  40.80770   100
      grepl 57.025665  60.209641  63.748722  62.204733  66.900175  73.81332   100
       rcpp  8.090686   8.545949   9.419899   8.764327   9.567707  24.65368   100
 data.table 92.093603 100.449884 107.358620 104.229641 107.240123 240.52098   100

无论var1是因素还是角色,时间都相似。

答案 3 :(得分:1)

这是在R中使用String函数的简单方法。这与Keith提到的类似。

main_data = data.frame(var1 = c('abd_cde', 'BPCS', 'POIU', 'CDRD', 'UPqw', 'qwer'))
main_data$var2<-ifelse(grepl("_",main_data$var1),1,ifelse(grepl("B.*|C.*",main_data$var1),2,ifelse(grepl("P.*|U.*",main_data$var1),3,0)))

在这里,我使用ifelse()遍历所有行并给出你想要的结果。