R 这个迭代是否可以用整洁的函数方式编写
数据有一个名为description of type character的列和一个由行\号设置的integer类型的列id data_map有一个character类型的列名desc_map和一个按行数设置的整型列id data和data_map在联接后的进一步处理中使用了其他列 下面代码的思想是使用data_map$desc_map作为str_detect中的模式来匹配data$description。在一个匹配中,它将使用data$id和data_-map$id向另一个tible添加一行。结果匹配允许将data和data_-map连接在一起 我的问题是,这段代码是否可以用一种更整洁的功能性方式编写,它会是什么样子?如果不能这样做,原因是什么?更新 根据你的最新问题,这里是我答案的最新版本 这次我只是按原样使用您的输入,没有创建命名函数。相反,我把所有的东西都放在一个管道里。找到的列应该指出找到一个模式的次数,因此您不应该需要不同的对象,因为它们不是唯一的、匹配的、匹配的 我在你的问题的评论中从GenesRus那里学到了创建列表列并取消嵌套的想法,但我没有进一步使用spread/pivot wider,而是选择map2在description和desc_map列上循环 旧答案 下面是一个基于tidyverse的方法,应该会产生相同的结果。”“应该”,因为我只能猜测您的输入数据和预期结果是什么样子。注意:1我选择普通字符向量作为输入。行ID是动态生成的。2我将您的方法放入一个名为match_tbl的函数中。3我将tidyverse函数与管道操作符结合使用。这使得整个方法易于阅读,外观看起来“整洁”。但是,当您查看tidyverse软件包的实际函数时,您会发现作者通常不在函数中使用管道操作符,因为它很容易抛出错误。在管道操作中使用RStudio调试器,并尝试深入了解正在发生的事情,您将看到它非常混乱。因此,如果您想从中生成一个真正稳定的函数,请删除管道并使用中间变量 数据和包 生成所需输出的函数:匹配表 函数调用和输出 更新 根据你的最新问题,这里是我答案的最新版本 这次我只是按原样使用您的输入,没有创建命名函数。相反,我把所有的东西都放在一个管道里。找到的列应该指出找到一个模式的次数,因此您不应该需要不同的对象,因为它们不是唯一的、匹配的、匹配的 我在你的问题的评论中从GenesRus那里学到了创建列表列并取消嵌套的想法,但我没有进一步使用spread/pivot wider,而是选择map2在description和desc_map列上循环 旧答案 下面是一个基于tidyverse的方法,应该会产生相同的结果。”“应该”,因为我只能猜测您的输入数据和预期结果是什么样子。注意:1我选择普通字符向量作为输入。行ID是动态生成的。2我将您的方法放入一个名为match_tbl的函数中。3我将tidyverse函数与管道操作符结合使用。这使得整个方法易于阅读,外观看起来“整洁”。但是,当您查看tidyverse软件包的实际函数时,您会发现作者通常不在函数中使用管道操作符,因为它很容易抛出错误。在管道操作中使用RStudio调试器,并尝试深入了解正在发生的事情,您将看到它非常混乱。因此,如果您想从中生成一个真正稳定的函数,请删除管道并使用中间变量 数据和包 生成所需输出的函数:匹配表 函数调用和输出R 这个迭代是否可以用整洁的函数方式编写,r,dplyr,tidyverse,stringr,tibble,R,Dplyr,Tidyverse,Stringr,Tibble,数据有一个名为description of type character的列和一个由行\号设置的integer类型的列id data_map有一个character类型的列名desc_map和一个按行数设置的整型列id data和data_map在联接后的进一步处理中使用了其他列 下面代码的思想是使用data_map$desc_map作为str_detect中的模式来匹配data$description。在一个匹配中,它将使用data$id和data_-map$id向另一个tible添加一行。结
您能否提供一些简单的示例数据来了解输入和输出?您可以使用和包快速创建一个可复制的示例,以便其他人能够提供帮助。请不要使用str、head或屏幕截图。另请参阅&从代码中不清楚事务[j]来自何处。如果您有足够的RAM,则只需获取字符串的唯一向量data\u map$desc\u map,将其作为数据中的一个新列进行修改,将其放入列表中,unnest,为每个添加一个运行str\u detect on data$description的布尔列,spread/pivot\u,并根据需要进行过滤。如果您确实关心数据映射中的ID,则可以在任意点启用该属性,假设ID-desc映射是唯一的,或者只使用TIBLE而不是带有初始变异的向量。@TimTeaFan我有时间更新代码e
以董的反馈为例。你能提供一些简单的示例数据来了解输入和输出吗?你可以使用和包快速创建一个可复制的示例,以便其他人可以提供帮助。请不要使用str、head或屏幕截图。另请参阅&从代码中不清楚事务[j]来自何处。如果您有足够的RAM,则只需获取字符串的唯一向量data\u map$desc\u map,将其作为数据中的一个新列进行修改,将其放入列表中,unnest,为每个添加一个运行str\u detect on data$description的布尔列,spread/pivot\u,并根据需要进行过滤。如果你真的关心data\u map中的ID,你可以在任何时候打开它,假设ID-desc\u map映射是唯一的,或者只使用TIBLE而不是带有初始变异的向量。@TimTeaFan我有时间用Tung的反馈更新代码示例。我很惊讶,谢谢你。顺便说一句,我认为你的代码中有一个bug。当它在第二次变异中说map_id=ifelsefound==F,NA,desc_map时,我相信应该是map_id而不是desc_map。是的,映射是更好的方法!没有理由在不需要的地方添加额外的代码行:我很惊讶,谢谢你。顺便说一句,我认为你的代码中有一个bug。当它在第二次变异中说map_id=ifelsefound==F,NA,desc_map时,我相信应该是map_id而不是desc_map。是的,映射是更好的方法!没有理由在不需要的地方添加额外的代码行:
library(tidyverse)
data = tribble(
~description,
"19ABB123456",
"19BCC123456",
"19CDD123456",
"19DEE123456",
"19EFF456789",
"19FF0056789",
"19A0A123456",
) %>% mutate(id = row_number())
data_map = tribble(
~desc_map,
"AA",
"BB",
"CC",
"DD",
"EE",
"FF",
"00",
) %>% mutate(id = row_number())
seq_along_rows <- function(.data) {
seq_len(nrow(.data))
}
matches <- data %>% (function (tbl) {
m <- tibble(
row_id = integer(),
map_id = integer()
)
for (i in seq_along_rows(tbl)) {
row <- tbl[i, ]
key <- row[["description"]]
found <- FALSE
for (j in seq_along_rows(data_map)) {
map_row <- data_map[j, ]
pattern <- map_row[["desc_map"]]
if (str_detect(key, pattern)) {
m <- add_row(m, row_id = row[["id"]], map_id = map_row[["id"]])
found <- TRUE
# allow for finding more than one match
}
}
if (!found) {
m <- add_row(m, row_id = row[["id"]], map_id = NA)
}
}
return(m)
})
not_unique <- matches %>%
group_by(row_id) %>%
filter(n() > 1) %>%
ungroup() %>%
inner_join(data, by = c("row_id" = "id")) %>%
inner_join(data_map, by = c("map_id" = "id"))
head(not_unique)
#> # A tibble: 2 x 4
#> row_id map_id description desc_map
#> <int> <int> <chr> <chr>
#> 1 6 6 19FF0056789 FF
#> 2 6 7 19FF0056789 00
matches_not_found <- matches %>%
filter(is.na(map_id)) %>%
select(-map_id) %>%
inner_join(data, by = c("row_id" = "id"))
head(matches_not_found)
#> # A tibble: 1 x 2
#> row_id description
#> <int> <chr>
#> 1 7 19A0A123456
matches_found <- matches %>%
filter(!is.na(map_id)) %>%
inner_join(data, by = c("row_id" = "id")) %>%
inner_join(data_map, by = c("map_id" = "id"))
head(matches_found)
#> # A tibble: 6 x 4
#> row_id map_id description desc_map
#> <int> <int> <chr> <chr>
#> 1 1 2 19ABB123456 BB
#> 2 2 3 19BCC123456 CC
#> 3 3 4 19CDD123456 DD
#> 4 4 5 19DEE123456 EE
#> 5 5 6 19EFF456789 FF
#> 6 6 6 19FF0056789 FF
library(tidyverse)
data %>%
mutate(pattern = list(data_map)) %>%
unnest %>%
rename(row_id = "id", map_id = "id1") %>%
mutate(v = map2_lgl(description, desc_map,
~ str_detect(.x, .y))) %>%
group_by(row_id) %>%
mutate(found = sum(v),
desc_map = ifelse(found == F, NA, desc_map),
map_id = ifelse(found == F, NA, map_id)) %>%
filter(v == T | (v == F & found == 0)) %>%
distinct %>%
select(-v)
library(tidyverse)
# some description data (not a dataframe but a normal char vector)
description <- c("This is a text description",
"Some words that won't match",
"Some random text goes here",
"and some more explanation here")
# patterns that we want to find (not a dataframe but a normal char vector)
pattern <- c("explanation","description", "text")
# a function which replaces your nested for loop
match_tbl <- function(.string, .pattern) {
res <- imap(.pattern,
~ stringr::str_detect(.string, .x) %>%
tibble::enframe(name = "row_id") %>%
dplyr::mutate(map_id = .y) %>%
dplyr::filter(value == T) %>%
dplyr::select(-"value"))
string_tbl <- .string %>%
tibble::enframe(name = "id") %>%
dplyr::select("id")
dplyr::bind_rows(res) %>%
dplyr::right_join(string_tbl, by = c("row_id" = "id"))
}
match_tbl(description, pattern)
> row_id map_id
> <int> <int>
> 1 1 2
> 2 1 3
> 3 2 NA
> 4 3 3
> 5 4 1