【问题标题】:Reliably extract names of R functions from a text file从文本文件中可靠地提取 R 函数的名称
【发布时间】:2015-03-01 13:19:24
【问题描述】:

我想找到我在 R 脚本中经常使用的命名函数(忽略“+”、“$”和“[”等运算符)。我。这里是一个小例子和我到目前为止的笨拙代码。我欢迎更干净、更可靠和更全面的代码。

test1 <- "colnames(x) <- subset(df, max(y))" 
test2 <- "sat <- as.factor(gsub('International', 'Int'l', sat))"
test3 <- "score <- ifelse(str_detect(as.character(sat), 'Eval'), 'Importance', 'Rating')"
test <- c(test1, test2, test3)

测试对象包括八个函数(colnames、subset、max、as.factor、gsub、ifelse、str_detect、as.character),前两个函数两次。匹配它们的迭代一是:

(result <- unlist(strsplit(x = test, split = "\\(")))
 [1] "colnames"                               "x) <- subset"                          
 [3] "df, max"                                "y)"                                    
 [5] "sat <- as.factor"                       "gsub"                                  
 [7] "'International', 'Int'l', sat)))"       "score <- ifelse"                       
 [9] "str_detect"                             "as.character"                          
[11] "sat), 'Eval'), 'Importance', 'Rating')"

然后,一系列手工制作的 gsub 会从这个特定的测试集中清除结果,但这些手动步骤无疑会在其他不那么做作的字符串上有所不足(我在下面提供了一个)。

(result <- gsub(" <- ", " ", gsub(".*\\)", "", gsub(".*,", "", perl = TRUE, result))))
 [1] "colnames"      " subset"       " max"          ""              "sat as.factor" "gsub"          ""             
 [8] "score ifelse"  "str_detect"    "as.character"

下面的对象 test4 包括函数 lapply、function、setdiff、unlist、sapply 和 union。它也有缩进,所以有内部间距。我已将其包含在内,以便读者可以尝试更困难的情况。

test4 <- "contig2 <- lapply(states, function(state) {
                             setdiff(unlist(sapply(contig[[state]], 
                                                   function(x) { contig[[x]]})), union(contig[[state]], state))"

(result <- unlist(strsplit(x = test4, split = "\\("))) 
(result <- gsub(" <- ", " ", gsub(".*\\)", "", gsub(".*,", "", perl = TRUE, result))))

顺便说一句,这个 SO 问题与提取整个函数以创建一个包有关。 A better way to extract functions from an R script?

第一次回答后编辑

test.R <- c(test1, test2, test3) # I assume this was your first step, to create test.R
save(test.R,file = "test.R") # saved so that getParseData() could read it
library(dplyr)
tmp <- getParseData(parse("test.R", keep.source=TRUE))
tmp %>% filter(token=="SYMBOL") # token variable had only "SYMBOL" and "expr" so I shortened "SYMBOL_FUNCTION_CALL"
  line1 col1 line2 col2 id parent  token terminal text
1     1    1     1    4  1      3 SYMBOL     TRUE RDX2
2     2    1     2    1  6      8 SYMBOL     TRUE    X

所有文本都发生了一些事情。我应该怎么做?

【问题讨论】:

    标签: regex r


    【解决方案1】:

    正则表达式可能有效,但您可以使用 R 本身来帮助您。我把你的四行放到一个文件test.R 中,修复了语法问题并运行了以下内容:

    library(dplyr)
    
    tmp <- getParseData(parse("test.R", keep.source=TRUE))
    
    tmp %>% filter(token=="SYMBOL_FUNCTION_CALL")
    
    ##   line1 col1 line2 col2  id parent                token terminal         text
    ## 1      1    1     1    8   1      3 SYMBOL_FUNCTION_CALL     TRUE     colnames
    ## 2      1   16     1   21  11     13 SYMBOL_FUNCTION_CALL     TRUE       subset
    ## 3      1   27     1   29  19     21 SYMBOL_FUNCTION_CALL     TRUE          max
    ## 4      2    8     2   16  39     41 SYMBOL_FUNCTION_CALL     TRUE    as.factor
    ## 5      2   18     2   21  42     44 SYMBOL_FUNCTION_CALL     TRUE         gsub
    ## 6      3   10     3   15  72     74 SYMBOL_FUNCTION_CALL     TRUE       ifelse
    ## 7      3   17     3   26  75     77 SYMBOL_FUNCTION_CALL     TRUE   str_detect
    ## 8      3   28     3   39  78     80 SYMBOL_FUNCTION_CALL     TRUE as.character
    ## 9      5   12     5   17 119    121 SYMBOL_FUNCTION_CALL     TRUE       lapply
    ## 10     6    3     6    9 134    136 SYMBOL_FUNCTION_CALL     TRUE      setdiff
    ## 11     6   11     6   16 137    139 SYMBOL_FUNCTION_CALL     TRUE       unlist
    ## 12     6   18     6   23 140    142 SYMBOL_FUNCTION_CALL     TRUE       sapply
    ## 13     8   11     8   15 191    193 SYMBOL_FUNCTION_CALL     TRUE        union
    

    如您所见,text 列包含您调用的函数的名称。这应该适用于所有语法正确的 R 文件。

    请注意,它不会评估代码,只是解析它。

    编辑 test.R 看起来像这样:

    colnames(x) <- subset(df, max(y))
    sat <- as.factor(gsub('International', 'Int\'l', sat))
    score <- ifelse(str_detect(as.character(sat), 'Eval'), 'Importance', 'Rating')
    
    contig2 <- lapply(states, function(state) {
      setdiff(unlist(sapply(contig[[state]],
                            function(x) { contig[[x]]})),
              union(contig[[state]], state))})
    

    【讨论】:

    • 太棒了!我从未听说过 getParseData()。但是,我编辑了我的问题以显示结果,并欢迎您进一步思考我一定是哪里出了问题。
    • 优秀:此方法找出哪些对象实际上是闭包。任何基于正则表达式的方法都注定要失败,因为它只查找可能有效的字符串。 - 我想这个评论同样适用于 Gabor 的回答 :-)
    【解决方案2】:

    问题中的代码没有有效的语法,但如果我们更正它:

    test1 <- "colnames(x) <- subset(df, max(y))" 
    test2 <- "sat <- as.factor(gsub('International', 'Intl', sat))"
    test3 <- "score <- ifelse(str_detect(as.character(sat), 'Eval'), 'Importance', 'Rating')"
    test <- c(test1, test2, test3)
    

    那么我们可以在codetools包中使用findGlobals

    library(codetools)
    
    f.text <- c("function(){", test, "}")
    f <- eval(parse(text = f.text))
    funs <- findGlobals(f, merge = FALSE)$functions
    

    给予:

     > funs
     [1] "{"            "<-"           "as.character" "as.factor"    "colnames<-"  
     [6] "gsub"         "ifelse"       "max"          "str_detect"   "subset"    
    

    不清楚您希望排除哪些函数,但如果F 是包含它们的字符向量,那么setdiff(funs, F) 将给出除这些之外的所有函数。

    另见Finding out which functions are called within a given function 和:Generating a Call Graph in R

    【讨论】:

    • 感谢您的代码更正。您还从“国际”中取出了撇号。在我的 test4 上尝试了 codetools 技术后,我意识到代码必须正确解析才能使 findGlobals 能够工作。对于这个问题,我只是认为一个包含很多函数的字符串就可以了。也许是正则表达式,但不是代码工具或 hrbrmstr 的方法。
    • 问题包含“Int'l”(两个单引号内的单引号),这是语法错误,因此必须更改。
    猜你喜欢
    • 1970-01-01
    • 2014-10-30
    • 1970-01-01
    • 1970-01-01
    • 2018-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多