Поместите диаграмму в всплывающее окно plotly

Я использую plotly для R, хотя я также открыт для использования версии Python. Когда я наведу курсор на точку данных, есть ли способ сделать всплывающее окно содержать другую диаграмму? В идеале диаграмма будет создана из данных, хотя я могу использовать статическое изображение в качестве запасного варианта.

Я не уверен, с чего начать, и заранее извиняюсь за то, что у меня нет MWE.

3 ответов


Решение 1: придерживайтесь R

спасибо @MLavoie. В следующем примере используйте pure R чтобы создать два сюжета, "mainplot" и "hover", который реагирует на событие наведения первого.

library(shiny)
library(plotly)

ui <- fluidPage(
  plotlyOutput("mainplot"),
  plotlyOutput("hover")
)

server <- function(input, output) {
  output$mainplot <- renderPlotly({
    # https://plot.ly/r/
    d <- diamonds[sample(nrow(diamonds), 1000), ]
    plot_ly(d, x = carat, y = price, text = paste("Clarity: ", clarity), mode = "markers", color = carat, size = carat, source="main")
  })

  output$hover <- renderPlotly({
    eventdat <- event_data('plotly_hover', source="main") # get event data from source main
    if(is.null(eventdat) == T) return(NULL)        # If NULL dont do anything
    point <- as.numeric(eventdat[['pointNumber']]) # Index of the data point being charted

    # draw plot according to the point number on hover
    plot_ly(  x = c(1,2,3), y = c(point, point*2, point*3), mode = "scatter")
  })
}
shinyApp(ui, server)

в этом примере использовать shiny binds for plotly. для каждого события наведения a POST запрос отправляется на сервер, затем сервер обновит всплывающую диаграмму. Это очень неэффективно, поэтому может не работать хорошо на медленном подключение.

приведенный выше код предназначен только для демонстрации и еще не протестирован. См. рабочий и гораздо более сложный пример здесьисточник).

Решение 2: Javascript

Да, вы можете сделать это с помощью плотный Javascript API.

короткий ответ:

  1. создайте свой график с помощью R или Python или любой другой поддерживаемый язык.
  2. вставить график в новую HTML-страницу и добавить функцию обратного вызова, как показано в примере ниже. Если у вас есть хорошие знания о дом, вы также можете добавить JS в исходный HTML вместо создания нового.
  3. нарисуйте всплывающий график внутри функции обратного вызова,которая принимает параметры, содержащие данные точки данных при наведении.

подробности

Как упоминалось в @MLavoie, показан хороший пример в plotly.Ховер-события

давайте покопаемся в коде. В js-файле есть простая функция обратного вызова, прикрепленная к Plot:

Plot.onHover = function(message) {
var artist = message.points[0].x.toLowerCase().replace(/ /g, '-');

var imgSrc = blankImg;
if(artistToUrl[artist] !== undefined) imgSrc = artistToUrl[artist];

Plot.hoverImg.src = imgSrc;
};

выше artistToUrl - огромный объект, заполненный строкой base64, которую я не буду вставлять здесь, чтобы переполнить сообщение. Но вы можете увидеть его на вкладке JS страницы примера. Он имеет такую структуру:

var artistToUrl = { 'bob-dylan': 'data:image/jpeg;base64,/...',...}

пример:

для демонстрации, я готовлю простой пример здесь (щелкните, чтобы попробовать):

<!DOCTYPE html>
<html>
<head>
   <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>
<body>
<iframe id="plot" style="width: 900px; height: 600px;" src="https://plot.ly/~jackp/10816.embed" seamless></iframe>
<div id="myDiv"></div>
<script>
(function main() {
var Plot = { id: 'plot', domain: 'https://plot.ly' };
Plot.onHover = function(message) {
    var y = message.points[0].y; /*** y value of the data point(bar) under hover ***/
    var line1 = {
      x: [0.25,0.5,1],           /*** dummy x array in popup-chart ***/
      y: [1/y, 2, y],            /*** dummy y array in popup-chart ***/
      mode: 'lines+markers'
    };
    var layout = {
      title:'Popup graph on hover',
      height: 400,
      width: 480
    };
    Plotly.newPlot('myDiv', [  line1 ], layout); // this finally draws your popup-chart
};
Plot.init = function init() {
    var pinger = setInterval(function() {
        Plot.post({task: 'ping'});
    }, 500);

    function messageListener(e) {
        var message = e.data;
        if(message.pong) {
            console.log('Initial pong, frame is ready to receive');
            clearInterval(pinger);
            Plot.post({
                'task': 'listen',
                'events': ['hover']
            });
        }
        else if(message.type === 'hover') {
            Plot.onHover(message);
        }
    }
    window.removeEventListener('message', messageListener);
    window.addEventListener('message', messageListener);
};
Plot.post = function post(o) {
    document.getElementById(Plot.id).contentWindow.postMessage(o, Plot.domain);
};

Plot.init();
})();
</script>
</body>
</html>

- это доработанная от poltly.Ховер-события пример для python. Вместо попиных изображение, изменить onhover обратный вызов для построения кривой на основе y значение каждого бара.

основная диаграмма генерируется python и вставляется здесь как iframe. Вы можете сделать свой собственный на любом языке, включая R. На этой странице мы добавляем <div id="myDiv"></div> и используйте plotly.js для рисования popup-chart whithin it.

экспорт кадра данных R в JS enviornment

блестящие использует jsonlite преобразование R объекты json и отправить их клиенту. Мы можем использовать тот же механизм для упаковки и отправки фрейма данных, чтобы обратный вызов JS мог использовать данные для отображения всплывающей диаграммы.

сервер.r

