Как отобразить группу данных в виде точек, а другую в виде эллипса уверенности? Проблемы с ggplot и ggsave

Я новичок в R и пытаюсь создать точечные диаграммы с двумя переменными, при этом значения каждой переменной сгруппированы в 4 класса.

В частности, я пытаюсь добиться следующего:

  1. Отображение двух групп в виде точек данных, двух групп в виде эллипсов достоверности
  2. Создайте и сохраните точечные диаграммы, имеющие одинаковые размеры с точки зрения размера рамки графика и площади графика (т. Е. Длина оси x 8 см, длина оси y 6 см.).

Ниже вы можете найти воспроизводимую версию (вам просто нужно определить вывод для файла png) кода, который работает, но показывает точки данных и эллипсы достоверности для всех данных:

library(ggplot2)

out_path = YOUR OUTPUT DIRECTORY

#data frame
gr1 <- (rep(paste('B-12-B-002'), 10))
gr2 <- (rep(paste('B-12-M-03'), 10))
gr3 <-  (rep(paste('b-b-d-3'), 10))
gr4 <-  (rep(paste('h-12-b-01'), 10))

Run_type <- c(gr1,gr2,gr3,gr4)

axial_ratio <- runif (40,0,1) 
Solidity <- runif (40,0,1)
Convexity <- runif (40,0,1)

sel_data_all <- data.frame(Run_type,axial_ratio,Solidity,Convexity)
fill_colors <- c('red','blue','green','orange');

#Plot

one_plot = ggplot(sel_data_all,aes(x = axial_ratio,y = Solidity))+         
  geom_point(aes(x = axial_ratio,y = Solidity, fill = Run_type, shape = Run_type), color = "black", stroke = 1, 
             size = 5, alpha = 0.4)+
  stat_ellipse(data = sel_data_all, aes(x = axial_ratio, y = Solidity, fill = Run_type,colour=Run_type),geom = "polygon",alpha = 0.4,type = "norm",level = 0.6, 
               show.legend = FALSE) +  #, group=Run_type , data = subset(sel_data_all, Run_type %in% leg_keys_man[1:7]),
  scale_shape_manual(values=c(21,21,23,23))+ 
  scale_fill_manual(values = fill_colors)+
  scale_color_manual(values = fill_colors)+
  coord_fixed(ratio = 1)+
  theme(legend.position = "top", # write 'none' to hide the legend
        legend.key = element_rect(fill = "white"), # Set background of the points in the legend
        legend.title = element_blank(), # Remove legend title
        
        panel.background=element_rect(fill = "white", colour = "black"),
        panel.grid.major=element_line(colour = "lightgrey"),
        panel.grid.minor=element_line(colour = "lightgrey"),
        axis.title.x = element_text(margin = margin(t = 10), size = 12,face = "bold"), # margin = margin(t = 10) vjust = 0
        axis.title.y = element_text(margin = margin(r = 10), size = 12,face = "bold"), # margin = margin(r = 10) vjust = 2
        axis.text = element_text(color = "black", size = 10), # To hide the text from a specific axis do: axis.text.y = element_blank()
        axis.ticks.length=unit(-0.15, "cm"), # To hide the ticks from a specific axis do: axis.ticks.y = element_blank()
        #plot.margin = margin(t = 0, r = 1, b = 0.5, l = 0.5, unit = "cm"), # define margine of the plot frame t = top, r = right, b = bottom,  l = left
  )
  #expand_limits(x = 0, y = 0)+ #Force the origin of the plot to 0
  #xlim(c(0,1))+
  #ylim(c(0,1)) # or xlim, limit the axis to the values defined

show(one_plot)

# Save plots 
ggsave(
  filename=paste("Axial_ratio","_vs_","Solidity",".png",sep = ""),
  plot = one_plot,
  device = "png",
  path = out_path,
  scale = 1,
  width = 8, # Refers to the plot frame, not the area
  height = 6, # Refers to the plot frame, not the area
  units = "cm",
  dpi = 300,
  limitsize = FALSE,
  bg = "white")

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

