javascript - D3 堆积段图,按总数排序

标签 javascript d3.js

在过去的几天里,我一直在尝试创建一个堆积段图,在所有三 Angular 函数和键堆叠之间,应用看似最基本的调整对我来说真的是一场噩梦。目前,我的分割图表中的数据是根据 Active equity 降序排列的。我想将其更改为按 total 对数据进行排序。

const csvData = `State,Active equity,Passive equity,Fixed income,Mixed assets
BlackRock,1,17,0,0
Fidelity,13,2,0,0
SSgA,12,0,0,0
Hang Seng,11,0,0,0
UBS,9,0,0,1
Schroders,6,0,2,1
JP Morgan,5,2,0,1
Value Partners,1,0,6,0
First State,5,0,0,0
Invesco,4,1,0,0
HSBC,1,1,1,1
DBS,0,2,1,0
BOCI,1,1,1,0
CSOP,0,2,1,0
Principal,1,1,0,0
Allianz,2,1,0,0
Yuanta,0,2,1,0
Manulife,1,0,1,0
Aberdeen,2,0,0,0
Mirae,1,1,0,0
 ,0,0,0,0`;

//const data = d3.csvParse(csvData, d3.autoType);

var margins = {top:20, bottom:300, left:30, right:100};

var height = 600;
var width = 900;

var totalWidth = width+margins.left+margins.right;
var totalHeight = height+margins.top+margins.bottom;

var outerRadius = (400 / 2);
var innerRadius = 15;

var svg = d3.select('body')
    .append('svg')
    .attr('width', totalWidth)
    .attr('height', totalHeight);

