使用 R 脚本从完全定义的 class 名称中提取包名称

Extracting the Package name from fully defined class names using R scripting

我的 CSV 文件中有以下类型的数据集 (ds1),其中包括 class 名称和相应的故障。我打算使用 R 脚本从故障数等于 2 的数据中提取或过滤包名称。

Class                              Faults

org.apache.tools.ant.taskdefs.Definer   2
org.apache.tools.ant.taskdefs.Definer   2
org.apache.tools.ant.taskdefs.Delete    1
org.apache.tools.ant.taskdefs.Deltree   2
org.apache.tools.ant.taskdefs.DependSet 2
org.apache.tools.ant.taskdefs.DependSet 2
org.apache.tools.ant.taskdefs.DependSet 2
org.apache.tools.ant.taskdefs.Ear   2
org.apache.tools.ant.taskdefs.Ear   2
org.apache.tools.ant.taskdefs.Echo  1
org.apache.tools.ant.Exec   2
org.apache.tools.ant.Exec   2

我已经编写了以下代码,但是,它没有产生所需的输出

dschanged<- subset(ds1, grep( "/^([^\.]+)/", class) & Faults==2 )

从技术上讲,我需要适当的正则表达式来提取最后一个点 (.) 之前的字符串以生成以下输出。

org.apache.tools.ant.taskdefs       2
org.apache.tools.ant.taskdefs       2
org.apache.tools.ant.taskdefs       2
org.apache.tools.ant.taskdefs       2
org.apache.tools.ant.taskdefs       2
org.apache.tools.ant.taskdefs       2
org.apache.tools.ant.taskdefs       2
org.apache.tools.ant.taskdefs       2
org.apache.tools.ant                2
org.apache.tools.ant                2

您也可以在没有任何花哨的正则表达式的情况下执行此操作:在点上拆分每个 Class 字符串,然后点粘贴除最后一个子字符串之外的所有字符串。

library(magrittr)  # Provides pipe operator `%>%`

dschanged <- subset(ds1, Faults == 2)
dschanged$Class <- dschanged$Class %>%
                     strsplit(split = "[.]") %>%
                     sapply(function(x) head(x, -1L) %>% paste(collapse = "."))

请注意,没有点的字符串将被转换为空字符串。它也比@r2evans 建议的解决方案慢很多。

我认为您不是在寻找基于 class 名称的过滤。 只需分两步完成。

# Filter
dschanged <- ds1[ds1$Faults == 2,]
# Extract package name
dschanged$class <- sub('(.*)[.](.*)','\1',dschanged$class)

grep(和 grepl)不适用于此:您没有根据文本内容进行过滤。您 (a) 基于 Faults 进行过滤,并且 (b) 更改 Class.

中的文本

您的数据:

ds1 <- structure(list(Class = c("org.apache.tools.ant.taskdefs.Definer", "org.apache.tools.ant.taskdefs.Definer", "org.apache.tools.ant.taskdefs.Delete", "org.apache.tools.ant.taskdefs.Deltree", "org.apache.tools.ant.taskdefs.DependSet", "org.apache.tools.ant.taskdefs.DependSet", "org.apache.tools.ant.taskdefs.DependSet", "org.apache.tools.ant.taskdefs.Ear", "org.apache.tools.ant.taskdefs.Ear", "org.apache.tools.ant.taskdefs.Echo", "org.apache.tools.ant.Exec", "org.apache.tools.ant.Exec"),
                      Faults = c(2L, 2L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 2L, 2L)),
                 .Names = c("Class", "Faults"), class = "data.frame", row.names = c(NA, -12L))

过滤 Faults(您已经有了这个)。您只需要这两个命令之一,它们都做同样的事情;主要区别在于可读性(个人偏好)和性能(第二个,在这种情况下,花费的时间减少了大约 35%,尽管因为它们都是以微秒为单位来衡量的,所以竞争似乎很愚蠢)。

ds2 <- subset(ds1, Faults == 2)
ds2 <- ds1[ds1$Faults == 2,]

更新 Class 以删除最后一个单词(和点):

ds2$Class <- gsub("\.[^.]*$", "", ds2$Class)
ds2
#                            Class Faults
# 1  org.apache.tools.ant.taskdefs      2
# 2  org.apache.tools.ant.taskdefs      2
# 4  org.apache.tools.ant.taskdefs      2
# 5  org.apache.tools.ant.taskdefs      2
# 6  org.apache.tools.ant.taskdefs      2
# 7  org.apache.tools.ant.taskdefs      2
# 8  org.apache.tools.ant.taskdefs      2
# 9  org.apache.tools.ant.taskdefs      2
# 11          org.apache.tools.ant      2
# 12          org.apache.tools.ant      2

注意:这也可以用 sub 而不是 gsub 来完成,但后者是我的第一个目标,因为我的大部分用途都处理更大和重复的正则表达式。两者之间的主要(唯一?)区别是:

'sub' and 'gsub' perform replacement of the first and all matches respectively

(来自 ?sub)。

我知道没有工具可以在单个命令中 进行过滤和更改(尽管我不知道 data.table 可以)。

类似于@egnha 的解决方案(使用 magrittr),这是一个使用 dplyr 的解决方案,许多人声称它非常容易阅读和适应(以潜在的性能成本为代价):

library(dplyr)
ds2 <- ds1 %>%
  filter(Faults == 2) %>%
  mutate(Class = gsub("\.[^.]*$", "", Class))

既然我提到了性能,这里有一个比较:

microbenchmark(indexing = { ds2 <- ds1[ds1$Faults == 2,]; ds2$Class <- gsub("\.[^.]*$", "", ds2$Class) },
               subset   = { ds2 <- subset(ds1, Faults == 2) ; ds2$Class <- gsub("\.[^.]*$", "", ds2$Class) },
               dplyr    = { ds1 %>% filter(Faults == 2) %>% mutate(Class = gsub("\.[^.]*$", "", Class)) })
# Unit: microseconds
#      expr      min        lq      mean    median        uq      max neval
#  indexing   71.841   87.7045  109.4496  104.2975  120.7075  269.493   100
#    subset  102.473  115.6020  147.0108  139.1230  165.5620  287.726   100
#     dplyr 1067.030 1156.3745 1323.1174 1225.4805 1351.2920 4270.308   100

郑重声明,与其他方法相比,以这种方式使用的 dplyr 并不经常这么慢。它通常不会 更快 ,但通常不会慢一个数量级。