簡體   English   中英

在 plotly R 中插入圖像和/或 ggplot 的工具提示

[英]Tooltip inserting image and/or ggplot in plotly R

我想在 plotly 中創建的 3D UMAP 中編輯工具提示 hover 功能。 What I want to do is attach a saved image corresponding to a specific 'Sample.Code' and have that image appear when I hover over it in the 3D plotly scatter plot.

這將類似於此鏈接中附加的交互式 t-SNE/MNIST 數據集: https://dash.plotly.com/dash-core-components/tooltip

但是,上面寫的是 python 而不是 R。 是否有可能在 R 中實現這一點?

我在下面附上了我的代碼:

    # load ggplot2
library(ggplot2)
library(hrbrthemes)
library(tidyverse)
library(dplyr)
library(wesanderson)
library(RColorBrewer)
library(manipulate)
library(umap)
library(plotly)
library(shiny)
library(leaflet)

#set wd to location where plots will be saved. First step is to upload the initial data. 

dt <- read.csv('data.csv')

#Run to the UMAP

df <- as.matrix(dt[, 2:149]) #remember to only set columns which will be used in the UMAP calculation

  custom.config = umap.defaults  #manual settings
  custom.config$n_components = 3
  custom.config$n_neighbors = 8
  custom.config$min_dist = 0.6
  
data_umap <-umap(df, config = custom.config)
  
data_raw<-as.data.frame(data_umap$layout)

data_plot <- cbind(data_raw, dt)

  #running graphics script. Make sure to change color and size to appropriate labels
  #This is the visual that will get manipulated playing around with the UMAP parameters
  
UMAP_plot <- ggplot(data_plot, aes(x = V1, y = V3, color = Dataset)) +
    labs(title = '2D UMAP') +
    geom_point(alpha = 0.7, shape =19 ) + 
    theme_light() +
    theme(axis.text.y = element_text(size = 15),
          axis.text.x = element_text(size = 10),
          axis.title = element_text(size = 15),
          plot.title = element_text(size = 17),
          legend.text = element_text(size=15)) +
    scale_radius(range=c(4, 10))

colors <- c('#4AC6B7', '#1972A4', '#965F8A', '#FF7070', 'darkolivegreen')
  
UMAP_2D <- plot_ly(data_plot, x= ~V1, y= ~V3, color = ~Dataset, colors=colors,  type = "scatter", mode = "markers", text = ~paste('Sample.Code:', Sample.Code))

UMAP_3D <- plot_ly(data_plot, x= ~V1, y= ~V2, z= ~V3, type="scatter3d", mode="markers", color = ~Dataset, colors=colors, text = ~paste('Sample.Code:', Sample.Code))

UMAP_3D <- UMAP_3D %>% layout(title = '3D UMAP',
                             paper_bgcolor = 'grey',
                             plot_bgcolor = 'black')

UMAP_3D.update_layout(legend=dict(
  yanchor="top",
  y=0.99,
  xanchor="left",
  x=0.01
))

#Eventually add fingerprints to hover based on this code here: https://anchormen.nl/blog/data-science-ai/images-in-plotly-with-r/


print(UMAP_plot)
print(UMAP_2D)
print(UMAP_3D)

根據您的評論,我絕對可以使用 Plotly 示例中的數據。 我還沒有購買 Dash,但您當然可以這樣做並讓自己更輕松。 (我想。我沒用過;所以我猜。)

在沒有 Dash 的情況下,使用 Python 示例,我基本上用 Plotly 在 R 中重新創建了該圖。 我沒有使用ggplot。

我使用了兩個庫,但我唯一一次使用htmlwidgets時附加了庫名稱。

首先,數據是從與 Python 示例中相同的位置收集的,但我只使用了前 100 行。 接下來,我將換行標記添加到描述中。 SVG 文本中沒有字符串換行。 每行都單獨標記。 我用了 65 個字符,給出或接受一個完整的單詞。 您可以根據您要查找的工具提示的大小對其進行修改。

之后,我創建了基本的 plot。 我將我可以添加的所有內容都添加到了工具提示中,因此我只需要進行一些調整。 Plotly 將所有內容呈現為工具提示中的文本,因此我添加了幾個換行符 (18),以便為圖像留出空間。 我還在工具提示中添加了一些樣式(例如"darkblue"文本)。

在 JS( htmlwidgets::onRender中的所有內容)中,您需要收集工具提示中的內容,刪除沒有文本的換行符,並 retrofit 將圖像 URL 作為<image>元素。

這需要獲得職位和可用空間。 這需要確定工具提示是否在點的左側、點的右側,或者文本是否在工具提示中居中。 對於有擴展描述的點,事情是不對的,所以也需要一些調整。 我在 JS 中添加了很多注釋,所以這也應該解釋很多代碼的目的。

