R:改进嵌套 ifelse 语句和多种模式
R: Improving Nested ifelse Statements & Multiple Patterns
我将继续使用一些动物收容所数据进行一些数据清理实践。我的目标是减少品种类别的数量。
我将每个品种类别用作与 outgoing$Single.Breed
数据框列的 部分 模式匹配。因此,在某些情况下品种只是 Chihuahua
,但也可能是 Long Hair Chihuahua
。 (因此,我使用 grepl
。)因此,任何包含品种类别的内容都将在该类别的不同列中表示。此外,我还需要添加 cat 品种类别...使代码更加混乱。
下面的代码是我的"solution",但它很笨拙。是否有更好、更灵活 and/or 更有效的方法来完成此任务?
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")
创建一个 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()
访问每一行并检索最佳(最后)匹配(如果没有匹配则恰好是 'Other')。
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
我想这种方法很有吸引力,因为它更清楚地将 'data'(正则表达式和输入品种)与 'program'(grepl()
和 max.col()
)区分开来。
'Other' 的处理似乎有点脆弱——如果您忘记它应该是 map
的最后一个元素怎么办?一种可能性是创建一个指示变量来测试 isa 的行总和,并使用它来有条件地分配 breed
test = rowSums(isa)
outgoing$BreedCategory[test == 0] = "Other"
outgoing$BreedCategory[test == 1] = map$isa[max.col(isa)][test == 1]
outgoing$BreedCategory[test > 1] = "Mixed"
上面的方法 space 效率不高(矩阵将长度为 n 的数据转换为正则表达式矩阵的 n x #),但似乎可以完成 1M 输入行的工作。
dplyr::case_when()
好像要写很多grepl()
语句,容易出错
我将继续使用一些动物收容所数据进行一些数据清理实践。我的目标是减少品种类别的数量。
我将每个品种类别用作与 outgoing$Single.Breed
数据框列的 部分 模式匹配。因此,在某些情况下品种只是 Chihuahua
,但也可能是 Long Hair Chihuahua
。 (因此,我使用 grepl
。)因此,任何包含品种类别的内容都将在该类别的不同列中表示。此外,我还需要添加 cat 品种类别...使代码更加混乱。
下面的代码是我的"solution",但它很笨拙。是否有更好、更灵活 and/or 更有效的方法来完成此任务?
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")
创建一个 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()
访问每一行并检索最佳(最后)匹配(如果没有匹配则恰好是 'Other')。
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
我想这种方法很有吸引力,因为它更清楚地将 'data'(正则表达式和输入品种)与 'program'(grepl()
和 max.col()
)区分开来。
'Other' 的处理似乎有点脆弱——如果您忘记它应该是 map
的最后一个元素怎么办?一种可能性是创建一个指示变量来测试 isa 的行总和,并使用它来有条件地分配 breed
test = rowSums(isa)
outgoing$BreedCategory[test == 0] = "Other"
outgoing$BreedCategory[test == 1] = map$isa[max.col(isa)][test == 1]
outgoing$BreedCategory[test > 1] = "Mixed"
上面的方法 space 效率不高(矩阵将长度为 n 的数据转换为正则表达式矩阵的 n x #),但似乎可以完成 1M 输入行的工作。
dplyr::case_when()
好像要写很多grepl()
语句,容易出错