Подстрока {.col} из mutate(across()) на лету

Скажем, у меня есть следующие данные:

df <- structure(list(treat = structure(1:4, levels = c("Control", "Treatment 1", 
"Treatment 2", "Treatment 3"), class = "factor"), 
    female_n = c(314709L, 10456L, 10481L, 10455L), female_mean = c(0.506, 
    0.506, 0.504, 0.5), female_sd = c(0.5, 0.5, 0.5, 0.5), birth_year_n = c(314709L, 
    10456L, 10481L, 10455L), birth_year_mean = c(1973.74, 1973.654, 
    1973.486, 1973.766), birth_year_sd = c(16.867, 16.997, 16.869, 
    16.89), provided_phone_no_n = c(314709L, 10456L, 10481L, 
    10455L), provided_phone_no_mean = c(0.656, 0.666, 0.663, 
    0.647), provided_phone_no_sd = c(0.475, 0.472, 0.473, 0.478
    ), dem_n = c(314709L, 10456L, 10481L, 10455L), dem_mean = c(0.48, 
    0.474, 0.482, 0.478), dem_sd = c(0.5, 0.499, 0.5, 0.5), rep_n = c(314709L, 
    10456L, 10481L, 10455L), rep_mean = c(0.136, 0.141, 0.142, 
    0.138), rep_sd = c(0.343, 0.348, 0.349, 0.345), uaf_n = c(314709L, 
    10456L, 10481L, 10455L), uaf_mean = c(0.363, 0.365, 0.357, 
    0.363), uaf_sd = c(0.481, 0.481, 0.479, 0.481)), class = c("tbl_df", 
"tbl", "data.frame"), row.names = c(NA, -4L))

Я хочу добавить новый столбец *_se, который принимает в качестве входных данных существующие столбцы *_n и *_sd для каждой группы переменных в моих данных. Т.е. по одному на каждый из female_*, birth_year_*, provided_phone_no_*, dem_*, rep_* и uaf_*.

Пытаясь сделать это, я думаю, что mutate(across()), вероятно, является подходящей вспомогательной функцией, но у меня возникли некоторые проблемы с подстрокой {.col} и получением R распознать ее как имя столбца. Это моя попытка:

df %>%
    mutate(
    across(ends_with("_sd"),
           list(
            se = ~.x / sqrt(!!ensym("{str_replace(.col, '_sd', '_n')}"))
           )
    )

Вышеупомянутое возвращает ошибку:

Error in `ensym()`:
! `arg` must be a symbol
Backtrace:
  1. ... %>% ...
 10. rlang::abort(message = message)

Может ли кто-нибудь увидеть, где я ошибаюсь?

Ваша проблема в том, что вы используете синтаксис стиля клея в обычной строке. Выполнение команды "{str_replace(.col, '_sd', '_n')}" вернет... "{str_replace(.col, '_sd', '_n')}", и вы передаете это ensym(). Вам нужно сделать что-то вроде glue::glue("{str_replace(.col, '_sd', '_n')}") или stringr::str_glue("{str_replace(.col, '_sd', '_n')}"). str_glue() — это просто оболочка вокруг glue::glue().

LMc 12.06.2024 23:23

Спасибо @LMc. У меня было ощущение, что я ошибаюсь; я думал, что ensym() говорил R, что это действительно переменная, поэтому, пожалуйста, оцените ее и относитесь к ней как к таковой. Я только что опробовал подход glue и получил сообщение, похожее на другие попытки, но я собираюсь использовать подход Pivot_longer / более широкий, которым вы поделились ниже.

C.Robin 12.06.2024 23:31

Не используйте ensym() используйте sym()! Кроме того, внутри вашей поперечной функции нет .col, характерного для аргумента .names. Используйте cur_column(), чтобы получить имя столбца.

LMc 12.06.2024 23:32

Это всего лишь советы, которые помогут понять некоторые проблемы вашего кода. В целом я не думаю, что удастся ввести имя столбца так, как вы пытаетесь.

LMc 12.06.2024 23:36

Я это понимаю (и ценю). Я не собираюсь продвигать этот подход, но мне очень хочется понять механику/точку отказа – подозреваю, что этому можно научиться в другой раз.

C.Robin 12.06.2024 23:37
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
5
53
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это действительно идеальная ситуация для перевода ваших данных в длинный формат, а затем обратно в широкий формат:

library(tidyr)
library(dplyr)

df |>
  pivot_longer(cols = -treat, 
               names_pattern = "(.*)_(.*)", 
               names_to = c("grp", ".value")) |>
  mutate(se = sd / sqrt(n)) |>
  pivot_wider(names_from = grp, 
              values_from = n:se, 
              names_glue = "{grp}_{.value}", 
              names_vary = "slowest")
#> # A tibble: 4 × 25
#>   treat    female_n female_mean female_sd female_se birth_year_n birth_year_mean
#>   <fct>       <int>       <dbl>     <dbl>     <dbl>        <int>           <dbl>
#> 1 Control    314709       0.506       0.5  0.000891       314709           1974.
#> 2 Alexand…    10456       0.506       0.5  0.00489         10456           1974.
#> 3 Politic…    10481       0.504       0.5  0.00488         10481           1973.
#> 4 Mark yo…    10455       0.5         0.5  0.00489         10455           1974.
#> # ℹ 18 more variables: birth_year_sd <dbl>, birth_year_se <dbl>,
#> #   provided_phone_no_n <int>, provided_phone_no_mean <dbl>,
#> #   provided_phone_no_sd <dbl>, provided_phone_no_se <dbl>, dem_n <int>,
#> #   dem_mean <dbl>, dem_sd <dbl>, dem_se <dbl>, rep_n <int>, rep_mean <dbl>,
#> #   rep_sd <dbl>, rep_se <dbl>, uaf_n <int>, uaf_mean <dbl>, uaf_sd <dbl>,
#> #   uaf_se <dbl>

Если вы действительно хотели сделать это в широком формате, вот одно из возможных решений:

library(dplyr)
library(stringr)

df |> 
  mutate(across(ends_with("_sd"), \(x) {
    x / sqrt(pick(all_of(str_replace(cur_column(), "_sd", "_n")))[[1]])
  }, 
  .names = "{str_remove(.col, '_sd')}_se"
  ))

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