【问题标题】:Set marker size based on coordinate values, not pixels, in plotly R在plotly R中根据坐标值而不是像素设置标记大小
【发布时间】:2025-12-21 02:35:12
【问题描述】:

这篇文章 - Set marker size in plotly - 不幸的是,它对我正在寻找的内容没有帮助,并且是我能找到的关于该主题的唯一帖子。根据散点图大小参数的绘图文档:

"size(大于等于0的数字或数字数组),默认:6,设置标记大小(单位px)"

(以 px 为单位)对我来说是个问题。我想创建一个标记大小基于坐标值而不是像素的图,这样我可以增加图形的大小(例如,通过全屏筛选)并且点的大小也会增加。我目前的代码示例:

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))

如果您在 R 中创建此绘图,然后通过拖动 Rstudio 查看器窗口或以其他方式使绘图变大或变小,您会注意到标记保持完全相同的大小(10 像素),这令人沮丧。如果我可以让这些标记的直径 == 1(在 x 轴上)而不是设置为多个像素,我会很高兴。这可能吗?

任何帮助都非常感谢!!!

【问题讨论】:

    标签: r plotly r-plotly


    【解决方案1】:
    • 让我们从定义轴范围的图开始。

      p <- plot_ly() %>% layout(xaxis = list(range = c(1, 5)))
      
    • 现在添加一个 JavaScript 事件监听器,它可以捕获绘图布局中的任何变化

      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
      
    • 事件通过eventdata,我们可以从中获取新的轴范围。

      eventdata['xaxis.range[1]']
      
    • 由于我们事先不知道绘图的大小,所以我们通过轴线的大小来确定

      var plot_width = Plotly.d3.select('.xlines-above').node().getBBox()['width'];
      
    • 我们需要手动调用一次事件以确保正确初始化绘图

    初始情节

    放大

    完整的 R 代码

    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
    

    交互式 JavaScript 示例

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

    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>

    【讨论】:

    • 这与我迄今为止一直在使用的解决方法一致,因为我相当确定这是我必须采用的方法。然后,我当前的挑战是在 RShiny 中自动调整这些 plot_ly() 图形的高度和宽度。当 plot_ly() 中没有设置高度和宽度时,R Shiny 会动态调整宽度(高度是它自己的痛苦),但是设置了高度和宽度时当然不再是这种情况了。
    • 无论如何,一如既往地感谢您的意见。我看到你的例子是用 Javascript,我会用 R 代码实现类似的东西。
    • @Canovic:使用 R 代码查看更新的答案并自动确定宽度。
    最近更新 更多