R散点图极坐标图-如何单独旋转标签?

wfveoks0  于 2023-01-22  发布在  其他
关注(0)|答案(1)|浏览(141)

我尝试在R Plotly中制作一个简单的散点极坐标图,径向轴上有60多个分类标签。由于标签太多,放置标签的默认方法会导致它们在图表的顶部和底部相互重叠。我尝试将layout.polar.angularaxis.tickangle设置为90、-90等。但这会将所有标签旋转这个量。是否有方法A)将每个单独的标签旋转指定的值,这样我就可以将一些标签设置为-90度,其他的设置为-45度,其他的设置为0度,其他的设置为+45度,等等...或者B)将标签旋转保留为0(就像现在一样),但是稍微避开它们,这样它们就不会在图表的顶部和底部重叠了?我认为B会更容易,也是首选,但是不确定A或B是否可行。
下面是一个可重复的最小示例:

labs1 <- c("Red ", "Green ", "Blue ", "Yellow ", "Orange ", 
           "Purple ", "Pink ", "Black ", "White ", "Gray ")
labs2 <- c("Bookshelf", "Television", "Refridgerator", 
           "Toolbox", "Xylophone" , "Chromosome")
labs <- sample(paste0(rep(labs1, each = 6), rep(labs2, times = 10)), 60, replace = F)

df <- data.frame(Label = labs,
                 Radius = sample(0:100, 60, replace = F))

plot_ly(df) %>%
  add_trace(type = 'scatterpolar', mode = 'markers',
            r = ~Radius,
            theta = ~Label,
            fill = 'toself')

结果是这样的:

jc3wubiy

jc3wubiy1#

你已经问过我一段时间了,但我刚好碰到你的问题。我有个变通办法。
如果您想复制我使用过的数据,在调用df的创建之前,我使用了set.seed(35)
除了plotly之外,它还使用库htmlwidgets
我在布局中添加了margin以考虑标签旋转。这可能需要修改。(例如,如果标签很长,则边距需要更大。)
onRender调用中,发生了几件事。

  • 函数detector:检查是否存在彼此冲突的标签
  • 函数angler:提取与标注相关联的记号Angular
  • 函数quad:标识它正在处理的圆的象限,然后相应地旋转文本
  • lbls是图中每个标签的节点列表。
  • for循环循环遍历lbls,每两个标签发送一次给detector函数,如果检测到冲突,则将指示符发送给angler函数(该函数调用quad函数)。

它不完美,但它的工作。

library(plotly)
library(htmlwidgets)

plot_ly(df, type = 'scatterpolar', mode = 'markers',
        r = ~Radius, theta = ~Label, fill = 'toself') %>% 
  layout(margin = list(t = 75, r = 75, b = 75, l = 75)) %>% # make room for rotated labels
  onRender(
    "function(el, x) {
      function detector(r1, r2) {
        var r1 = r1.getBoundingClientRect(); /* catch associated real estate */
        var r2 = r2.getBoundingClientRect();
        return !(r2.left > r1.right ||       /* check for collisions */
                 r2.right < r1.left ||
                 r2.top > r1.bottom || 
                 r2.bottom < r1.top);
      }
      function angler(ind) {             /* get tick angle to rotate label */
        var par = lbls[ind].parentNode.parentNode.children[ind];
        var parAt = par.getAttribute('transform');
                                         /* send extracted angle to quad function*/
        quad(/(?<=rotate\\()[^)]*/.exec(parAt)[0], ind); /* using regex, after 'rotate(' -- except ')' */
      }
      function quad(ang, ind) {
        var ang = Math.abs(Number(ang)); /* make the angle a positive number */
        var gimmeXform = lbls[ind].getAttribute('transform'); /* extract values to modify */
        var xer = lbls[ind].getAttribute('x');  /* 3rd & 4th quad, ctrl rotation pt */
        var yer = lbls[ind].getAttribute('y');  /* 3rd & 4th quad, ctrl rotation pt */
        if(ang <= 90) {
          tang = ang * -.3;             /* rotate ang * -x, text-anchor: start */
          tanch = 'start';
        } else if (ang > 90 && ang <= 180) {
          tang = (180 - ang) * 0.3;       /* rotate ang * +x, text-anchor: end */
          tanch = 'end';
        } else if (ang > 180 && ang < 270) {
          tang = (ang - 180) * -.3;       /* rotate ang * -x, text-anchor: end */
          tang = '' + tang + ',' + xer + ',' + yer;    /* add rotation controls*/
          tanch = 'end';
        } else {
          tang = (360 - ang) * 0.3;     /* rotate ang * +x, text-anchor: start */
          tang = '' + tang + ',' + xer + ',' + yer;    /* add rotation controls*/
          tanch = 'start';
        } /* rotate text */
        lbls[ind].setAttribute('transform', gimmeXform + 'rotate(' + tang + ')');
        lbls[ind].setAttribute('text-anchor', tanch);
      }
      lbls = document.querySelectorAll('g.angularaxistick > text');
      inds = [];
      for(i = 0; i < lbls.length; i+=2) {
        if(detector(lbls[i], lbls[i + 1])) { /* if collision detected */
          angler(i);
          angler(i + 1);
        }
      }
    }"
  )

无论你是用一吨的真实的地产来规划它:

或者很少真实的地产,它改变了标签:

仅供参考:如果所有的标签重叠,它将无法工作。

相关问题