我正在尝试编写一个 R 脚本来执行以下任务。我有两个问题:
library(dplyr); library(magrittr)
(
tibs <- list(
top = tibble(
letter = c(rep("A",4),rep("B",4)),
number = c(rep(1,2), rep(2,2)) %>% rep(2),
element = c("x","y","z","w","x","y","z","w"),
score = 1:8
) %>% group_by(letter,number),
bottom = tibble(
letter = c(rep("A",2),rep("B",2)),
element = c("p","q","y","z"),
score = c(2.5,3.5, 4,5.5)
) %>% group_by(letter)
)
)
# A tibble: 8 x 4
# Groups: letter, number [4]
letter number element score
<chr> <dbl> <chr> <int>
1 A 1 x 1
2 A 1 y 2
3 A 2 z 3
4 A 2 w 4
5 B 1 x 5
6 B 1 y 6
7 B 2 z 7
8 B 2 w 8
$bottom
# A tibble: 4 x 3
# Groups: letter [2]
letter element score
<chr> <chr> <dbl>
1 A p 2.5
2 A q 3.5
3 B y 4
4 B z 5.5
对于每个组 Xn
由字母(X
=“A”或“B”)和数字(n
= 1 或 2)定义的顶级 tibble 的,我想在出现的元素中选择两个得分最低的元素在群里Xn
顶级 tibble 或组中的 X
较低级别的tibble。如果一个元素同时出现在顶层和低层tibble中,它的分数取自top
。小标题。
所以,在这个例子中,我期望得到 x,y
组A1
, p,z
组A2
, x,z
组B1
和 y,z
组B2
.
我必须对具有多达一百万个不同组(在顶级)并且每个组中有几个条目的(顶级)tibbles 执行这种操作。我想获得一个快速并且可能可读的解决方案,无论是否在 dplyr 内。
到目前为止,我的解决方案返回了预期的输出,但从效率的角度来看尤其不令人满意:
summarizer <- function(letter, element, score, bottom){
bottom %<>% filter(letter == !!letter[1], !(element %in% !!element))
order(c(score, bottom$score)) %>%
c(element, bottom$element)[.] %>%
head(2) %>%
paste0(collapse = " ")
}
tibs$top %>% summarise(preds = summarizer(letter, element, score,
tibs$bottom)
)
# A tibble: 4 x 3
# Groups: letter [2]
letter number preds
<chr> <dbl> <chr>
1 A 1 x y
2 A 2 p z
3 B 1 x z
4 B 2 y z
特别是对于大量组,最大的瓶颈是管道分配 %<>%
在我的函数中 summarizer
,但是我看不出如何避免。
我有以下与上述相关的问题:
-
dplyr
根据构造,'s group_by 组从不相交。有没有一种方法(在 dplyr 内或不在 dplyr 内)以行可以属于多个组的方式对 data.frames 进行分组? - 如果没有,我的任务可以通过创建属于更多组的元素的副本并适本地标记它们来解决。您将如何快速完成此操作?
- 您是否看到针对上述问题的任何其他快速(并且可能可读)解决方案?
最佳答案
这是一个使用 data.table
的选项。
library(data.table)
setDT(top)
setDT(bottom)
#get unique groups
g = unique(top[,.(letter, number)])
#creating duplicates for each letter in bottom for each group using a left join on letter
b = bottom[g, on=.(letter)]
#If an element appears both in the top- and lower-level tibble, it's score is taken from the top tibble.
#use an update join to lookup the scores from top tibble
b[top, on=.(letter, number, element), score := i.score]
#bind_rows and remove identical rows
rowsbind = rbindlist(list(top, b), use.names=TRUE)
both = unique(rowsbind, by=c("letter", "number", "element"))
#order and subset
setorder(both, letter, number, score)
both[rowid(letter, number) <= 2L]
数据:
library(data.table)
top = data.table(
letter = c(rep("A",4),rep("B",4)),
number = rep(c(rep(1,2), rep(2,2)), 2),
element = c("x","y","z","w","x","y","z","w"),
score = as.double(1:8)
)
bottom = data.table(
letter = c(rep("A",2),rep("B",2)),
element = c("p","q","y","z"),
score = c(2.5,3.5, 4,5.5)
)
关于r - 对具有交叉组的 tibbles 执行类似 `top_n` 的操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62677952/