Преобразуйте обычный график в объект ggplot (а затем графически)

Я использую язык программирования R. Я включил свой собственный код вместе с длинным руководством сюда: https://michael.hahsler.net/SMU/EMIS7332/R/viz_classifier.html. В конце концов, я создал визуальный сюжет (см. Конец этого кода, final_plot).

library(cluster)
library(Rtsne)
library(dplyr)

library(randomForest)
library(caret)
library(ggplot2)
library(plotly)


#PART 1 : Create Data

#generate 4 random variables : response_variable ~ var_1 , var_2, var_3

var_1 <- rnorm(10000,1,4)
var_2<-rnorm(10000,10,5)
var_3 <- sample( LETTERS[1:4], 10000, replace=TRUE, prob=c(0.1, 0.2, 0.65, 0.05) )
response_variable <- sample( LETTERS[1:2], 10000, replace=TRUE, prob=c(0.4, 0.6) )


#put them into a data frame called "f"
f <- data.frame(var_1, var_2, var_3, response_variable)

#declare var_3 and response_variable as factors
f$response_variable = as.factor(f$response_variable)
f$var_3 = as.factor(f$var_3)

#create id
f$ID <- seq_along(f[,1])

#PART 2: random forest

#split data into train set and test set
index = createDataPartition(f$response_variable, p=0.7, list = FALSE)
train = f[index,]
test = f[-index,]

#create random forest statistical model
rf = randomForest(response_variable ~ var_1 + var_2 + var_3, data=train, ntree=20, mtry=2)

#have the model predict the test set
pred = predict(rf, test, type = "prob")
labels = as.factor(ifelse(pred[,2]>0.5, "A", "B"))
confusionMatrix(labels, test$response_variable)

#PART 3: Visualize in 2D (source: https://dpmartin42.github.io/posts/r/cluster-mixed-types)

gower_dist <- daisy(test[, -c(4,5)],
                    metric = "gower")

gower_mat <- as.matrix(gower_dist)

labels = data.frame(labels)
labels$ID = test$ID


tsne_obj <- Rtsne(gower_dist,  is_distance = TRUE)

tsne_data <- tsne_obj$Y %>%
    data.frame() %>%
    setNames(c("X", "Y")) %>%
    mutate(cluster = factor(labels$labels),
           name = labels$ID)

plot = ggplot(aes(x = X, y = Y), data = tsne_data) +
    geom_point(aes(color = labels$labels))

plotly_plot = ggplotly(plot)


a = tsne_obj$Y
a = data.frame(a)
data = a
data$class = labels$labels


decisionplot <- function(model, data, class = NULL, predict_type = "class",
                         resolution = 100, showgrid = TRUE, ...) {
    
    if(!is.null(class)) cl <- data[,class] else cl <- 1
    data <- data[,1:2]
    k <- length(unique(cl))
    
    plot(data, col = as.integer(cl)+1L, pch = as.integer(cl)+1L, ...)
    
    # make grid
    r <- sapply(data, range, na.rm = TRUE)
    xs <- seq(r[1,1], r[2,1], length.out = resolution)
    ys <- seq(r[1,2], r[2,2], length.out = resolution)
    g <- cbind(rep(xs, each=resolution), rep(ys, time = resolution))
    colnames(g) <- colnames(r)
    g <- as.data.frame(g)
    
    ### guess how to get class labels from predict
    ### (unfortunately not very consistent between models)
    p <- predict(model, g, type = predict_type)
    if(is.list(p)) p <- p$class
    p <- as.factor(p)
    
    if(showgrid) points(g, col = as.integer(p)+1L, pch = ".")
    
    z <- matrix(as.integer(p), nrow = resolution, byrow = TRUE)
    contour(xs, ys, z, add = TRUE, drawlabels = FALSE,
            lwd = 2, levels = (1:(k-1))+.5)
    
    invisible(z)
}


model <- randomForest(class ~ ., data=data, mtry=2, ntrees=500)
 final_plot = decisionplot(model, data, class = "class", main = "rf (1)")

Теперь я хотел бы превратить это в интерактивный сюжет, используя библиотеку plotly в R:

plotly_plot = ggplotly(final_plot)

Но я получил следующую ошибку:

Error in UseMethod("ggplotly", p) : 
  no applicable method for 'ggplotly' applied to an object of class "c('matrix', 'array', 'integer', 'numeric')"

Есть ли способ преобразовать обычные графики в ggplot в R? Можно ли передать мой final_plot через сюжетный объект?


person stats555    schedule 22.12.2020    source источник
comment
ggplotly() преобразует ggplotобъект в сюжетно. Поэтому попробуйте сначала перенести объект сюжета в ggplot с помощью as.ggplot(). Поскольку вы конвертируете много разных функций, вам следует подумать о том, чтобы переделать график в ggplot или plotly. Не все функции поддерживаются для преобразования.   -  person mischva11    schedule 22.12.2020
comment
Спасибо за ваш ответ! Вы знаете, как я могу это сделать?   -  person stats555    schedule 23.12.2020