var graphGroup = svg.append('g')
    .attr('transform', "translate(250,250)");

    var x = d3.scaleBand()
        .range([0, 2 * Math.PI])
        .align(0);

    var y = d3.scaleRadial()
        .range([innerRadius, outerRadius]);

    var z = d3.scaleOrdinal()
        .range(["#003366", "#4f81b9", "#95b3d7", "#f6d18b"]);

    d3.csv("csvData", function(d, i, columns) {
      for (i = 1, t = 0; i < columns.length; ++i) t += d[columns[i]] = +d[columns[i]];
      d.total = t;
      return d;
    }, function(error, data) {
      if (error) throw error;

      weave(data, function(a, b) { return b[data.columns[6]] -  a[data.columns[6]]; });
      x.domain(data.map(function(d) { return d.State; }));
      y.domain([0, d3.max(data, function(d) { return d.total; })*1.3]);
      z.domain(data.columns.slice(1));

      graphGroup.append('g')
      .selectAll('g')
      .data(d3.stack().keys(data.columns.slice(1))(data))
      .enter().append("g")
      .selectAll(".bg-arc2")
      .data(function(d) { return d; })
        .enter().append("path")
            .attr("d", d3.arc()
                .innerRadius(innerRadius)
                .outerRadius(outerRadius+2)
                .startAngle(function(d) { return x(d.data.State); })
                .endAngle(function(d) { return x(d.data.State) + x.bandwidth()*.90; })
                .padAngle(0.1)
                .padRadius(innerRadius))
            .attr('class','bg-arc2')
            .attr('fill','none')
            .attr('stroke-width','4px')
            .attr('stroke','#003366');

graphGroup.append('circle')
    .attr('cx',0)
    .attr('cy',0)
    .attr('r',200)
    .style('fill','#d9d9d9');


graphGroup.append('g')
.selectAll('g')
.data(d3.stack().keys(data.columns.slice(1))(data))
.enter().append("g")
.selectAll(".bg-arc")
.data(function(d) { return d; })
  .enter().append("path")
      .attr("d", d3.arc()
          .innerRadius(innerRadius)
          .outerRadius(outerRadius)
          .startAngle(function(d) { return x(d.data.State); })
          .endAngle(function(d) { return x(d.data.State) + x.bandwidth()*.95; })
          .padAngle(0.1)
          .padRadius(innerRadius))
      .attr('class','bg-arc')
      .attr('fill','#fff');

graphGroup.append('circle')
    .attr('cx',0)
    .attr('cy',0)
    .attr('r',innerRadius)
    .style('fill','#fff');

var stackedData = d3.stack().keys(data.columns.slice(1))(data);
var stackedData2 = stackedData.sort(function(a,b) { return d3.descending(a[0].data.total, b[0].data.total)});
console.log(stackedData[0][0].data.total)
console.log(stackedData2);

      graphGroup.append("g")
        .selectAll("g")
        .data(stackedData)
        .enter().append("g")
          .attr("fill", function(d) { return z(d.key); })
        .selectAll("path")
        .data(function(d) { return d; })
          .enter().append("path")
            .attr("d", d3.arc()
                .innerRadius(function(d) { return y(d[0]); })
                .outerRadius(function(d) { return y(d[1]); })
                .startAngle(function(d) { return x(d.data.State); })
                .endAngle(function(d) { return x(d.data.State) + x.bandwidth()*.95; })
                .padAngle(0.04)
                .padRadius(innerRadius));

      var label = graphGroup.append("g")
        .selectAll("g")
        .data(data)
        .enter().append("g")
          .attr("text-anchor", "middle")
          .attr("transform", function(d) { return "rotate(" + ((x(d.State) + x.bandwidth() / 2) * 180 / Math.PI - 90) + ")translate(" + (outerRadius+25) + ",0)"; });



      label.append("text")
          .attr("transform", function(d) { return (x(d.State) + x.bandwidth() / 2 + Math.PI / 2) % (2 * Math.PI) < Math.PI ? "rotate(90)translate(0,16)" : "rotate(-90)translate(0,-9)"; })
          .text(function(d) { return d.State; });

      var yAxis = graphGroup.append("g")
          .attr("text-anchor", "end");

      var yTick = yAxis
        .selectAll("g")
        .data(y.ticks(10).slice(1))
        .enter().append("g");


    });

    function weave(array, compare) {
      var i = -1, j, n = array.sort(compare).length, weave = new Array(n);
      while (++i < n) weave[i] = array[(j = i << 1) >= n ? (n - i << 1) - 1 : j];
      while (--n >= 0) array[n] = weave[n];
    }
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://gist.githubusercontent.com/mbostock/3686329aa6e1f5938df8eef12ec353fe/raw/1ab722df937c3ac86cac8292e34cfc7279b017f8/d3-scale-radial.js"></script>

相关代码在这里:

var stackedData = d3.stack().keys(data.columns.slice(1))(data);
var stackedData2 = stackedData.sort(function(a,b) { return d3.descending(a[0].data.total, b[0].data.total)});
console.log(stackedData[0][0].data.total)
console.log(stackedData2);

我通过控制台日志进行了检查,以确保我在正确的位置进行切片,正如 console.log(stackedData[0][0].data.total) 所确认的那样它返回了 18 的正确值。

但是我无法按需要应用排序。数据仍按 Active equity 而非总数排序。

问题

堆叠径向图的默认排序似乎是第一个变量是什么。在我的例子中,它是 Active equity。考虑到这一点,根据我上面的进展,是什么阻止我应用 data.total 的降序排序而不是默认排序?

最佳答案

您的问题不清楚:您是在谈论对每个切片中的片段进行排序,还是在谈论对切片本身进行排序?

对于第一种情况,有一个名为order的方法,您可以将其与堆栈生成器一起使用(链接转到 v5 文档,它与 v4 有点不同)。但是,因为您说“我想更改 [顺序] 以按总计对数据进行排序”,所以在我看来您是在谈论对切片进行排序。如果这是正确的,有两个观察结果:它不是按 Active equity 排序,现在它只是原始数据数组中对象的顺序。

