Выпуклые оболочки с ggbiplot

Основываясь на приведенной ниже справке, я безуспешно пробовал этот скрипт для построения PCA с выпуклыми корпусами, есть идеи, как я могу это решить?

library(ggbiplot)
library(plyr)

data <-read.csv("C:/Users/AAA.csv")
my.pca <- prcomp(data[,1:9] , scale. = TRUE)


find_hull <- function(my.pca) my.pca[chull(my.pca$x[,1], my.pca$x[,2]), ]
hulls <- ddply(my.pca , "Group", find_hull)

ggbiplot(my.pca, obs.scale = 1, var.scale = 1,groups = data$Group) + 
  scale_color_discrete(name = '') + geom_polygon(data=hulls, alpha=.2) + 
  theme_bw() + theme(legend.direction = 'horizontal', legend.position = 'top')

Спасибо.

Сценарий ниже отображает PCA с эллипсами (слегка измененный пример из https://github.com/vqv/ggbiplot поскольку «opts» устарела)

library(ggbiplot)
data(wine)
wine.pca <- prcomp(wine, scale. = TRUE)
g <- ggbiplot(wine.pca, obs.scale = 1, var.scale = 1, 
              groups = wine.class, ellipse = TRUE, circle = TRUE)
g <- g + scale_color_discrete(name = '')
g <- g + theme(legend.direction = 'horizontal', legend.position = 'top')
print(g)

Удалить эллипсы легко, но я безуспешно пытаюсь заменить их выпуклыми корпусами, есть идеи, как это сделать?

Спасибо


person wrek    schedule 10.08.2013    source источник
comment
это полезно?   -  person MYaseen208    schedule 10.08.2013
comment
Спасибо. Я смотрю что-то вроде этого 4.bp.blogspot .com/-eGnbet1tU4k/UBw4SlMMzFI/AAAAAAAAAFLU/ или этот i.stack.imgur. com/hvh2j.png   -  person wrek    schedule 10.08.2013
comment
Тогда это будет хорошим началом.   -  person MYaseen208    schedule 11.08.2013


Ответы (2)


Да, мы можем создать новую геометрию для ggplot, а затем использовать ее с ggbiplot. Вот новая геометрия, которая будет делать выпуклые оболочки:

library(ggplot2)
StatBag <- ggproto("Statbag", Stat,
                   compute_group = function(data, scales, prop = 0.5) {

                     #################################
                     #################################
                     # originally from aplpack package, plotting functions removed
                     plothulls_ <- function(x, y, fraction, n.hull = 1,
                                            col.hull, lty.hull, lwd.hull, density=0, ...){
                       # function for data peeling:
                       # x,y : data
                       # fraction.in.inner.hull : max percentage of points within the hull to be drawn
                       # n.hull : number of hulls to be plotted (if there is no fractiion argument)
                       # col.hull, lty.hull, lwd.hull : style of hull line
                       # plotting bits have been removed, BM 160321
                       # pw 130524
                       if(ncol(x) == 2){ y <- x[,2]; x <- x[,1] }
                       n <- length(x)
                       if(!missing(fraction)) { # find special hull
                         n.hull <- 1
                         if(missing(col.hull)) col.hull <- 1
                         if(missing(lty.hull)) lty.hull <- 1
                         if(missing(lwd.hull)) lwd.hull <- 1
                         x.old <- x; y.old <- y
                         idx <- chull(x,y); x.hull <- x[idx]; y.hull <- y[idx]
                         for( i in 1:(length(x)/3)){
                           x <- x[-idx]; y <- y[-idx]
                           if( (length(x)/n) < fraction ){
                             return(cbind(x.hull,y.hull))
                           }
                           idx <- chull(x,y); x.hull <- x[idx]; y.hull <- y[idx];
                         }
                       }
                       if(missing(col.hull)) col.hull <- 1:n.hull
                       if(length(col.hull)) col.hull <- rep(col.hull,n.hull)
                       if(missing(lty.hull)) lty.hull <- 1:n.hull
                       if(length(lty.hull)) lty.hull <- rep(lty.hull,n.hull)
                       if(missing(lwd.hull)) lwd.hull <- 1
                       if(length(lwd.hull)) lwd.hull <- rep(lwd.hull,n.hull)
                       result <- NULL
                       for( i in 1:n.hull){
                         idx <- chull(x,y); x.hull <- x[idx]; y.hull <- y[idx]
                         result <- c(result, list( cbind(x.hull,y.hull) ))
                         x <- x[-idx]; y <- y[-idx]
                         if(0 == length(x)) return(result)
                       }
                       result
                     } # end of definition of plothulls
                     #################################


                     # prepare data to go into function below
                     the_matrix <- matrix(data = c(data$x, data$y), ncol = 2)

                     # get data out of function as df with names
                     setNames(data.frame(plothulls_(the_matrix, fraction = prop)), nm = c("x", "y"))
                     # how can we get the hull and loop vertices passed on also?
                   },

                   required_aes = c("x", "y")
)

#' @inheritParams ggplot2::stat_identity
#' @param prop Proportion of all the points to be included in the bag (default is 0.5)
stat_bag <- function(mapping = NULL, data = NULL, geom = "polygon",
                     position = "identity", na.rm = FALSE, show.legend = NA, 
                     inherit.aes = TRUE, prop = 0.5, alpha = 0.3, ...) {
  layer(
    stat = StatBag, data = data, mapping = mapping, geom = geom, 
    position = position, show.legend = show.legend, inherit.aes = inherit.aes,
    params = list(na.rm = na.rm, prop = prop, alpha = alpha, ...)
  )
}


geom_bag <- function(mapping = NULL, data = NULL,
                     stat = "identity", position = "identity",
                     prop = 0.5, 
                     alpha = 0.3,
                     ...,
                     na.rm = FALSE,
                     show.legend = NA,
                     inherit.aes = TRUE) {
  layer(
    data = data,
    mapping = mapping,
    stat = StatBag,
    geom = GeomBag,
    position = position,
    show.legend = show.legend,
    inherit.aes = inherit.aes,
    params = list(
      na.rm = na.rm,
      alpha = alpha,
      prop = prop,
      ...
    )
  )
}

#' @rdname ggplot2-ggproto
#' @format NULL
#' @usage NULL
#' @export
GeomBag <- ggproto("GeomBag", Geom,
                   draw_group = function(data, panel_scales, coord) {
                     n <- nrow(data)
                     if (n == 1) return(zeroGrob())

                     munched <- coord_munch(coord, data, panel_scales)
                     # Sort by group to make sure that colors, fill, etc. come in same order
                     munched <- munched[order(munched$group), ]

                     # For gpar(), there is one entry per polygon (not one entry per point).
                     # We'll pull the first value from each group, and assume all these values
                     # are the same within each group.
                     first_idx <- !duplicated(munched$group)
                     first_rows <- munched[first_idx, ]

                     ggplot2:::ggname("geom_bag",
                                      grid:::polygonGrob(munched$x, munched$y, default.units = "native",
                                                         id = munched$group,
                                                         gp = grid::gpar(
                                                           col = first_rows$colour,
                                                           fill = alpha(first_rows$fill, first_rows$alpha),
                                                           lwd = first_rows$size * .pt,
                                                           lty = first_rows$linetype
                                                         )
                                      )
                     )


                   },

                   default_aes = aes(colour = "NA", fill = "grey20", size = 0.5, linetype = 1,
                                     alpha = NA, prop = 0.5),

                   handle_na = function(data, params) {
                     data
                   },

                   required_aes = c("x", "y"),

                   draw_key = draw_key_polygon
)

И здесь он используется с ggbiplot, мы устанавливаем prop в 1, чтобы указать, что мы хотим нарисовать многоугольник, который охватывает все точки:

library(ggbiplot)
data(wine)
wine.pca <- prcomp(wine, scale. = TRUE)
g <- ggbiplot(wine.pca, obs.scale = 1, var.scale = 1, 
              groups = wine.class, ellipse = FALSE, circle = TRUE)
g <- g + scale_color_discrete(name = '')
g <- g + theme(legend.direction = 'horizontal', legend.position = 'top')
g + geom_bag(aes(group = wine.class, fill = wine.class), prop = 1) 

введите здесь описание изображения

person Ben    schedule 22.03.2016
comment
Это прекрасное решение. Как удалить легенду wine.class (верхнюю), поскольку они дублируются? - person elyraz; 26.09.2016
comment
Взгляните на stackoverflow.com/a/11714990/1036500 и stackoverflow.com/a/14604540/1036500 для некоторых способов сделать это - person Ben; 26.09.2016
comment
Измените строку show.legend = NA в geom_bag‹- Function(mapping… на show.legend = F - person wrek; 26.09.2016
comment
Красиво, я использую это. Однако я не могу контролировать цвета корпусов. У меня есть несколько участков, и я хотел бы, чтобы цвета оставались одинаковыми для каждой категории (класса вина) от участка к участку. Какие-нибудь советы? - person Pertinax; 13.05.2018
comment
Я добавил новый ответ, который показывает немного другой метод, с комментарием по управлению цветами. - person Ben; 15.05.2018

Мы также можем сделать это с помощью ggbiplot и более нового пакета под названием ggpubr:

library(ggpubr)
library(ggbiplot)
data(wine)
wine.pca <- prcomp(wine, scale. = TRUE)

ggbiplot(
  wine.pca,
  obs.scale = 1,
  var.scale = 1,
  groups = wine.class,
  ellipse = FALSE,
  circle = TRUE
) + 
  stat_chull(aes(color = wine.class, 
                 fill = wine.class), 
             alpha = 0.1, 
             geom = "polygon") +
  scale_colour_brewer(palette = "Set1",
                      name = '',
                      guide = 'none') +
  scale_fill_brewer(palette = "Set1",
                      name = '')  +
  theme_minimal()

введите здесь описание изображения

Я использовал scale_colour_brewer и scale_fill_brewer для управления цветами корпусов и точек и подавления одной из легенд.

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

person Ben    schedule 15.05.2018
comment
Это прекрасно работает. Как я могу выделить определенные точки/записи набора данных? - person Philipp R; 26.05.2020