在plotly R中根据坐标值而不是像素设置标记大小像素、标记、而不是、根据

2023-09-06 07:19:00 作者:蔡文鸡腿堡

这篇文章 -

放大

使用R进行交互式数据可视化

完整的 R 代码

library("plotly")图书馆(htmlwidgets")p <- plot_ly() %>%add_trace(x = c(1.5, 3, 4.2),y = c(-2, 1, 2),类型='分散',模式='线+标记',showlegend = F) %>%add_trace(x = c(2, 3, 4.2),y = c(2, 0, -1),类型='分散',模式='线+标记',showlegend = F) %>%add_trace(x = c(1.5, 3, 4.2),y = c(1, 0.5, 1),类型='分散',模式='标记',showlegend = F) %>%布局(xaxis = 列表(范围 = c(1, 5)))javascript <-"标记大小 = 0.5;//x 轴单位var myPlot = document.getElementsByClassName('plotly')[0];函数调整大小(事件数据){var xaxis_stop = 5;var xaxis_start = 1;var plot_width = Plotly.d3.select('.xlines-above').node().getBBox()['width'];if (eventdata['xaxis.range[1]'] !== undefined) {var update = {'marker.size': marker_size * plot_width/(eventdata['xaxis.range[1]'] - eventdata['xaxis.range[0]'])};} 别的 {var update = {'marker.size': marker_size * plot_width/(xaxis_stop - xaxis_start)};}Plotly.restyle(myPlot, 更新, 2);}调整大小({事件数据:{}});myPlot.on('plotly_relayout', function(eventdata) {调整大小(事件数据);});"p <- htmlwidgets::prependContent(p,onStaticRenderComplete(javascript))p

交互式 JavaScript 示例

您可以从定义图形的高度/宽度和定义的轴范围开始,这样您就可以知道一个轴单位有多少像素.标记的大小最初会是您想要的大小.每当用户更改坐标轴范围时,都会触发 plotly_relayout 事件,您可以从 xaxis.range[1 中的 eventdata 检索新范围](开始)和 xaxis.range[1](结束)根据新的范围,您可以重新布局您的标记大小.

var plot_width = 500;var plot_height = 500;var margin_l = 100;var margin_r = 100;标记大小 = 0.5;//x 轴单位xaxis_start = 1;xaxis_stop = 5;变量跟踪1 = {x: [1.5, 2, 3, 4],y: [10, 15, 13, 17],模式:标记",标记:{尺寸:marker_size *(plot_width - margin_l - margin_r)/(xaxis_stop - xaxis_start)},传奇:假};变量跟踪2 = {x: [2, 3, 4, 4.5],y: [16, 5, 11, 10],模式:线"};变量跟踪3 = {x: [1.5, 2, 3, 4],y: [12, 9, 15, 12],模式:线条+标记",传奇:假};var 数据 = [trace1,trace2,trace3];可变布局 = {宽度:绘图宽度,高度:情节高度,利润: {l: 边距_l,r:margin_r},坐标轴:{范围:[1, 5]},传奇:假};Plotly.newPlot("myDiv", 数据, 布局);var refplot = document.getElementById("myDiv");refplot.on("plotly_relayout", function(eventdata) {if (eventdata["xaxis.range[1]"] !== undefined) {变量更新 = {marker.size":marker_size *(plot_width - margin_l - margin_r)/(eventdata["xaxis.range[1]"] - eventdata["xaxis.range[0]"])};} 别的 {变量更新 = {marker.size":marker_size *(plot_width - margin_l - margin_r)/(xaxis_stop - xaxis_start)};}Plotly.restyle("myDiv", update, 0);});

<head><script src="https://cdn.plot.ly/plotly-latest.min.js"></script></头><身体><div id="myDiv"></div></body>

This post - Set marker size in plotly - unfortunately did not help with what I was looking for, and is the only post on the topic I could find. Per the plotly documentation on the size parameter for scatter plots:

"size (number or array of numbers greater than or equal to 0), default: 6, Sets the marker size (in px)"

the (in px) is a problem for me. I'd like to create a plot where the marker sizes are based on the coordinate values, not pixels, that way I can increase the size of a graph (by full screening it, for example) and the points increase in size as well. An example of my code currently:

library(plotly)
mydf <- data.frame(x = rep(1:20, times = 20), y = rep(1:20, each = 20),
                   thesize = 10)
plot_ly(mydf) %>%
  add_trace(x = ~x, y = ~y, type = 'scatter', mode = 'markers', 
            marker = list(symbol = 'hexagon', size = ~thesize, opacity = 0.6))

If you create this plot in R, and then make the plot either larger or smaller by dragging the Rstudio viewer window, or in some other way, you'll notice that the markers stay exactly the same size (10 pixels), which is frustrating. I would love it if I could have these markers have diameter == 1 (on the x axis), rather than be set to a number of pixels. Is this possible?

Any help is super appreciated!!!

解决方案

Let's start with a plot with a defined axis range.

p <- plot_ly() %>% layout(xaxis = list(range = c(1, 5)))

Now add a JavaScript eventlistener which captures any changes in the plot's layout

javascript <- "
var myPlot = document.getElementsByClassName('plotly')[0];

function resize(eventdata) {
  // magic happens here
}

myPlot.on('plotly_relayout', function(eventdata) {
  resize(eventdata);
});
"
p <- htmlwidgets::prependContent(p, 
                                 onStaticRenderComplete(javascript))
p

The event passes eventdata from where we can get the new axis range.

eventdata['xaxis.range[1]']

Since we don't know the size of the plot in advance, we determine it via the size of the axis line

var plot_width = Plotly.d3.select('.xlines-above').node().getBBox()['width'];

We need to call the event once manually to make sure that plot is correctly initialized

Initial plot

Zoom in

Complete R code

library("plotly")
library("htmlwidgets")

p <- plot_ly() %>%
  add_trace(x = c(1.5, 3, 4.2),
            y = c(-2, 1, 2), 
            type = 'scatter',
            mode = 'lines+markers',
            showlegend = F) %>% 
  add_trace(x = c(2, 3, 4.2),
            y = c(2, 0, -1),
            type = 'scatter',
            mode = 'lines+markers',
            showlegend = F) %>%
  add_trace(x = c(1.5, 3, 4.2),
            y = c(1, 0.5, 1),
            type = 'scatter',
            mode = 'markers',
            showlegend = F) %>% 
  layout(xaxis = list(range = c(1, 5)))
javascript <- "
marker_size = 0.5; // x-axis units
var myPlot = document.getElementsByClassName('plotly')[0];

function resize(eventdata) {
  var xaxis_stop = 5;
  var xaxis_start = 1;
  var plot_width = Plotly.d3.select('.xlines-above').node().getBBox()['width'];
  if (eventdata['xaxis.range[1]'] !== undefined) {
    var update = {'marker.size': marker_size * plot_width / (eventdata['xaxis.range[1]'] - eventdata['xaxis.range[0]'])};
  } else {
    var update = {'marker.size': marker_size * plot_width / (xaxis_stop - xaxis_start)};
  }
  Plotly.restyle(myPlot, update, 2);
}
resize({eventdata: {}});

myPlot.on('plotly_relayout', function(eventdata) {
  resize(eventdata);
});
"
p <- htmlwidgets::prependContent(p, 
                                 onStaticRenderComplete(javascript))
p

Interactive JavaScript example

You could start with a defined height/width for your graph and a defined axis range, this way you would know how many pixels is one axis unit. The size of your markers would be initially in the size you want. Whenever a user changes the axis range, a plotly_relayout event will be triggered and you can retrieve the new ranges from the eventdata in xaxis.range[1] (start) and xaxis.range[1] (end) Based on the new ranges you can relayout your marker sizes.

var plot_width = 500;
var plot_height = 500;
var margin_l = 100;
var margin_r = 100;
marker_size = 0.5; // x-axis units
xaxis_start = 1;
xaxis_stop = 5;

var trace1 = {
  x: [1.5, 2, 3, 4],
  y: [10, 15, 13, 17],
  mode: "markers",
  marker: {
    size: marker_size *
      (plot_width - margin_l - margin_r) /
      (xaxis_stop - xaxis_start)
  },
  showlegend: false
};

var trace2 = {
  x: [2, 3, 4, 4.5],
  y: [16, 5, 11, 10],
  mode: "lines"
};

var trace3 = {
  x: [1.5, 2, 3, 4],
  y: [12, 9, 15, 12],
  mode: "lines+markers",
  showlegend: false
};

var data = [trace1, trace2, trace3];

var layout = {
  width: plot_width,
  height: plot_height,
  margin: {
    l: margin_l,
    r: margin_r
  },
  xaxis: {
    range: [1, 5]
  },
  showlegend: false
};

Plotly.newPlot("myDiv", data, layout);

var refplot = document.getElementById("myDiv");
refplot.on("plotly_relayout", function(eventdata) {
  if (eventdata["xaxis.range[1]"] !== undefined) {
    var update = {
      "marker.size": marker_size *
        (plot_width - margin_l - margin_r) /
        (eventdata["xaxis.range[1]"] - eventdata["xaxis.range[0]"])
    };
  } else {
    var update = {
      "marker.size": marker_size *
        (plot_width - margin_l - margin_r) /
        (xaxis_stop - xaxis_start)
    };
  }
  Plotly.restyle("myDiv", update, 0);
});

<head>
  <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>

<body>
  <div id="myDiv"></div>
</body>

 
精彩推荐