要按 total 排序,您只需更改该数组:

data.sort(function(a, b) {
  return b.total - a.total;
});

此外,去掉 weave 函数。

结果如下:

(function(global, factory) {
  typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("d3-scale")) :
    typeof define === "function" && define.amd ? define(["exports", "d3-scale"], factory) :
    (factory(global.d3 = global.d3 || {}, global.d3));
}(this, function(exports, d3Scale) {
  'use strict';

  function square(x) {
    return x * x;
  }

  function radial() {
    var linear = d3Scale.scaleLinear();

    function scale(x) {
      return Math.sqrt(linear(x));
    }

    scale.domain = function(_) {
      return arguments.length ? (linear.domain(_), scale) : linear.domain();
    };

    scale.nice = function(count) {
      return (linear.nice(count), scale);
    };

    scale.range = function(_) {
      return arguments.length ? (linear.range(_.map(square)), scale) : linear.range().map(Math.sqrt);
    };

    scale.ticks = linear.ticks;
    scale.tickFormat = linear.tickFormat;

    return scale;
  }

  exports.scaleRadial = radial;

  Object.defineProperty(exports, '__esModule', {
    value: true
  });
}));

const csvData = `State,Active equity,Passive equity,Fixed income,Mixed assets
BlackRock,1,17,0,0
Fidelity,13,2,0,0
SSgA,12,0,0,0
Hang Seng,11,0,0,0
UBS,9,0,0,1
Schroders,6,0,2,1
JP Morgan,5,2,0,1
Value Partners,1,0,6,0
First State,5,0,0,0
Invesco,4,1,0,0
HSBC,1,1,1,1
DBS,0,2,1,0
BOCI,1,1,1,0
CSOP,0,2,1,0
Principal,1,1,0,0
Allianz,2,1,0,0
Yuanta,0,2,1,0
Manulife,1,0,1,0
Aberdeen,2,0,0,0
Mirae,1,1,0,0
 ,0,0,0,0`;

const data = d3.csvParse(csvData, function(d, i, columns) {
  for (i = 1, t = 0; i < columns.length; ++i) t += d[columns[i]] = +d[columns[i]];
  d.total = t;
  return d;
});

data.sort(function(a, b) {
  return b.total - a.total;
});

var margins = {
  top: 20,
  bottom: 300,
  left: 30,
  right: 100
};

var height = 600;
var width = 900;

var totalWidth = width + margins.left + margins.right;
var totalHeight = height + margins.top + margins.bottom;

var outerRadius = (400 / 2);
var innerRadius = 15;

var svg = d3.select('body')
  .append('svg')
  .attr('width', totalWidth)
  .attr('height', totalHeight);

var graphGroup = svg.append('g')
  .attr('transform', "translate(250,250)");

var x = d3.scaleBand()
  .range([0, 2 * Math.PI])
  .align(0);

var y = d3.scaleRadial()
  .range([innerRadius, outerRadius]);

var z = d3.scaleOrdinal()
  .range(["#003366", "#4f81b9", "#95b3d7", "#f6d18b"]);


x.domain(data.map(function(d) {
  return d.State;
}));
y.domain([0, d3.max(data, function(d) {
  return d.total;
}) * 1.3]);
z.domain(data.columns.slice(1));

graphGroup.append('g')
  .selectAll('g')
  .data(d3.stack().keys(data.columns.slice(1))(data))
  .enter().append("g")
  .selectAll(".bg-arc2")
  .data(function(d) {
    return d;
  })
  .enter().append("path")
  .attr("d", d3.arc()
    .innerRadius(innerRadius)
    .outerRadius(outerRadius + 2)
    .startAngle(function(d) {
      return x(d.data.State);
    })
    .endAngle(function(d) {
      return x(d.data.State) + x.bandwidth() * .90;
    })
    .padAngle(0.1)
    .padRadius(innerRadius))
  .attr('class', 'bg-arc2')
  .attr('fill', 'none')
  .attr('stroke-width', '4px')
  .attr('stroke', '#003366');