output$json <- reactive({
  paste('<script>data =', RJSONIO::toJSON(your_data_frame, byrow=T, colNames=T),'</script>')

пользовательский интерфейс.r

fluidPage(..., htmlOutput("json"), ...)

в функции обратного вызова JS вы можете использовать data как и любой другой JS объекты.

Подробнее идет здесь и здесь.


если вы хотите придерживаться с R можно использовать Shiny получить почти нужный результат. При наведении указателя мыши на каждую точку изображение будет отображаться под основным сюжетом. В приведенном ниже примере я использовал первые три строки mtcars наборы данных. Чтобы запустить код, вам нужно только 3 логотипа / изображения, соответствующие названию первых трех строк (под mtcars$name, Mazda RX4, Mazda RX4 Wag, Datsun 710 в этом примере).

    library(shiny)
    library(plotly)

    datatest <- diamonds %>% count(cut)
    datatest$ImageNumber <- c(0, 1, 2, 3, 4)
    datatest$name <- c("Image0", "Image1", "Image2", "Image3", "Image4")


    ui <- fluidPage(
  plotlyOutput("plot"),
 # verbatimTextOutput("hover2"),
  #imageOutput("hover"),
  plotlyOutput("hover3")

)

server <- function(input, output, session) {
  output$plot <- renderPlotly({
  plot_ly(datatest, x = cut, y = n, type = "bar", marker = list(color = toRGB("black")))
  })

  selected_image <- reactive({
  eventdat <- event_data('plotly_hover', source = 'A')
  ImagePick <- as.numeric(eventdat[['pointNumber']]) 
  sub <- datatest[datatest$ImageNumber %in% ImagePick, ]
  return(sub)    
  })

 # output$hover2 <- renderPrint({
  #d <- event_data("plotly_hover")
  #if (is.null(d)) "Hover events appear here (unhover to clear)" else d
  #})

 # output$hover <- renderImage({
 # datag <- selected_image()
  #filename <- normalizePath(file.path('/Users/drisk/Desktop/temp',
        #                      paste(datag$name, '.png', sep='')))

  # Return a list containing the filename and alt text
 # list(src = filename,
 # alt = paste("Image number", datag$name))
 # }, deleteFile = FALSE) 

    output$hover3 <- renderPlotly({
datag <- selected_image()

    # draw plot according to the point number on hover
    plot_ly(data=datag,  x = ImageNumber, y = n, mode = "scatter")
  })

}
shinyApp(ui, server)

enter image description here


кажется, ответы, опубликованные не работают для вас @Adam_G. Я изучал подобные библиотеки для своей собственной работы и определил, что Plot.ly не всегда правильный путь, когда вы хотите дополнительных функций. Вы видели bokeh? Он в основном предназначен для этого типа задач и намного проще в реализации (также D3.библиотеку JS, как Plot.ly). Вот копия примера, который они опубликовали, где вы можете переместить ползунок, чтобы изменить график данных (аналогично примеру, опубликованному @gdlmx для Сюжет.ly, но вы можете использовать его без размещения на веб-сайте). Я добавил flexx пакет, чтобы вы могли использовать этот чистый Python (без JavaScript-он может переводить функции Python на JavaScript (CustomJS.from_py_func(callback)) https://github.com/zoofIO/flexx-notebooks/blob/master/flexx_tutorial_pyscript.ipynb):

from bokeh.io import vform
from bokeh.models import CustomJS, ColumnDataSource, Slider
from bokeh.plotting import figure, output_file, show
import flexx


output_file("callback.html")

x = [x*0.005 for x in range(0, 200)]
y = x

source = ColumnDataSource(data=dict(x=x, y=y))

plot = figure(plot_width=400, plot_height=400)
plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)

def callback(source=source):
    data = source.get('data')
    f = cb_obj.get('value') #this is the bokeh callback object, linked to the slider below
    x, y = data['x'], data['y']
    for i in range(len(x)):
        y[i] = x[i]**f #the slider value passed to this function as f will alter chart as a function of x and y
    source.trigger('change') #as the slider moves, the chart will change

slider = Slider(start=0.1, end=4, value=1, step=.1, title="power", callback=CustomJS.from_py_func(callback))


layout = vform(slider, plot)

show(layout)        

см. здесь фактический пример в действии: http://bokeh.pydata.org/en/0.10.0/docs/user_guide/interaction.html#customjs-for-widgets

для интеграции с событиями наведения см. здесь (from bokeh.models import HoverTool): http://bokeh.pydata.org/en/0.10.0/docs/user_guide/interaction.html#customjs-for-hover

наведите пример:

from bokeh.plotting import figure, output_file, show, ColumnDataSource
from bokeh.models import HoverTool

output_file("toolbar.html")

source = ColumnDataSource(
        data=dict(
            x=[1, 2, 3, 4, 5],
            y=[2, 5, 8, 2, 7],
            desc=['A', 'b', 'C', 'd', 'E'],
        )
    )

hover = HoverTool(
        tooltips=[
            ("index", "$index"),
            ("(x,y)", "($x, $y)"),
            ("desc", "@desc"),
        ]
    )

p = figure(plot_width=400, plot_height=400, tools=[hover], title="Mouse over the dots")

p.circle('x', 'y', size=20, source=source)

show(p)

глядя на 1-й код, вы можете поместить любую формулу, которую хотите под - где требуется. Вы можете получить наведение на измените график рядом с ним (hform(leftchart, rightchart) или над / под ним (vform(topchart, bottomchart)). Это передается как CustomJS, который bokeh использует для расширения и flexx позволяет писать его на Python.

альтернатива заключается в том, чтобы поместить все, что вы хотите настроить на hover tooltips использование HTML (хотя этот пример помещает изображения в словари вместо новых графиков из базовых данных): http://bokeh.pydata.org/en/0.10.0/docs/user_guide/tools.html#custom-tooltip