Для первой задачи я попытался разделить данные, изменив функции geom_point и stat_ellipse,

  geom_point(data = subset(sel_data_all, Run_type %in% c('B-12-B-002','B-12-M-03')),aes(x = axial_ratio,y = Solidity, fill = Run_type, shape = Run_type), color = "black", stroke = 1, 
             size = 5, alpha = 0.4)+ #
  stat_ellipse(data = subset(sel_data_all, Run_type %in% c('b-b-d-3','h-12-b-01')), aes(x = axial_ratio, y = Solidity, fill = Run_type,colour=Run_type),geom = "polygon",alpha = 0.4,type = "norm",level = 0.6, 
               show.legend = FALSE) +  #

но я получаю дубликат легенды (в сером цвете).

Вот так.

Для моего второго вопроса, с рабочей версией скрипта в верхней части сообщения,

Вот сюжет, который отображается в окне "Plots" в RStudio:

Но это то, что сохраняется в каталоге вывода.

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

отношение осей против выпуклости

ves_pct против осевого соотношения

Кто-нибудь может помочь?

Заранее спасибо всем!

Обновлено:

Итак, благодаря MarBlo (которому большое спасибо) мне удалось получить почти то, что я хочу, но есть еще кое-что, чего я пока не могу понять.

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

          library(tidyverse)

          set.seed(123)
          gr1 <- (rep(paste("B-12-B-002"), 10))
          gr2 <- (rep(paste("B-12-M-03"), 10))
          gr3 <- (rep(paste("b-b-d-3"), 10))
          gr4 <- (rep(paste("h-12-b-01"), 10))

          Sample_ID <- c(gr1, gr2, gr3, gr4)

          axial_ratio <- runif (40, 0, 1)
          Solidity <- runif (40, 0, 1)
          Convexity <- runif (40, 0, 1)

          sel_data_all <- data.frame(Sample_ID, axial_ratio, Solidity, Convexity)
          fill_colors <- c("#5bd9ca",
          "#1e99d6","#1e49d6","#f2581b80","#e8811280","#e3311280","#fc000080")

          sel_data_all <- sel_data_all |> add_column(Run_type = c(
            rep("MAG", 10), rep("PMAG", 10),
            rep("MAG", 10), rep("PMAG", 10)),   .before = "Sample_ID")
          
          one_plot = ggplot( 

          data = sel_data_all |> dplyr::filter(Run_type == "PMAG"),
          aes(x = axial_ratio, y = Solidity)
        ) +
        
        # CONFIDENCE ELLIPSE
        stat_ellipse(
          data = sel_data_all |> dplyr::filter(Run_type == "MAG"),
          aes(x = axial_ratio, y = Solidity,
              fill = Sample_ID),
          geom = "polygon", type = "norm", 
          level = 0.6,
          colour = 'white', # ellipse border
        ) +
        
        # DATA POINTS
        geom_point(aes(colour = Sample_ID, 
                       shape = Sample_ID),
                   stroke = 0.5,
                   size = 3,
        ) +
        
        scale_color_manual(values = fill_colors[1:3]) + # of Data points
        scale_shape_manual(values = c(21, 21, 23, 23,21,23,22)) + # of data points
        scale_fill_manual(values = fill_colors[4:7]) + # of ellipses
        coord_cartesian(xlim=c(0,1))+
        #scale_x_continuous(expand = expansion(mult = c(0.001, 0.05)))+           
        coord_cartesian(ylim=c(0,1))+           
        #scale_y_continuous(expand = expansion(mult = c(0.001, 0.05)))+
        # Theme
        theme(
          legend.position = "top",
          legend.key.size = unit(5, 'mm'), #change legend key size
          # legend.key.height = unit(1, 'cm'), #change legend key height
          # legend.key.width = unit(1, 'cm'), #change legend key width
          legend.text = element_text(size=8),
          legend.key = element_rect(fill = "white", colour = 'white'),
          legend.background = element_rect(fill = "transparent"),
          legend.title = element_blank(),
          panel.background = element_rect(fill = "white", colour = "black"),
          panel.grid.major = element_line(colour = "lightgrey"),
          panel.grid.minor = element_line(colour = "lightgrey"),
          axis.title.x = element_text(vjust = -1, size = 12, face = "bold"),
          axis.title.y = element_text(vjust = 4, size = 12, face = "bold"),
          axis.text = element_text(color = "black", size = 10),
          axis.ticks.length = unit(-0.15, "cm"),
          plot.margin = margin(t = 2,  # Top margin
                               r = 4,  # Right margin
                               b = 4,  # Bottom margin
                               l = 4, # Left margin
                               unit = "mm"),
        )+
        guides(colour = guide_legend(nrow=2, byrow=TRUE)+
        coord_fixed(ratio = 1))
              ggsave(
            filename=paste("snap",".png",sep = ""),
            plot = one_plot,
            device = "png",
            path = here::here(),
            width = 8, # Refers to the plot frame, not the area
            height = 8, # Refers to the plot frame, not the area
            units = "cm",
            #dpi = 300,
            #limitsize = FALSE,
            bg = "white")


    

