ggplot с эстетикой, сгенерированной из входных данных - PullRequest
0 голосов
/ 21 января 2019

Так как мне нужно будет сделать много разных графиков в R, я пытаюсь добавить больше логики при подготовке данных (добавить имена столбцов, соответствующие эстетике) и меньше логики в самом графике.

Рассмотрим следующий график диафрагмы по умолчанию:

library(ggplot2)
library(data.table)
scatter <- ggplot(data=iris, aes(x = Sepal.Length, y = Sepal.Width)) 
scatter + geom_point(aes(color=Species, shape=Species))

Теперь я создаю модифицированные данные радужной оболочки с именами столбцов, соответствующими желаемой эстетике:

iris2 <- as.data.table(iris)
iris2 <- iris2[,.(x=Sepal.Length, y=Sepal.Width, color=Species,
                  shape=Species)]

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

ggplot(data, aes(x=x, y=y)) + geom_point(aes(color=color, shape=shape))

Прошло много времени с тех пор, как я что-то читал о нестандартных оценках, выражениях и цитатах, и я заметил, что есть довольно много событий с rlang и цитатами ( cheatsheet ). [Этот] вопрос был своего рода полезным, но он не решил тот факт, что я хочу вывести эстетику из данных.

В конце концов, я много чего перепробовал и заглянул внутрь. Там я вижу:

exprs <- rlang::enquos(x = x, y = y, ...)

и я думаю, что это причина того, что все попытки, которые я сделал, вроде:

ggplot(iris2, aes(x=x, y=y)) +
    geom_point(aes(rlang::quo(expr(color=color))))

не сработало, так как aes пытается "навести порядок" в моих предложениях.

ВОПРОС Есть ли способ динамически передавать аргументы aes на основе содержимого данных (поэтому вы заранее не знаете, какая эстетика вам понадобится?

Если мой вопрос недостаточно ясен, в конце концов я сделал что-то, что работает, только у меня есть ощущение, что это совершенно не нужно, потому что я не знаю / не понимаю, как это сделать правильно. Таким образом, материал ниже работает, и это то, что я имею в виду, но то, что я, например. не нравится, что я должен был изменить AES:

Блок ниже автономен и может быть выполнен без кусков кода выше.

library(data.table)
library(ggplot2)
library(rlang)
iris2 <- as.data.table(iris)
iris2 <- iris2[,.(x=Sepal.Length, y=Sepal.Width, color=Species, shape=Species)]
myaes <- function (x, y, myquo=NULL, ...) {    
    exprs <- rlang::enquos(x = x, y = y, ...)    
    exprs <- c(exprs, myquo)
    is_missing <- vapply(exprs, rlang::quo_is_missing, logical(1))
    aes <- ggplot2:::new_aes(exprs[!is_missing], env = parent.frame())
    ggplot2:::rename_aes(aes)
}

generalPlot <- function(data, f=geom_point,
                        knownaes=c('color'=expr(color), 'shape'=expr(shape))){
    myquo  <- list()
    for(i in names(knownaes)){
        if(i %in% names(data)){
            l <- list(rlang::quo(!!knownaes[[i]]))
            names(l) <- i
            myquo <- c(myquo, l)
        }
    }    

    ggplot(data, aes(x=x, y=y)) +
        f(myaes(myquo=myquo))   
}

generalPlot(iris2[,.(x, y, color)])
generalPlot(iris2[,.(x, y, color, shape)])

Ответы [ 2 ]

0 голосов
/ 21 января 2019

Вы можете использовать эту пользовательскую функцию, которая анализирует имена столбцов входных данных и генерирует текстовую строку aes, которая передается eval().

generateAES <- function(foo) {
    eval(parse(text = paste0("aes(", 
        paste(
            lapply(foo, function(i) paste(i, "=", i)), 
        collapse = ","), 
        ")"
    )))
}

Вы можете использовать ее с:

ggplot(iris2, generateAES(colnames(iris2))) +
    geom_point()

Или с трубами:

library(magrittr)
iris2 %>%
    ggplot(generateAES(colnames(.))) +
        geom_point()

generateAES вывод aes как:

Aesthetic mapping: 
* `x`      -> `x`
* `y`      -> `y`
* `colour` -> `color`
* `shape`  -> `shape`

Генерируется из текстовой строки "aes(x = x,y = y,color = color,shape = shape)"

0 голосов
/ 21 января 2019

Итак, если ваши данные в виде столбца «цвет» или «форма», вы просто хотите сопоставить их с эстетикой цвета или формы?Я думаю, что более простой способ сделать это будет

generalPlot <- function(data, f=geom_point, knownaes=c('color', 'shape')) {
  match_aes <- intersect(names(data), knownaes)
  my_aes_list <- purrr::set_names(purrr::map(match_aes, rlang::sym), match_aes)
  my_aes <- rlang::eval_tidy(quo(aes(!!!my_aes_list)))
  ggplot(data, aes(x=x, y=y)) +
        f(mapping=my_aes)

}

Тогда вы можете сделать

generalPlot(iris2[,.(x, y)])
generalPlot(iris2[,.(x, y, color)])
generalPlot(iris2[,.(x, y, color, shape)])

, и для этого не требуется дополнительная функция myaes.

Я немного удивлен, что мне пришлось использовать eval_tidy, но по какой-то причине вы не можете использовать !!! с aes().

x <- list(color=sym("color"))
ggplot(iris2, aes(x,y)) + geom_point(aes(!!!x))
# Error: Can't use `!!!` at top level

(протестировано с ggplot2_3.1.0)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...