Извлечение атрибута столбца внутри функции

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

df1 <- structure(list(var1 = structure(c("Didn't do a thing", "Almost did a thing", 
"Once did a thing", "Have never done a thing", "Always do a thing"
), description = "This is the question i asked respondents (and the title of the plot)"), 
    wtd_pct = c(4L, 15L, 62L, 11L, 8L)), row.names = c(NA, -5L
), class = c("tbl_df", "tbl", "data.frame"))

Я хочу создать функцию построения графика, которая принимает в качестве входных данных имя тиббла (df1) и имя столбца внутри него (в данном случае есть только var1, но в моем реальном тиббле у меня гораздо больше столбцов).

Внутри функции сюжета я хочу извлечь атрибут, связанный с var1, и сделать его заголовком сюжета. Например, вне функции это выглядит так:

df1 %>% 
  ggplot(aes(y = var1, x = wtd_pct)) +
  geom_col(aes(fill = var1)) +
  geom_text(aes(label = paste0(round(wtd_pct, 0), "%")), size = 3.5, vjust = -.5, hjust = -.3, color = 'black') +
  theme_minimal() + theme(legend.position = "none") +
  labs(y = "", 
       x = "Weighted percent", 
       title = paste0("\"", str_wrap(attr(df1$var1, "description"), 100), "\""))

Обратите внимание на строку title выше. Однако когда я помещаю это в функцию и пытаюсь вызвать ее, я получаю всевозможные ошибки. Например.

plot_function <- function(.x, .y){
.x %>% 
  ggplot(aes(y = {{.y}}, x = wtd_pct)) +
  geom_col(aes(fill = {{.y}})) +
  geom_text(aes(label = paste0(round(wtd_pct, 0), "%")), size = 3.5, vjust = -.5, hjust = -.3, color = 'black') +
  theme_minimal() + theme(legend.position = "none") +
  labs(y = "", 
       x = "Weighted percent", 
       title = paste0("\"", str_wrap(attr({{.x$.y}}, "description"), 100), "\""))
}

plot_function(df1, var1)

Это возвращает сюжет, но без заголовка + ошибку Warning message: Unknown or uninitialised column: .y.. Я пробовал разные другие вещи (обертывание !!ensym(), .data[[]], предварительное извлечение атрибута в отдельную строку и т. д., но так и не получил того, что хочу.

Кажется, суть проблемы в том, что вы не можете передать df в attr(), но ему также не нравится синтаксис .x$.y. Может ли кто-нибудь здесь указать мне в правильном направлении?

Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
62
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Оператор $ использует нестандартную оценку без подстановки, поэтому .x$.y будет интерпретироваться как «столбец с именем .y внутри df1», которого, конечно, не существует.

Обычный способ обойти это — использовать [[ вместо $, но здесь это немного сложнее, потому что вы хотите передать имя столбца без кавычек.

Один из вариантов — использовать .x[[deparse(substitute(.y))]] (без фигурных операторов)

plot_function <- function(.x, .y){
  .x %>% 
    ggplot(aes(y = {{.y}}, x = wtd_pct)) +
    geom_col(aes(fill = {{.y}})) +
    geom_text(aes(label = paste0(round(wtd_pct, 0), "%")), 
              size = 3.5, vjust = -.5, hjust = -.3, color = 'black') +
    theme_minimal() + theme(legend.position = "none") +
    labs(y = "", 
         x = "Weighted percent", 
         title = paste0("\"", str_wrap(attr(.x[[deparse(substitute(.y))]],
                                            "description"), 100), "\""))
}

plot_function(df1, var1)

Чистым эквивалентом tidyverse может быть использование чего-то вроде

rlang::eval_tidy(enquo(.y), data = .x)

вместо .x[[deparse(substitute(.y))]]

Замечательный Аллан. Я бы никогда не догадался об этом самостоятельно (я могу подтвердить, что это работает для моего варианта использования, и сейчас я нахожусь в кроличьей норе, ища deparse и substitute). Спасибо!

C.Robin 15.09.2023 13:42

Используйте потянуть, заменив {{.x$.y}} на pull(.x, {{.y}}), как показано в строке, отмеченной ## ниже. Также обратите внимание, что все утверждения library должны быть предоставлены при публикации в SO.

library(dplyr)
library(ggplot2)
library(stringr)

plot_function <- function(.x, .y) {
  .x %>% 
    ggplot(aes(y = {{.y}}, x = wtd_pct)) +
      geom_col(aes(fill = {{.y}})) +
      geom_text(aes(label = paste0(round(wtd_pct, 0), "%")), size = 3.5, 
        vjust = -.5, hjust = -.3, color = 'black') +
      theme_minimal() + theme(legend.position = "none") +
      labs(y = "", 
        x = "Weighted percent", 
        title = paste0(
          "\"", 
          str_wrap(attr(pull(.x, {{.y}}), "description"), 100), ##
          "\"")
      ) 
}

plot_function(df1, var1)

или мы можем переписать часть title= как конвейер и перейти к sprintf, чтобы ее было немного легче читать.

plot_function <- function(.x, .y) {
  .x %>% 
    ggplot(aes(y = {{.y}}, x = wtd_pct)) +
      geom_col(aes(fill = {{.y}})) +
      geom_text(aes(label = paste0(round(wtd_pct, 0), "%")), size = 3.5, 
        vjust = -.5, hjust = -.3, color = 'black') +
      theme_minimal() + theme(legend.position = "none") +
      labs(y = "", 
        x = "Weighted percent", 
        title = pull(.x, {{.y}}) %>%          ##
                  attr("description") %>%     ##
                  str_wrap(100) %>%           ##
                  sprintf('"%s"', .)          ##


      ) 
}

plot_function(df1, var1)

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