Редактировать Примечание. Мой исходный минимальный рабочий пример не совсем точно отражал мой вопрос. Были опубликованы два ответа, которые являются отличными ссылками, но были введены в заблуждение моим примером. Для получения дополнительной информации см. Мой комментарий к ответу r2evan. С тех пор я отредактировал пример. Спасибо!
Недавно я оказался в ситуации, когда захотел объединить два объекта ggplot. plot1
и plot2
имели одинаковые значения x
, и я просто хотел взять значения y
из plot2
и добавить их в основу plot1
.
Это очень просто, поэтому я предположил, что нечто подобное уже существовало, но ничего не смог найти. Учитывая, насколько хорошо разработан R, я предполагаю, что либо (1) это существует, и я просто не нашел его, либо (2) то, что я делаю, - это плохая практика/стиль. Не могли бы вы сообщить мне, какой именно, и если последний, то почему мне следует этого избегать?
(Реальная ситуация: у меня есть метод в пакете, который возвращает хорошо отформатированный объект ggplot. Я хочу запустить этот метод с кучей разных объектов, но объединить вывод в один график. Я бы использовал эту вспомогательную функцию для накопления всех участков в один)
Минимальный рабочий пример
Ладно, я полагаю, это уже не полностью рабочий пример. Предположим, что класс «mysteryObject» был определен, и метод plotMystery
работает с этими объектами и создает фрейм данных с постоянными значениями x, но разными значениями y на основе поля id
.
object1 <- new("mysteryObject", id=1)
plot1 <- plotMystery(object1)
plot1
object2 <- new("mysteryObject", id=2)
plot2 <- plotMystery(object2)
plot2
combine_plots <- function(ggplot1, ggplot2, color = black) {
return(
ggplot1 +
geom_line(aes(x = ggplot2$data[,1], y = ggplot2$data[,2]), color = color)
)
}
plot_combined <- combine_plots(plot1, plot2, color = "red")
plot_combined
Объедините данные, затем перейдите к длинному формату, что можно сделать с помощью tidyverse
:
library(tidyverse)
data <- data.frame(x = c(1,2,3,4,5), y = c(1,2,3,4,5))
data2 <- data.frame(x = c(1,2,3,4,5), y = c(1,2,9,16,25))
data %>%
full_join(data2, by = "x") %>%
gather(key, value, -x) %>%
ggplot(aes(x = x, y = value, group = key, color = key)) +
geom_line()
Обновлено, чтобы использовать full_join
вместо right_join
. Это должно сработать.
Да, и вы также столкнетесь со значениями NA
в результирующих данных, которые являются добавлен и не являются частью исходных данных. Если утверждение "имели одинаковые значения x" выполняется совершенно дословно, то это будет работать, но, поскольку вы все равно gather
обрабатываете данные, может быть немного безопаснее просто rbind
их вместе. Я считаю (несмотря на дополнительные NA
) ваше сообщение с данными gather
и мое datn
функционально эквивалентны.
Другими словами: у меня обсессивно-компульсивное расстройство при добавлении данных, когда в этом нет необходимости. Если случайно один кадр немного отличается от другого, операция соединения начинается с изменение данных, чего следует избегать. Я думал, что метод rbind
создает ту же структуру для единственного вызова ggplot
(в этом мы полностью согласны).
Вам не нужно много делать, просто объедините данные и предоставьте поле типа «источник», которое ggplot
может использовать для разделения/группировки/цвета на основе.
Например, ваши данные и объединенные данные:
dat1 <- data.frame(x = c(1,2,3,4,5), y = c(1,2,3,4,5))
dat2 <- data.frame(x = c(1,2,3,4,5), y = c(1,2,9,16,25))
datn <- rbind.data.frame(
transform(dat1, source = "dat1"),
transform(dat2, source = "dat2")
)
datn
# x y source
# 1 1 1 dat1
# 2 2 2 dat1
# 3 3 3 dat1
# 4 4 4 dat1
# 5 5 5 dat1
# 6 1 1 dat2
# 7 2 2 dat2
# 8 3 9 dat2
# 9 4 16 dat2
# 10 5 25 dat2
И сюжет, никаких гроб-комбинаций не требуется:
ggplot(data = datn, aes(x = x, y = y, color = source)) +
geom_line()
Существует множество способов дальнейшего управления этим, в том числе (1) определение того, какие цвета доступны для групп/цветов/фасетов и т. д.; (2) наличие и эстетика легенды; (3) почти все остальное (так как это ggplot2
).
Да! Это абсолютно работает для большинства случаев. Причина, по которой я не сделал этого сразу, заключается в том, что у меня нет прямого доступа к данным. Я изменю свой вопрос, чтобы быть более ясным. У меня есть метод, который принимает объект -> создает фрейм данных -> отображает данные. Ваш метод будет работать для меня, если я разделю свой метод на: (1) который принимает объект → создает фрейм данных и (2) фрейм данных → форматированный график. Таким образом, я могу использовать метод (1) для создания фреймов данных, объединять их и использовать (2) для построения графика. Это требует, чтобы я изменил свою инфраструктуру, но, держу пари, я должен был сделать это с самого начала. Спасибо!
Спасибо за ответ. Да, это всегда зависит от вашего рабочего процесса. Я нахожу преимущества в полном отделении сбора/агрегации данных от построения графиков, когда это возможно. То есть создавайте/собирайте/обрабатывайте данные одновременно для кадров 1-к-n, а затем беспокойтесь о создании графиков 1-к-n (или только 1) из этих кадров. Это другое мышление и поток. Этот ответ не очень хорошо подходит для создания графиков на лету, и в этом случае вам, вероятно, придется исследовать объединение grobs; и даже тогда, я полагаю, вы в конечном итоге получите данные или компоненты от одного и добавите к другому ... апостериорную версию этого.
Это не работает, если в одном есть
x
, которых нет в другом.