Сопоставить функцию top_n с сгруппированными данными - PullRequest
2 голосов
/ 17 июня 2019

Предположим, у меня есть фрейм данных:

value = c(1:5,16:20, 26:30)
group = c(rep("A", 5), rep("B", 5), rep("C", 5))
df = data.frame(value, group)

Я хотел бы создать новый фрейм данных, который будет содержать значения top_n для каждой группы, такие что n для группы A = 3, n для группы B = 2 иn для группы C = 1.

# new dataframe should look like this:

  value group
1     5     A
2     4     A
3     3     A
4    20     B
5    19     B
6    30     C

Я думаю, мне следует сопоставить функцию top_n с моими данными, но я изо всех сил пытаюсь найти правильную реализацию.

Ответы [ 5 ]

6 голосов
/ 17 июня 2019

Вы можете использовать tail в вызове Map.

do.call(rbind, Map(tail, split(df, df$group), 3:1))
#      value group
# A.3      3     A
# A.4      4     A
# A.5      5     A
# B.9     19     B
# B.10    20     B
# C       30     C

Примечание: сортировать заранее, если данные отсортированы не так хорошо, как в данном примере,например, df <- with(df, df[order(group, value), ]).

Данные

df <- structure(list(value = c(1L, 2L, 3L, 4L, 5L, 16L, 17L, 18L, 19L, 
20L, 26L, 27L, 28L, 29L, 30L), group = structure(c(1L, 1L, 1L, 
1L, 1L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L), .Label = c("A", 
"B", "C"), class = "factor")), class = "data.frame", row.names = c(NA, 
-15L))
3 голосов
/ 17 июня 2019

Использование map и top_n

library(tidyverse)
df %>% nest(-group) %>% 
       mutate(dt = map(data, ~top_n(.x, n=.x$n[1], wt=value))) %>% 
       unnest(dt)

#Using map_df
map_df(df %>% group_split(group), ~top_n(.x, n=.x$n[1], wt=value))

# A tibble: 6 x 3
  value group     n
  <int> <chr> <dbl>
  1     3 A         3
  2     4 A         3
  3     5 A         3
  4    19 B         2
  5    20 B         2
  6    30 C         1

Данные

value = c(1:5,16:20, 26:30)
group = c(rep("A", 5), rep("B", 5), rep("C", 5))
n = c(rep(3, 5), rep(2, 5), rep(1, 5))
df = data.frame(value, group,n,stringsAsFactors = FALSE)
3 голосов
/ 17 июня 2019

Я бы предпочел добавить n в кадре данных, затем arrange и slice

library(dplyr)

df %>%
   mutate(n = case_when(group == "A"~3L, 
                        group == "B"~ 2L, 
                        TRUE ~ 1L)) %>%
   arrange(group, desc(value)) %>%
   group_by(group) %>%
   slice(seq_len(n[1L])) %>%
   select(-n)


#  value group
#  <int> <fct>
#1     5 A    
#2     4 A    
#3     3 A    
#4    20 B    
#5    19 B    
#6    30 C    
2 голосов
/ 17 июня 2019

Вы можете использовать однострочник с базой R. Я думаю, что здесь может быть сложнее с dplyr.

#split the df on group and then subset each group
mylist <- Map(function(x, y) x[order(x$value, decreasing = TRUE)[1:y], ], split(df, group), 3:1)
do.call(rbind, mylist)

#  value group
#1     5     A
#2     4     A
#3     3     A
#4    20     B
#5    19     B
#6    30     C

Поскольку вы уже используете dplyr, вы также можете использовать bind_rows:

bind_rows(Map(function(x, y) x[order(x$value, decreasing = TRUE)[1:y], ], split(df, group), 3:1))
2 голосов
/ 17 июня 2019

Вот реализация с {dplyr}> = 0.8 & {purrr}:

value = c(1:5,16:20, 26:30)
group = c(rep("A", 5), rep("B", 5), rep("C", 5))
df = data.frame(value, group)
library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
library(purrr)
df %>% 
  group_by(group) %>%
  group_split() %>%
  map2_df(., length(.):1, ~ top_n(.x, .y, value) %>% arrange(desc(value)))
#> # A tibble: 6 x 2
#>   value group
#>   <int> <fct>
#> 1     5 A    
#> 2     4 A    
#> 3     3 A    
#> 4    20 B    
#> 5    19 B    
#> 6    30 C

Обратите внимание, что top_n не упорядочивает данные, поэтому вам нужно объединить top_n() и arrange().

Еще одно предложение в базе R:

x <- df %>%
  split(df$group)
mapply(function(x, y){
  top_n(x, y, value)
}, x = x, y = length(x):1, SIMPLIFY = FALSE) %>%
  do.call(rbind, .)
    value group
A.1     3     A
A.2     4     A
A.3     5     A
B.1    19     B
B.2    20     B
C      30     C
...