graphGroup.append('circle')
  .attr('cx', 0)
  .attr('cy', 0)
  .attr('r', 200)
  .style('fill', '#d9d9d9');


graphGroup.append('g')
  .selectAll('g')
  .data(d3.stack().keys(data.columns.slice(1))(data))
  .enter().append("g")
  .selectAll(".bg-arc")
  .data(function(d) {
    return d;
  })
  .enter().append("path")
  .attr("d", d3.arc()
    .innerRadius(innerRadius)
    .outerRadius(outerRadius)
    .startAngle(function(d) {
      return x(d.data.State);
    })
    .endAngle(function(d) {
      return x(d.data.State) + x.bandwidth() * .95;
    })
    .padAngle(0.1)
    .padRadius(innerRadius))
  .attr('class', 'bg-arc')
  .attr('fill', '#fff');

graphGroup.append('circle')
  .attr('cx', 0)
  .attr('cy', 0)
  .attr('r', innerRadius)
  .style('fill', '#fff');

var stackedData = d3.stack()
  .keys(data.columns.slice(1))
  (data);


graphGroup.append("g")
  .selectAll("g")
  .data(stackedData)
  .enter().append("g")
  .attr("fill", function(d) {
    return z(d.key);
  })
  .selectAll("path")
  .data(function(d) {
    return d;
  })
  .enter().append("path")
  .attr("d", d3.arc()
    .innerRadius(function(d) {
      return y(d[0]);
    })
    .outerRadius(function(d) {
      return y(d[1]);
    })
    .startAngle(function(d) {
      return x(d.data.State);
    })
    .endAngle(function(d) {
      return x(d.data.State) + x.bandwidth() * .95;
    })
    .padAngle(0.04)
    .padRadius(innerRadius));

var label = graphGroup.append("g")
  .selectAll("g")
  .data(data)
  .enter().append("g")
  .attr("text-anchor", "middle")
  .attr("transform", function(d) {
    return "rotate(" + ((x(d.State) + x.bandwidth() / 2) * 180 / Math.PI - 90) + ")translate(" + (outerRadius + 25) + ",0)";
  });



label.append("text")
  .attr("transform", function(d) {
    return (x(d.State) + x.bandwidth() / 2 + Math.PI / 2) % (2 * Math.PI) < Math.PI ? "rotate(90)translate(0,16)" : "rotate(-90)translate(0,-9)";
  })
  .text(function(d) {
    return d.State;
  });

var yAxis = graphGroup.append("g")
  .attr("text-anchor", "end");

var yTick = yAxis
  .selectAll("g")
  .data(y.ticks(10).slice(1))
  .enter().append("g");


function weave(array, compare) {
  var i = -1,
    j, n = array.sort(compare).length,
    weave = new Array(n);
  while (++i < n) weave[i] = array[(j = i << 1) >= n ? (n - i << 1) - 1 : j];
  while (--n >= 0) array[n] = weave[n];
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>

关于javascript - D3 堆积段图,按总数排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57368484/

相关文章:

javascript - MQTT JavaScript 客户端未连接(协议(protocol)错误?)

javascript - 使用javascript映射一个对象数组,它返回一个未定义的值

javascript - 如何使用 JS 变量更新 Jquery 日历

javascript - 面向对象的 D3 JS - 如何选择对象?

javascript - Magento 2 CE : Implement User Defined Quantity per Selection for Bundle Option of Select Type Checkbox

返回 1969 年 12 月 31 日的 Javascript Date 对象

d3.js - 使用 Nuxt/Vue 加载 D3

javascript - 如何在 Plottable.js/D3.js 制作的 Piechart 中启用 qtip2

javascript - Meteor 发布/订阅延迟时间

javascript - 如何禁用 Iframe 内的右键单击