Линейные сегменты или прямоугольники с информацией о зависании в R

Я хочу создать интерактивную фигуру сегментов или прямоугольников, так что каждый сегмент или прямоугольник дает различную информацию, когда пользователь наводит на нее свою мышь. Я посмотрел в htmlwidgets showcase, и я подумал, что сюжетно выглядит многообещающим. (Я открыт для других методов, связанных с R).

Ниже приведен простой пример. Я могу создать график конечных точек (t1 и t2), которые предоставляют информацию наведения. Но я хотел бы, чтобы информация наведения отображалась в любое время, когда пользователь наводил курсор на пространство между двумя конечными точками).

Я могу добавить сегмент линии, используя add_trace(), но я не могу заставить зависание работать. И если я добавлю второй сегмент линии, я получаю сообщение об ошибке:

Error in plot_ly(data = mydat, x = t2, y = y, mode = "markers", hoverinfo = "text",  : 
  requires numeric/complex matrix/vector arguments

Я могу добавить прямоугольники с помощью layout(), но опять же, я не могу заставить зависание работать.

В случае, если кто-то предлагает способ заставить аргументы зависания работать для любого подхода, я также приветствовал бы предложения о том, как закодировать это для большого количества сегментов/прямоугольников (не только 2, как в этом простом примере),

Любые предложения?

mydat <- data.frame(t1=c(1, 3), t2=c(4, 5), y=c(1, 2), task=c("this", "that"))
library(plotly)

# attempt with one line segment - hover doesn't work
plot_ly(data=mydat, x=t2, y=y, mode="markers",
  hoverinfo="text", text=task) %>%
add_trace(data=mydat, x=t1, y=y, mode="markers",
  hoverinfo="text", text=task) %>%
add_trace(
  x=c(mydat$t1[1], mydat$t2[1]), y=c(mydat$y[1], mydat$y[1]), 
  mode="lines", hoverinfo="text", text=mydat$task[1])

# attempt with both line segments - 
# Error in plot_ly, requires numeric/complex matrix/vector arguments
plot_ly(data=mydat, x=t2, y=y, mode="markers",
  hoverinfo="text", text=task) %>%
add_trace(data=mydat, x=t1, y=y, mode="markers",
  hoverinfo="text", text=task) %>%
add_trace(
  x=c(mydat$t1[1], mydat$t2[1]), y=c(mydat$y[1], mydat$y[1]), 
  mode="lines", hoverinfo="text", text=mydat$task[1]) %*%
add_trace(
  x=c(mydat$t1[2], mydat$t2[2]), y=c(mydat$y[2], mydat$y[2]), 
  mode="lines", hoverinfo="text", text=mydat$task[2])

# attempt with rectangles - hover doesn't work
plot_ly(data=mydat, x=t2, y=y, mode="markers",
  hoverinfo="text", text=task) %>%
add_trace(data=mydat, x=t1, y=y, mode="markers",
  hoverinfo="text", text=task) %>%
layout(shapes=list(
  list(type="rect", x0=mydat$t1[1], x1=mydat$t2[1], xref="x",
    y0=mydat$y[1], y1=mydat$y[1]+0.1, yref="y",
    hoverinfo="text", text=mydat$task[1]),
  list(type="rect", x0=mydat$t1[2], x1=mydat$t2[2], xref="x",
    y0=mydat$y[2], y1=mydat$y[2]+0.1, yref="y",
    hoverinfo="text", text=mydat$task[2])
  ))

Ответ 1

Вы можете использовать highcharter, который обертывает библиотеку Highcharts.js.

# note i'm renaming y to x, since that how highcharts will treat this dataset
mydat <- data.frame(t1=c(1, 3), t2=c(4, 5), x=c(1, 2), task=c("this", "that"))

library(highcharter)

highchart(hc_opts = list(
  chart = list(inverted = 'true')
  )) %>% 
  hc_add_series_df(data = mydat, type = 'columnrange',
                   group = task,
                   x = x, low = t1, high = t2)

введите описание изображения здесь

Работает отлично, без дополнительного кода, необходимого для большего количества баров:

set.seed(123)
n <- 20
largedat <- data.frame(t1 = runif(n, 1, 10), 
                       x = 1:n,
                       task = paste('series', 1:n))
largedat$t2 <- largedat$t1 + runif(n, 2, 5)

highchart(hc_opts = list(
  chart = list(inverted = 'true')
  )) %>% 
  hc_add_series_df(data = largedat, type = 'columnrange',
                   group = task,
                   x = x, low = t1, high = t2) %>%
  hc_legend(enabled = FALSE) # disable legend on this one

введите описание изображения здесь

Ответ 2

Вы можете получить информацию о курсе вдоль линии, поставив много точек на линии. Вам не нужно размещать маркеры в этих точках, поэтому сюжет по-прежнему будет выглядеть одинаково:

NP=100
mydat <- data.frame(t1=seq(1,3,len=NP), t2=seq(4,5,len=NP), y1=rep(1,NP),  y2=rep(2,NP))

plot_ly(data=mydat) %>%
  add_trace(x=t1, y=y1, mode="lines", hoverinfo="text", text="hello") %>%
  add_trace(x=t2, y=y2, mode="lines", hoverinfo="text", text="there") 

введите описание изображения здесь

Расширяя это до прямоугольников, мы можем сделать

plot_ly() %>%
  add_trace(x=c(seq(1,3,len=NP), rep(3,NP), seq(3,1,len=NP), rep(1,NP)),
            y=c(rep(1,NP), seq(1,2,len=NP), rep(2,NP), seq(2,1,len=NP)),
            mode="lines", hoverinfo="text", text="A sublime rectangle") %>%
  layout(hovermode = 'closest')

Или, если вы хотите пометить каждую сторону прямоугольника отдельно

plot_ly() %>%
  add_trace(x=c(seq(1,3,len=NP), rep(3,NP), seq(3,1,len=NP), rep(1,NP)),
            y=c(rep(1,NP), seq(1,2,len=NP), rep(2,NP), seq(2,1,len=NP)),
            mode="lines", hoverinfo="text", 
            text=c(rep("bottom",NP),rep("right",NP),rep("top",NP),rep("left",NP))) %>%
  layout(hovermode = 'closest')

И для диагональных линий:

plot_ly() %>%
  add_trace(x=seq(1,3,len=NP), y=seq(1,2,len=NP), mode="lines", hoverinfo="text", text="diagonal") %>%
  layout(hovermode = 'closest')

В ответ на ваш дополнительный вопрос "как закодировать это для большого количества сегментов/прямоугольников"? Вы не указываете, сколько из них "большое число". Но то, что я могу сказать, заключается в том, что добавление многих отдельных следов в plotly может сделать его довольно медленным для обработки. Невыносимо медленно с большим количеством следов. Путь вокруг этого состоит в том, чтобы добавить все ваши сегменты линии в одну трассу. Для некоторых эффектов (например, для управления цветами отдельно от групп и записей легенд) может потребоваться добавить значения NA, разделяющие сегменты, и использовать опцию connectgaps=FALSE в add_trace (см. здесь и здесь), но это не обязательно для этого простого случая. Вот минимальный пример

line1 <- data.frame(x=seq(3.5,4.5,len=NP), y=rep(2.5,NP), text="hello")
line2 <- data.frame(x=seq(3,3.5,len=NP), y=seq(3,2.5,len=NP), text="mouth")
line3 <- data.frame(x=seq(4.5,5,len=NP), y=seq(2.5,3,len=NP), text="mouth")
line4 <- data.frame(x=rep(4,NP), y=seq(2.75,3.5,len=NP), text="nose")
rect1 <- data.frame(x=c(seq(2,6,len=NP), rep(6,NP), seq(6,2,len=NP), rep(2,NP)),
                    y=c(rep(2,NP), seq(2,4.5,len=NP), rep(4.5,NP), seq(4.5,2,len=NP)),
                    text="head")
rect2 <- data.frame(x=c(seq(2.5,3.5,len=NP), rep(3.5,NP), seq(3.5,2.5,len=NP), rep(2.5,NP)),
                    y=c(rep(3.5,NP), seq(3.5,4,len=NP), rep(4,NP), seq(4,3.5,len=NP)),
                    text="left eye")
rect3 <- data.frame(x=c(seq(4.5,5.5,len=NP), rep(5.5,NP), seq(5.5,4.5,len=NP), rep(4.5,NP)),
                    y=c(rep(3.5,NP), seq(3.5,4,len=NP), rep(4,NP), seq(4,3.5,len=NP)),
                    text="right eye")

trace_dat <- rbind(line1, line2, line3, line4, rect1, rect2, rect3)

plot_ly(data=trace_dat, x=x, y=y, mode="lines", hoverinfo="text", text=text, group = text) %>%
  layout(hovermode = 'closest')

введите описание изображения здесь