Вот сохраненный сюжет

Что мне нужно, так это точки данных, заполненные цветом, используемым в настоящее время для их границы, и граница всех точек данных черного цвета.

Я попытался обойти эстетику, но в итоге получил дубликат легенды и еще большую путаницу.

Заранее еще раз спасибо за вашу помощь.

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

Ответы 1

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

Я взял ваши данные и добавил переменную под названием group, которая упрощает фильтрацию ggplot.

Если вы определяете x и y в ggplot(..,aes()), вам не нужно определять их снова в geom_point. В geom_point вы уже задаете цвет Run_type переменной, из которой должна быть составлена ​​легенда. Поскольку вы используете в geom_ellipse другое подмножество DF, легенда будет обновлена ​​и снова сделает 4 записи легенды вместо 2 только для переменных. color = Run_type поэтому можно пропустить.

Я добавил set.seed(), что гарантирует сопоставимость результатов, хотя для составления DF генерируются случайные числа.

library(tidyverse)

set.seed(123)
gr1 <- (rep(paste("B-12-B-002"), 10))
gr2 <- (rep(paste("B-12-M-03"), 10))
gr3 <- (rep(paste("b-b-d-3"), 10))
gr4 <- (rep(paste("h-12-b-01"), 10))

Run_type <- c(gr1, gr2, gr3, gr4)

axial_ratio <- runif (40, 0, 1)
Solidity <- runif (40, 0, 1)
Convexity <- runif (40, 0, 1)

sel_data_all <- data.frame(Run_type, axial_ratio, Solidity, Convexity)
fill_colors <- c("red", "blue", "green", "orange")

df <- sel_data_all |> mutate(group = c(
  rep("Data", 10), rep("Conf", 10),
  rep("Data", 10), rep("Conf", 10)
))

ggplot(
  data = df |> dplyr::filter(group == "Data"),
  aes(x = axial_ratio, y = Solidity)
) +
  geom_point(aes(color = Run_type, shape = Run_type),
    stroke = 1,
    size = 5, alpha = 0.4
  ) +
  stat_ellipse(
    data = df |> dplyr::filter(group != "Data"),
    aes(
      x = axial_ratio, y = Solidity,
      fill = Run_type
    ),
    geom = "polygon", alpha = 0.4, type = "norm", level = 0.6,
    show.legend = FALSE
  ) +
  scale_shape_manual(values = c(21, 21, 23, 23)) +
  scale_fill_manual(values = fill_colors) +
  scale_color_manual(values = fill_colors) +
  coord_fixed(ratio = 1) +
  theme(
    legend.position = "top",
    legend.key = element_rect(fill = "white"),
    legend.title = element_blank(),
    panel.background = element_rect(fill = "white", colour = "black"),
    panel.grid.major = element_line(colour = "lightgrey"),
    panel.grid.minor = element_line(colour = "lightgrey"),
    axis.title.x = element_text(margin = margin(t = 10), size = 12, face = "bold"),
    axis.title.y = element_text(margin = margin(r = 10), size = 12, face = "bold"),
    axis.text = element_text(color = "black", size = 10),
    axis.ticks.length = unit(-0.15, "cm"),
  )

Затем график был сохранен с одинаковой шириной и высотой.

ggsave(
  filename=paste("Axial_ratio","_vs_","Solidity",".png",sep = ""),
  plot = last_plot(),
  device = "png",
  path = here::here(),
  width = 8, # Refers to the plot frame, not the area
  height = 8, # Refers to the plot frame, not the area
  units = "cm",
  #dpi = 300,
  #limitsize = FALSE,
  bg = "white")

сохраненный png выглядит так.


НОВОЕ ИЗМЕНЕНИЕ

Теперь я понимаю, что вам нужны два цвета для geom_points и 2 разных цвета для stat_ellipse.

Итак, ниже будет показана новая попытка. Если это правильный ответ, я удалю большую часть сверху, чтобы сделать этот пост более читаемым.

