R:改进嵌套的ifelse语句&多种模式

时间:2016-08-30 01:17:10

标签: r if-statement pattern-matching grepl

我正在继续研究一些动物收容所数据的数据清理实践。我的目标是减少品种类别的数量。

我将每个品种类别用作outgoing$Single.Breed数据框列的部分模式匹配。因此,有些情况下品种只是Chihuahua,但也可能是Long Hair Chihuahua。 (因此,我使用grepl。)因此,包含品种类别的任何内容都将在所述类别的不同列中表示。此外,我还需要添加 cat 品种类别......制作更加混乱的代码。

以下代码是我的"解决方案" ,但它非常笨重。是否有更好,更光滑和/或更有效的方法来实现这一目标?

BreedCategories <- ifelse(outgoing$New.Type == "Dog",
           ifelse(grepl("Chihuahua",outgoing$Single.Breed, ignore.case = TRUE), "Chihuahua",
           ifelse(grepl("Pit Bull",outgoing$Single.Breed, ignore.case = TRUE), "Pit Bull",
           ifelse(grepl("Terrier",outgoing$Single.Breed, ignore.case = TRUE), "Terrier",
           ifelse(grepl("Shepherd",outgoing$Single.Breed, ignore.case = TRUE), "Shepherd",
           ifelse(grepl("Poodle",outgoing$Single.Breed, ignore.case = TRUE), "Poodle",
           ifelse(grepl("Labrador|Retriever",outgoing$Single.Breed, ignore.case = TRUE),"Labrador",
           "Other")))))),"Cat")

1 个答案:

答案 0 :(得分:4)

创建一个data.frame,它在正则表达式和品种

之间进行映射
map <- data.frame(
    pattern=c(
        "Chihuahua", "Pit Bull", "Terrier", "Shepherd",
        "Poodle", "Labrador|Retriever", "Other"),
    isa=c(
        "Chihuahua", "Pit Bull", "Terrier", "Shepherd",
        "Poodle", "Labrador", "Other"),
    stringsAsFactors=FALSE)

和一些数据

outgoing <- data.frame(Single.Breed=c(map$isa, "Pit Bull Poodle", "Pug"),
                       stringsAsFactors=FALSE)

对于该计划,使用vapply()grepl()将每种模式与数据相匹配; grepl()的使用意味着结果是一个矩阵,行对应于每个条目

isa <- vapply(map$pattern, grepl, logical(nrow(outgoing)), outgoing$Single.Breed)
if (any(rowSums(isa) > 1))
    warning("ambiguous breeds: ", outgoing$Single.Breed[rowSums(isa) != 1])

使用max.col()访问每一行并检索最佳(最后)匹配(恰好是&#39;其他&#39;如果没有匹配)。

outgoing$BreedCategory <- map$isa[max.col(isa, "last")]

这是结果

> isa <- vapply(map$pattern, grepl, logical(nrow(outgoing)), outgoing$Single.Breed)
> if (any(rowSums(isa) > 1))
+     warning("ambiguous breeds: ", outgoing$Single.Breed[rowSums(isa) != 1])
Warning message:
ambiguous breeds: Pit Bull Poodle 
> outgoing$BreedCategory <- map$isa[max.col(isa, "last")]
> outgoing
     Single.Breed BreedCategory
1       Chihuahua     Chihuahua
2        Pit Bull      Pit Bull
3         Terrier       Terrier
4        Shepherd      Shepherd
5          Poodle        Poodle
6        Labrador      Labrador
7           Other         Other
8 Pit Bull Poodle        Poodle
9             Pug         Other

我认为这种方法很有吸引力,因为它更清楚地区分了数据&#39;来自“程序”的(正则表达式和输入品种) (grepl()max.col())。

处理&#39;其他&#39;看起来有点脆弱 - 如果你忘了它应该是map的最后一个元素怎么办?一种可能性是创建一个指示变量来测试isa的行总和,并使用它来有条件地分配品种

test = rowSums(isa)
outgoing$BreedCategory[test == 0] = "Other"
outgoing$BreedCategory[test == 1] = map$isa[max.col(isa)][test == 1]
outgoing$BreedCategory[test > 1] = "Mixed"

上面的空间效率不是很高(矩阵将长度为n的数据转换为n x#的正则表达矩阵),但似乎可以完成1M输入行的工作。

dplyr::case_when()似乎要求您编写许多grepl()语句,这很容易出错。