这是一个很难处理的问题,因为不幸的是,并非所有类型的layers 都可以像layer_points 那样与add_tooltip 配合得很好。我不是 100% 清楚许多 ggvis 函数的所有实现细节,但我至少会引导您了解我对它的理解,以便您了解我是如何得出我的解决方案的。我将使用cars 数据集(默认情况下应在您的会话中可用)进行演示,但您只需进行一些小的调整即可将该方法应用于您自己的数据。
library(dplyr)
library(ggvis)
##
my_data <- cars %>%
group_by(speed) %>%
summarize(avg_dist = mean(dist))
##
R> my_data
Source: local data frame [19 x 2]
speed avg_dist
1 4 6.00000
2 7 13.00000
3 8 16.00000
4 9 10.00000
5 10 26.00000
6 11 22.50000
7 12 21.50000
8 13 35.00000
9 14 50.50000
10 15 33.33333
11 16 36.00000
12 17 40.66667
13 18 64.50000
14 19 50.00000
15 20 50.40000
16 22 66.00000
17 23 54.00000
18 24 93.75000
19 25 85.00000
我还将使用这些分组数据定义几个可视化 - bar_obj(条形图)和 point_obj(散点图);和一个函数vis_names,它将打印出传递给add_tooltip的数据对象的名称。
bar_obj <- my_data %>%
ggvis(x = ~speed, y = ~avg_dist) %>%
layer_bars()
##
point_obj <- my_data %>%
ggvis(x = ~speed, y = ~avg_dist) %>%
layer_points()
##
vis_names <- function(x) {
if(is.null(x)) return(NULL)
paste0(names(x),collapse="<br />")
}
从add_tooltip 内部打印出名称是准确确定正在传递的数据的好方法。对于point_obj,我们有:
point_obj %>% add_tooltip(vis_names,"hover")
这是我输入数据的列名。另一方面,为bar_obj 这样做会给我们:
bar_obj %>% add_tooltip(vis_names,"hover")
这绝对不是my_data 中的列名。如果我们定义另一个函数,
bar_info <- function(x) {
if(is.null(x)) return(NULL)
paste(
names(x)[1],
x[1,1],
names(x)[2],
x[1,2],
names(x)[3],
x[1,3],
names(x)[4],
x[1,4],
sep=" : ",
collapse="<br />")
}
我们可以得到更多细节:
bar_obj %>% add_tooltip(bar_info,"hover")
所以xmin_ 和xmax_ 显然是我们悬停在给定条的x 坐标,而stack_upr_ 和stack_lwr_ 是各自的y 坐标。从这里开始,我们只需要将条形坐标转换回原始输入数据my_data。
在ggplot2 中有一个我经常使用的简洁函数ggplot_build。当您在 ggplot 对象上调用此函数时,它会创建绘图(就像打印通常会做的那样),但还会返回有关构建绘图的所有内容的非常详细的信息 - 长轴范围、短轴范围等。 . 当您需要找到有关图表的非常精确的信息时,这非常有用。我怀疑ggvis 会有类似于ggplot_build 的东西,通过小插图我发现了函数get_data。在ggvis 对象上调用此函数会返回data.frames 列表,其中第一个元素是原始data.frame (my_data),第四个元素是data.frame,其中包含上面的条形图中显示了四列 - xmin_、xmax_、stack_upr_ 和 stack_lwr_。
从这里开始,我将定义一个最终函数,该函数将传递给add_tooltip 以与bar_obj 一起使用:
tooltip_bars <- function(x) {
if(is.null(x)) return(NULL)
bar_obj <- get("bar_obj",.GlobalEnv)
input_data <- get_data(bar_obj)[[1]]
bar_data <- get_data(bar_obj)[[4]]
xmin_col <- bar_data[,3]
row_idx <- which.min(abs(x[1,1]-xmin_col))
paste0(
paste0(names(input_data)[1],
": ",
format(input_data[row_idx,1],4)),
paste0(names(input_data)[2],
": ",
format(input_data[row_idx,2],4)),
collapse = "<br />")
}
这里发生了很多事情 - 它肯定不如 all_values 工具提示函数简洁,但它可以完成工作。这一行
bar_obj <- get("bar_obj",.GlobalEnv)
从全局环境中获取我们实际绘制的ggvis 对象的副本,并将其拉入我们的工具提示子函数的范围内。接下来的两行,
input_data <- get_data(bar_obj)[[1]]
bar_data <- get_data(bar_obj)[[4]]
存储我们的输入数据集(相当于my_data)和图中使用的条形坐标,即我们可以通过变量x 访问的内容。 xmin_col 是定义条形左侧 x 坐标的值列。通过将其与 x 中包含的 xmin_ 值进行比较,我们可以确定正在绘制哪一行数据,即
row_idx <- which.min(abs(x[1,1]-xmin_col))
对于此示例,可能有一种更简单的方法来比较我们的值 x[1,1] 与列 xmin_ 以确定当前悬停在的数据行。我使用了上面的which.min(...) 方法,因为我认为在xmin_ 值不完全是某个整数+ 0.5 的情况下它会更稳健,因为它们在这种情况下。尝试测试浮点数之间的精确相等有时可能会出现问题。
无论如何,我们可以使用我们创建的row_idx 来提取由我们悬停的条表示的原始数据行(即x 包含的内容),这在paste0(...) 部分中完成。结果如下所示:
bar_obj %>% add_tooltip(tooltip_bars,"hover")
由于某种原因,标签的格式不正确;尽管这与我使用的一般方法无关。
所以回到你最初的问题,为什么要显示 NULL 值,简短的回答是在 layer_points 可视化中传递给 add_tooltip 的数据对象与传递给它的数据对象不同在layer_bars 可视化中,因此调用x$amount 和x$pv 将返回NULL,因为x 中不存在这些列。
澄清一下,将此代码应用于您自己的数据,请确保您创建一个基础 ggvis 对象,就像我对 bar_obj 所做的那样,并将其名称替换为我在 get("bar_obj",.GlobalEnv) 中使用 "bar_obj" 的位置tooltip_bars 函数的行。