Вопрос с ggsave считаю решенным.

Я определил два разных набора цветов; один для geom_point и один для stat_ellipse. (Определены 3 и 4 цвета, хотя позже в scale_color_manual и scale_fill_manual нужны только 2 цвета.)

Что касается как geom_point, так и stat_ellipse, используется другой DF, ggplot вызывается без каких-либо data или aes. Оба будут определены индивидуально при вызове geom_point и stat_ellipse.

Для stat_ellipse используется fill, а для geom_pointcolor используется как aes.

Если вы хотите исключить ту или иную легенду, вы можете использовать show.legend = F в соответствующем geom.

xlim и ylim определяют пределы осей.

guides() и theme_bw() убедитесь, что темный фон legend.key стерт.

Я попытался сделать тему более краткой.

library(tidyverse)

set.seed(123)
gr1 <- (rep(paste("B-12-B-002"), 10))
gr2 <- (rep(paste("B-12-M-03"), 10))
gr3 <- (rep(paste("b-b-d-3"), 10))
gr4 <- (rep(paste("h-12-b-01"), 10))

Sample_ID <- c(gr1, gr2, gr3, gr4)

axial_ratio <- runif (40, 0, 1)
Solidity <- runif (40, 0, 1)
Convexity <- runif (40, 0, 1)

sel_data_all <- data.frame(Sample_ID, axial_ratio, Solidity, Convexity)
fill_colors_points <- c("#5bd9ca", "#1e99d6", "#1e49d6")
fill_colors_ellipse <- c("#f2581b80", "#e8811280", "#e3311280", "#fc000080")

sel_data_all <- sel_data_all |> mutate(Run_type = c(
  rep("MAG", 10), rep("PMAG", 10),
  rep("MAG", 10), rep("PMAG", 10)
))

ggplot() +
  stat_ellipse(
    data = sel_data_all |> dplyr::filter(Run_type == "MAG"),
    aes(
      x = axial_ratio, y = Solidity,
      fill = Sample_ID
    ),
    geom = "polygon", type = "norm",
    level = 0.6, show.legend = T
  ) +
  geom_point(
    data = sel_data_all |> dplyr::filter(Run_type == "PMAG"),
    aes(
      x = axial_ratio, y = Solidity,
      color = Sample_ID,
      shape = Sample_ID
    ),
    stroke = 0.5, size = 3,
  ) +
  scale_color_manual(values = fill_colors_points[1:2]) + # of Data points
  scale_fill_manual(values = fill_colors_ellipse[1:2]) + # of ellipses
  xlim(0,1) + ylim(0,1) +
  guides(color = guide_legend(override.aes = list(fill = NA))) +
  theme_bw() +
  theme(
    legend.position = "top",
    legend.key.size = unit(5, "mm"), # change legend key size
    legend.text = element_text(size = 8),
    legend.title = element_blank(),
    panel.background = element_rect(fill = "white", colour = "black"),
    panel.grid = element_line(colour = "lightgrey"),
    axis.title.x = element_text(vjust = -1, size = 12, face = "bold"),
    axis.title.y = element_text(vjust = 4, size = 12, face = "bold"),
    axis.text = element_text(color = "black", size = 10),
    axis.ticks.length = unit(-0.15, "cm"),
  ) +
  coord_fixed(ratio = 1)

Большое спасибо! Это сработало как заклинание. Последний вопрос: если мне нужно будет добавить черную обводку вокруг точек и белую рамку вокруг эллипса, как мне это сделать? Спасибо большое, вы спаситель жизни!

Pier Volc 09.01.2023 17:55

@Pier Volc Добро пожаловать, рад, что смог помочь. Вы можете проголосовать за ответ. Если вы хотите добавить к эллипсу белую рамку, просто добавьте color = 'white' к stat_ellipse, но вне aes. Добавление черной рамки к точкам возможно, но вы должны выбрать правильные формы. Те, между 21 и 25 имеют черные границы. Но затем вы должны определить fill, и это может помешать fill из эллипса. Вероятно, это сработает, но требует небольшой настройки - не уверен, что оно того стоит.

MarBlo 09.01.2023 19:22

Большое спасибо еще раз. Я пытался проголосовать, но мне нужно как минимум 15 репутации :(

Pier Volc 09.01.2023 20:10

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