Beeswarm сюжет с направлением силы в javascript / d3 - PullRequest
7 голосов
/ 31 октября 2011

Вот статья о создании участков с пчелиным теплом в R .

Существует также пакет R для графика с пчелиным теплом .Следующие две картинки иллюстрируют некоторые возможности, которые предлагает этот пакет:

enter image description here

enter image description here

Однако я сейчас пытаюсь сделать это с помощьюнаправленная компоновка d3.js .

Мой план состоит в том, чтобы пользовательская гравитация притягивала точки к вертикальной линии и их правильное значение y, а обнаружение столкновений не позволяло точкам смещаться.1019 *

У меня есть полуработающий прототип :

enter image description here

К сожалению, я не могу найти способ обойти две проблемы -я подозреваю, что это действительно та же самая проблема:

  1. Мои точки продолжают перекрываться, по крайней мере, немного.

  2. После "перемешивания" продолжаетсяточки накапливаются в центре схемы, так как силы противодействия столкновениям и силы "приходят в центр" сражаются.

Я бы хотел, чтобы точки довольно быстроприйти к соглашению о том, где они должны жить, и не перекрываться.

Код силыЯ использую (на тот случай, если вы хотите увидеть это здесь, а не на bl.ocks.org):

force.on("tick", function(e) {
  var q,
    node,
    i = 0,
    n = nodes.length;

  var q = d3.geom.quadtree(nodes);

  while (++i < n) {
    node = nodes[i];
    q.visit(collide(node));
    xerr = node.x - node.true_x;
    yerr = node.y - node.true_y;
    node.x -= xerr*0.005;
    node.y -= yerr*0.9;
  }

  svg.selectAll("circle")
      .attr("cx", function(d) { return d.x; })
      .attr("cy", function(d) { return d.y; });
});

function collide(node) {
  var r = node.radius,
    nx1,
    nx2,
    ny1,
    ny2,
    xerr,
    yerr;

  nx1 = node.x - r;
  nx2 = node.x + r;
  ny1 = node.y - r;
  ny2 = node.y + r;

  return function(quad, x1, y1, x2, y2) {
    if (quad.point && (quad.point !== node)) {
      var x = node.x - quad.point.x,
          y = node.y - quad.point.y,
          l = Math.sqrt(x * x + y * y),
          r = node.radius + quad.point.radius;
      if (l < r) {
        // we're colliding.
        var xnudge, ynudge, nudge_factor;
        nudge_factor = (l - r) / l * .4;
        xnudge = x*nudge_factor;
        ynudge = y*nudge_factor;
        node.x -= xnudge;
        node.y -= ynudge;
        quad.point.x += xnudge;
        quad.point.y += ynudge;
      }
    }
    return x1 > nx2
        || x2 < nx1
        || y1 > ny2
        || y2 < ny1;
  };
}

Это мой первый набег на силовые макеты, так что извиняюсь, если это нубишка...

Ответы [ 2 ]

4 голосов
/ 07 ноября 2011

Ваши результаты выглядят довольно хорошо для меня. Но если вы хотите уменьшить вероятность наложения, попробуйте несколько вещей.

  1. Добавить некоторые отступы между узлами. В частности, у ваших кругов есть штрих, и половина этого удара будет выходить за пределы радиуса круга. Поэтому, если вы хотите избежать наложения штрихов, вам понадобится хотя бы один пиксель заполнения, когда вы вычисляете r, складывая два радиуса вместе. (Это предполагает, что у вас есть один штрих на каждом круге, что добавляет 0,5 пикселя к каждому радиусу.)

  2. Используйте .5 вместо .4 при вычислении nudge_factor. Это делает разрешение перекрытия более сильным, раздвигая любые перекрывающиеся круги так, чтобы они больше не перекрывались. Если вы используете значение меньше чем .4, решение будет немного более стабильным, но оно будет сходиться медленнее, поскольку круги все еще немного перекрываются даже после смещения.

  3. Запуск разрешения столкновения несколько раз за такт. В настоящее время вы используете разрешение столкновений, а затем применяете собственную гравитацию (в направлении true_x и true_y). Если вы запускаете разрешение столкновений несколько раз за такт, это делает разрешение столкновений более сильным по отношению к гравитации.

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

1 голос
/ 07 февраля 2013

Простая реализация

Вот еще одна реализация: http://bl.ocks.org/4732279

enter image description here

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

Эту реализацию можно улучшить, заменив обычно распределенное случайное дрожание интеллектуальной стратегией. Но для моей цели этого было достаточно.

  1. Общее количество итераций посетителя столкновения напрямую влияет на вероятность столкновения в конечном состоянии.
  2. Увеличение стандартного отклонения нормального распределения может помочь визуализации сойтись и в решении без столкновений также быстрее, но, конечно, может потребоваться больше вертикального пространства в конце.

Чуть более полнофункциональный, но более сложный ...

Здесь немного более размыто (с осью, масштабированием и т. Д.): http://bl.ocks.org/4734864

enter image description here

GIF анимация:

enter image description here

...