Ответы (1)


Как прокомментировал @ mischva11, я думаю, что проще создать ggplot с нуля. Ваша функция фактически возвращает матрицу, а не объект сюжета. функции plot и countour рисуют графики непосредственно в активном графическом окне. Я не уверен, есть ли способ преобразовать эти базовые графики в ggplot (возможно, есть).

Вот способ создать такой же график, как в ggplot, а затем преобразовать его в plotly.

decisionplot <- function(model, data, class = NULL, predict_type = "class", resolution = 100, showgrid = TRUE) {
  
  # create ggplot with minimal theme and no grid lines
  g <- ggplot() + theme_minimal() + theme(panel.grid = element_blank())

  # make grid values for contour and grid points
  r <- sapply(data[ ,1:2], range, na.rm = TRUE)
  xs <- seq(r[1,1], r[2,1], length.out = resolution)
  ys <- seq(r[1,2], r[2,2], length.out = resolution)
  g1 <- cbind(rep(xs, each=resolution), rep(ys, time = resolution))
  colnames(g1) <- colnames(r)
  g1 <- as.data.frame(g1)
    
  ### guess how to get class labels from predict
  ### (unfortunately not very consistent between models)
  p <- predict(model, g1, type = predict_type)
  if(is.list(p)) p <- p$class
  g1$class <- as.factor(p)
  
  if(showgrid) {        
    # add labeled grid points to ggplot
    g <- g + geom_point(data=g1, aes(x=X1, y=X2, col = class), shape = ".") 
  }

  # add points to plot
  g <- g + geom_point(data=data, aes(x=X1, y=X2, col = class, shape = class)) 
  
  # add contour curves
  g <- g + geom_contour(data=g1, aes(x=X1, y=X2, z=as.integer(class)), colour='black', linetype=1, size=rel(0.2), bins=length(unique(g1$class)))
  
  # return ggplot object
  return(g)
}    

# get ggplot object
final_plot <- decisionplot(model, data, class = "class")

# convert to plotly
ggplotly(final_plot)

Это работает. Финальный сюжет выглядит не очень хорошо, но с параметрами можно поиграться.

Одна вещь, которая, на мой взгляд, могла бы улучшить окончательный график, - это использовать geom_raster для построения областей с разными предсказаниями меток (вместо нанесения маленьких точек). Однако, когда я сделал это, преобразование в plotly заняло вечность (я фактически сдался). Я думаю, что при использовании дискретных меток для geom_raster есть проблема с преобразованием в графическое представление, потому что, когда я преобразовал дискретные метки в числовые значения, он очень быстро преобразовался в графически.

Другой вариант - работать непосредственно в plot_ly, но у меня нет большого опыта в этом.

Надеюсь, это сработает.

person kikoralston    schedule 23.12.2020
comment
Не могу поверить, насколько это прекрасно! большое спасибо! единственное, что хотел спросить - можно ли убрать цвет фона в сюжетной версии? Думаю, было бы легче читать, если бы фон в сюжетной версии был чисто-белым. Это возможно? Еще раз, я не могу отблагодарить вас за ваш ответ! - person stats555; 24.12.2020
comment
Пожалуйста! Рад, что смог помочь. Под цветом фона вы подразумеваете разные цвета для разных регионов классификации? В этом случае вы можете просто установить showgrid = FALSE при вызове функции decisionplot. Если вы это сделаете, функция не будет рисовать точки сетки разными цветами для разных областей принятия решения. Фон будет полностью белым. Я постарался создать сюжет, максимально приближенный к тому, который был у вас раньше. Поэтому я сохранил большинство этих параметров. - person kikoralston; 24.12.2020
comment
Спасибо за ваш ответ! Я пробовал запустить: final_plot ‹- solutionplot (model, data, class = class, showgrid = FALSE) ... но это дает мне следующую ошибку: Ошибка в уникальном (g1 $ class): объект 'g1' не найден ... Вы знаете, почему это могло быть? Еще раз спасибо за вашу помощь - person stats555; 24.12.2020
comment
Извините. Когда я написал функцию, я переместил вычисление фрейма данных g1 внутрь if (showgrid). Сейчас поправил (см. Отредактированный код выше). Он вычислит g1dataframe, но построит только точки сетки, когда showgrid = TRUE. Думаю, теперь должно работать. - person kikoralston; 24.12.2020
comment
если у вас есть время, не могли бы вы помочь мне преобразовать этот следующий объект в сюжетный объект: stackoverflow.com/questions/65836431/? Еще раз спасибо за вашу помощь - person stats555; 22.01.2021