【问题标题】:Place a chart in plotly popup在 plotly 弹出窗口中放置图表
【发布时间】:2016-08-13 20:02:19
【问题描述】:

虽然我也愿意使用 Python 版本,但我正在为 R 使用 plotly。当我将鼠标悬停在数据点上时,有没有办法让弹出窗口包含另一个图表?理想情况下,图表将根据数据创建,尽管我可以使用静态图像作为后备。

我不确定从哪里开始,并为没有 MWE 提前道歉。

【问题讨论】:

  • 这可能有助于plot.ly/python/hover-events
  • 我之前确实遇到过。我不明白情节是从哪里弹出的图像。
  • js文件里有

标签: python r plot plotly


【解决方案1】:

似乎发布的答案对您不起作用@Adam_G。我一直在为自己的工作探索类似的库,并确定Plot.ly 在您需要高级功能时并不总是正确的路径。你见过bokeh吗?它基本上是为这种类型的任务设计的,并且更容易实现(也是一个 D3.js 库,如 Plot.ly)。这是他们发布的示例的副本,您可以在其中移动滑块以更改数据图(类似于 @gdlmx 为 Plot.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://docs.bokeh.org/en/0.10.0/docs/user_guide/interaction.html#customjs-for-widgets

要与悬停事件集成,请参见此处 (from bokeh.models import HoverTool): http://docs.bokeh.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)

查看第一个代码,您可以将任何您想要的公式放在def callback 函数下 - 需要一些玩弄。您可以通过悬停来更改其旁边的图形(hform(leftchart, rightchart) 或上方/下方 (vform(topchart, bottomchart))。这是作为 CustomJS 传递的,bokeh 用于允许可扩展性,flexx 允许您编写它在 Python 中。

另一种方法是使用 HTML 将您想要自定义的任何内容放在悬停 tooltips 上(尽管此示例将图像放置在字典中,而不是从基础数据中获取新图):http://docs.bokeh.org/en/0.10.0/docs/user_guide/tools.html#custom-tooltip

【讨论】:

  • @Adam_G 当有人将鼠标悬停在栏上时,您可以发布您想要显示的确切内容吗?线?无论您在寻找什么。让我们这些试图回答您的问题的人对在此处发布什么有所了解......我在工作中正在做类似的事情,所以不要介意改进我的替代答案。我们不知道您是从事金融业(日期 x 轴/价格 y 轴)还是其他...
【解决方案2】:

解决方案 1:坚持 R

感谢@MLavoie。以下示例使用纯 R 创建两个情节,“主情节”和对第一个的悬停事件作出反应的“悬停”。

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对于每个悬停事件,都会向服务器发送一个POST 请求,然后服务器将更新弹出图表。它的效率非常低,因此在慢速连接时可能无法正常工作。

以上代码仅用于演示,尚未测试。查看一个更复杂的工作示例here(带有source)。

解决方案 2:Javascript

是的,您可以使用 plotly Javascript API

简答

  1. 使用RPython 或任何其他受支持的语言创建图表。
  2. 将图表插入新的 HTML 页面并添加回调函数,如下例所示。如果您对DOM有很好的了解,您也可以将JS添加到原始HTML中,而不是创建新的。
  3. 在回调函数内绘制弹出图,该函数接受包含悬停数据点数据的参数。

详情

正如@MLavoie 提到的,一个很好的例子显示在plotly.hover-events

让我们深入研究代码。在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,/...',...}

工作示例:

为了演示,我准备了一个简单的例子here(点击试试):

<!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.hover-events 的 python 示例修改而来的。我没有弹出图像,而是更改了onhover 回调以根据每个条形的y 值绘制曲线。

主图表由python生成,并在此处插入为iframe。您可以使用任何语言(包括R)制作自己的。在这个页面中,我们添加了一个 &lt;div id="myDiv"&gt;&lt;/div&gt; 并使用 plotly.js 在其中绘制弹出图表。

将R数据框导出到JS环境

Shiny 使用jsonliteR 对象转换为json 并将它们发送给客户端。我们可以使用相同的机制来打包和发送我们的数据帧,以便 JS 回调可以使用数据来渲染弹出图表。

服务器.r

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

ui.r

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

在 JS 回调函数中,您可以使用 data 作为任何其他 JS 对象。

更多详情转到herehere

【讨论】:

  • 谢谢,但有没有办法在情节中做到这一点?如果可能,我想避免使用 javascript。
  • Plotly 本身就是一个 JS 库。其他语言 API 只是 JS lib 的包装器。正如here 所说:“Python API 是一个旨在与 Plotly.js 库交互的包......”
  • 那么有没有办法在 R 代码中做到这一点?如上所述,我还想动态创建图表,而不是使用图像。
  • 我对R API不熟悉,但是在源代码repo上搜索了一下,一无所获。
  • 如果有办法在 R、Python 或任何其他高级方式中做到这一点,我会很高兴知道。
【解决方案3】:

如果你想坚持使用R,你可以使用Shiny 来获得几乎你想要的结果。当您将每个点悬停时,将在主图下渲染图像。对于下面的示例,我使用了 mtcars 数据集的前三行。要运行代码,您只需要与前三行名称对应的 3 个徽标/图像(在此示例中位于 mtcars$nameMazda RX4Mazda RX4 WagDatsun 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)

【讨论】:

  • 我很欣赏 R 代码,但这只是图像,不是图表,对吧?
  • 查看编辑。您没有可重复的示例,所以我只使用散点图,但您也可以使用图表。当您悬停一个点/条时,图像将悬停在主图下方。在示例中,我使用了 R 标志,但它可以是任何东西。您还可以指定图像的大小。显然你可以删除 output$hover2 调用;这只是为了锻炼。
  • 对不起,我没有关注。您能否编辑代码,以便在悬停时显示一个基本图表?
猜你喜欢
  • 2011-04-27
  • 1970-01-01
  • 2017-07-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多