如果您有任何問題,請告訴我。

library(plotly)

# get molecular data
dp <- read.csv("https://raw.githubusercontent.com/plotly/dash-sample-apps/main/apps/dash-drug-discovery/data/small_molecule_drugbank.csv")

# because even SVG doesn't string wrap, each row is a separate tag
dp$desc <- gsub('(.{1,65})(\\s|$)', '\\1<br>', dp$DESC)

plt <- plot_ly(
  dp[1:100, ], x = ~LOGP, y = ~PKA, mode = "markers", type = "scatter",
  marker = list(colorscale = "Viridis",
                color = ~MW, size = ~MW,
                colorbar = list(title = "Molecule<br>Weight",
                                outlinewidth = 0), # just looks better without you!
                line = list(color = "#444"),
                reversescale = T, sizeref = 45,
                sizemode = "diameter", opacity = .8),
  customdata = ~IMG_URL,
  text = ~NAME, hovertext = ~desc,
  hovertemplate = paste0(  # 'here' is used for regex in JS
    "image href='%{customdata}' here ",  # 18 <br> make space for image (see in JS where 18 matters)
    "<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>",
    "<span style='fill:darkblue;font-size:2em;'>%{text}</span><br>",
    "<span>%{hovertext}</span><extra></extra>")) %>% 
  layout(xaxis = list(title = "Log P"), yaxis = list(title = "pkA"),
         plot_bgcolor = "#1a1a1a", paper_bgcolor = "#0d0d0d", # almost and off black
         hoverdistance = 10) %>%                              # more precision in selection
  htmlwidgets::onRender("function(el){
    console.log(el);
    setInterval(function(){              /* no content when tip is called; keep looking */
      tt = document.querySelector('#' + el.id + '> div > div > svg:nth-child(5) > g');
    },100);
    el.on('plotly_hover', function(d){
      if(tt.firstChild) {
        cvy = parseFloat(getComputedStyle(tt).fontSize);      /* em to px conversion */
        str = tt.innerHTML;
        gimme = /image.*(?=( here \\<br))/.exec(str);         /* extract image details */
        x = /(?<=(data-notex=\"1\" x=\")).*(?=(\" y=.*data))/g.exec(str);/* image x */
        y = /(?<=(y=\")).*(?=(\" data-unformatted))/g.exec(str);         /* image y */
        bc = tt.getBoundingClientRect();
        wbc = bc.width;
        hbc = bc.height;
        if(/-/.exec(x[0])) wwbc = (wbc * -1) - Number(x[0]);  /* adjust x if tip to the left */
        if(/-/.exec(x[0]) === null) wwbc = Number(x[0]);      /* adjust x if tip to the right */
        if(x[0] == 0) wwbc = wbc * -.5;                       /* if text is centered */
        ts = str.match(/\\<tspan?.*?\\/tspan\\>/gm);          /* all tspans indexed */
        dy = /(?<=(dy=\")).*(?=( x=))/g.exec(ts[18]);         /* 18th break in the template (18 br's coded) */
        dyc = /.*(?=(em))/.exec(dy[0]);                       /* collect digits; remove units */
        cdy = cvy * Number(dyc[0]);                           /* convert em to px for image ht */
        if(cdy > hbc * .75) cdy = hbc * .65;                  /* if em => px is larger than plot, reduce */
        wbcw = wbc - Math.abs(Number(x[0]) * 2);              /* get width for image */
        if(ts.length >= 34) {
          y[0] = Number(y[0]) * 1.1;               /* lots of text images are low & too large */
          cdy = cdy * .9;
        }
        beg = str.match(/.*(?=(\\<text))/);                   /* everything up to text tags */
        jn = '\\<' + gimme[0] + ' x=\"' + wwbc + '\" y=\"' + y[0]; /* the image; can't crtl aspect ratio——Plotly-ism? */                           
        jn = jn + '\" height=\"' + cdy + '\" width=\"' + wbcw +'\"\\/\\>';  
        tx = str.match(/\\<text.*pre\\;\"\\>?/);              /* text element before tspan */
        nts = ts.slice(18, ts.length).join('\\<\\/tspan\\>'); /* reassemble kept tspans */
        clt = '\\<\\/text\\>\\<\\/g\\>';                      /* closing tags */
        altog = beg[0] + jn + tx[0] + nts + clt;              /* put it together */
        tt.innerHTML = altog;                                 /* replace tooltip */
      }
    });
  }")

在此處輸入圖像描述

在此處輸入圖像描述 在此處輸入圖像描述

在此處輸入圖像描述 在此處輸入圖像描述

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM