【发布时间】:2016-08-13 20:02:19
【问题描述】:
虽然我也愿意使用 Python 版本,但我正在为 R 使用 plotly。当我将鼠标悬停在数据点上时,有没有办法让弹出窗口包含另一个图表?理想情况下,图表将根据数据创建,尽管我可以使用静态图像作为后备。
我不确定从哪里开始,并为没有 MWE 提前道歉。
【问题讨论】:
-
我之前确实遇到过。我不明白情节是从哪里弹出的图像。
-
js文件里有
虽然我也愿意使用 Python 版本,但我正在为 R 使用 plotly。当我将鼠标悬停在数据点上时,有没有办法让弹出窗口包含另一个图表?理想情况下,图表将根据数据创建,尽管我可以使用静态图像作为后备。
我不确定从哪里开始,并为没有 MWE 提前道歉。
【问题讨论】:
似乎发布的答案对您不起作用@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
【讨论】:
感谢@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)。
是的,您可以使用 plotly Javascript API。
R 或Python 或任何其他受支持的语言创建图表。正如@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)制作自己的。在这个页面中,我们添加了一个 <div id="myDiv"></div> 并使用 plotly.js 在其中绘制弹出图表。
Shiny 使用jsonlite 将R 对象转换为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 对象。
如果你想坚持使用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)
【讨论】: