Кратко назначьте векторный вывод функции нескольким переменным в dplyr

Я пытаюсь назначить векторный вывод (т.е. больше длины 1) функции нескольким столбцам в одной операции (или, по крайней мере, как можно более кратко).

Возьмем, к примеру, функцию range(), которая возвращает числовой вектор длины 2, обозначающий минимум и максимум соответственно. Допустим, я хочу вычислить range() для каждой группы и назначить результат двум столбцам min и max.

Мой текущий подход заключается в объединении summarize с последующим добавлением ключа вручную, а затем изменением формы до широкого формата:

library(magrittr)

# create data
df <- dplyr::tibble(group = rep(letters[1:3], each = 3),
                    x = rpois(9, 10))

df
#> # A tibble: 9 x 2
#>   group     x
#>   <chr> <int>
#> 1 a         8
#> 2 a        12
#> 3 a         8
#> 4 b         9
#> 5 b        14
#> 6 b         9
#> 7 c        11
#> 8 c         6
#> 9 c        12

# summarize gives two lines per group
range_df <- df %>% 
  dplyr::group_by(group) %>% 
  dplyr::summarize(range = range(x)) %>% 
  dplyr::ungroup()

range_df
#> # A tibble: 6 x 2
#>   group range
#>   <chr> <int>
#> 1 a         8
#> 2 a        12
#> 3 b         9
#> 4 b        14
#> 5 c         6
#> 6 c        12

# add key and reshape
range_df %>% 
  dplyr::mutate(key = rep(c("min", "max"), 3)) %>% 
  tidyr::pivot_wider(names_from = key, values_from = range)
#> # A tibble: 3 x 3
#>   group   min   max
#>   <chr> <int> <int>
#> 1 a         8    12
#> 2 b         9    14
#> 3 c         6    12

Есть ли более элегантная/краткая альтернатива этому?

Редактировать:

В идеале альтернативное решение могло бы обрабатывать произвольное количество выходных данных (например, если функция возвращает выходные данные длиной 3, необходимо создать 3 переменные).

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
0
67
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

set.seed(1)

df <- dplyr::tibble(group = rep(letters[1:3], each = 3),
                    x = rpois(9, 10)) 

функция

g <- function(x){
      data.frame(min = min(x), max = max(x))
    }

звоню г:

df %>%
  group_by(group) %>%
  summarise(across(x, g,  .unpack = TRUE))

ОП просил решение, в котором вывод функции является вектором. Разве вы не просто заменили функцию ввода (диапазон -> мин и макс)?

Jakob Gepp 10.02.2023 17:41

Вопрос в том, как получить вывод более коротким скриптом! - перекройка широкого формата

SALAR 10.02.2023 17:53

Главный вопрос: «Я пытаюсь назначить векторный вывод (т.е. больше длины 1) функции нескольким столбцам в одной операции (или, по крайней мере, как можно более кратко)». чтобы не сокращать этот пример.

Jakob Gepp 10.02.2023 17:56

отредактировано!, до сих пор непонятно, зачем создавать функцию, если она уже существует в tidyverse. Кстати, большое спасибо за ваше уведомление.

SALAR 10.02.2023 19:05
# Writw a small function that does the job:

library(tidyverse)
f <- function(x){
  setNames(data.frame(t(range(x))), c('min', 'max'))
}

df %>%
  summarise(across(x, f, .unpack = TRUE), .by=group)
#> # A tibble: 3 × 3
#>   group x_min x_max
#>   <chr> <int> <int>
#> 1 a        10    13
#> 2 b         7    10
#> 3 c        10    12

Если вы используете более старую версию dplyr

df %>%
  group_by(group)%>%
  summarise(across(x, f))%>%
  unpack(x)
#> # A tibble: 3 × 3
#>   group   min   max
#>   <chr> <int> <int>
#> 1 a         6     9
#> 2 b         7    12
#> 3 c         6    10

Обратите внимание: если вы не хотите распаковывать, вы можете использовать функцию do после group_by. то есть df %>%group_by(group)%>%do(f(.$x))

onyambu 10.02.2023 18:06
Ответ принят как подходящий

Основываясь на ответе onyambu, я создаю для этого небольшую общую функцию. Вероятно, будут некоторые крайние случаи, когда это не сработает.

out2col <- function(x, fun, out_names = c(), add_args = list()) {
    tmp <- do.call(what = fun, args = c(list(x), add_args))
    out <- data.frame(t(tmp))
    if (length(out_names) != 0) {
      if (length(tmp) != length(out_names)) {
        stop("provided names did not match the number of outputs")
      }
      out <- setNames(object = out, nm = out_names)
    } 
    return(out)
}

Примеры без дополнительных параметров:

df %>%
  summarise(across(x, out2col, .unpack = TRUE, fun = range),
        .by=group)

Выход:

# A tibble: 3 × 3
  group  x_X1  x_X2
  <chr> <int> <int>
1 a         7    10
2 b        11    14
3 c         9    14

Примеры с дополнительными параметрами:

df %>%
  summarise(across(x, out2col, .unpack = TRUE, fun = quantile,
                   out_names = c("min", "max", "Q25"),
                   add_args = list(probs = c(0, 1, 0.25))
                   ),
            .by=group)

Выход:

# A tibble: 3 × 4
  group x_min x_max x_Q25
  <chr> <dbl> <dbl> <dbl>
1 a         7    10   7.5
2 b        11    14  11.5
3 c         9    14  10

Другие вопросы по теме