为了账号安全,请及时绑定邮箱和手机立即绑定

D3 堆叠条形图,每个堆叠由不同组设置不同的颜色,

D3 堆叠条形图,每个堆叠由不同组设置不同的颜色,

森林海 2023-08-24 17:56:16
问题我试图在 D3 (v5) 中获得一个堆叠条形图,为不同的组提供单独的彩色条形(我可以做到,图 1),每个堆叠具有不同的颜色(取决于图 2)。我找不到一种方法来获得堆栈着色(即我希望组颜色的不同色调随不同的堆栈高度而变化)图 3 中的示例(除非我希望不同的组具有不同的颜色,即不重复)因为他们在这里)。在我提供的代码示例中,有两组数据。一个简单的设置,可以帮助处理数据:Animal,Group,A,B,C,DDog,Domestic,10,10,20,5Cat,Domestic,20,5,10,10Mouse,Pest,75,5,35,0 Lion,Africa,5,5,30,25Elephant,Africa,15,15,20,20Whale,Marine,35,20,10,45Shark,Marine,45,55,0, 60Fish,Marine,20, 5,30,10我实际上正在尝试使用一个更大的集合。这是bl.ocks.org我正在尝试开发的代码:// set the dimensions and margins of the graphconst margin = {    top: 90,    right: 20,    bottom: 30,    left: 40  },  width = 960 - margin.left - margin.right,  height = 960 - margin.top - margin.bottom;const svg = d3.select("#my_dataviz")  .append("svg")  .attr("width", width + margin.left + margin.right)  .attr("height", height + margin.top + margin.bottom)  .append("g")  .attr("transform",    "translate(" + margin.left + "," + margin.top + ")");const y = d3.scaleBand()  .range([0, height])  .padding(0.1);const x = d3.scaleLinear()  .range([0, width]);const z = d3.scaleOrdinal()  .range(["none", "lightsteelblue", "steelblue", "darksteelblue"]);d3.csv("https://gist.githubusercontent.com/JimMaltby/844ca313589e488b249b86ead0d621a9/raw/f328ad6291ffd3c9767a2dbdba5ce8ade59a5dfa/TimeBarDummyFormat.csv", d3.autoType, (d, i, columns) => {      var i = 3;      var t = 0;      for (; i < columns.length; ++i)        t += d[columns[i]] = +d[columns[i]];      d.total = t;      return d;    }  )JSFiddle: https://jsfiddle.net/yq7bkvdL/我尝试过的我觉得我只是错过了一些简单的东西,但我是一个编码菜鸟,我的编码非常初级,所以我无法解决它。我想我要么把填充attr放在错误的地方。或者是我不明白如何在 的嵌套/分层数据中选择键d3.stack。我尝试了各种方法,但都没有成功:1.颜色数组 我尝试编写一个函数来创建颜色数组,通过迭代(使用forEach)“键”和“组”值/名称并将它们连接起来创建一个可以与d3 缩放(顺序)以选择正确的颜色。例如,对于第一个数据集,它将创建一个数组ColoursID [DomesticA, DomesticB, DomesticC, DomesticD,PestA, PestB...],然后与中的颜色相匹配ColourS ["grey", "lightgreen", "green", "darkgreen", "yellow", ...]下面是这样做的尝试,以及注释掉的各种其他探索。
查看完整描述

1 回答

?
HUX布斯

TA贡献1876条经验 获得超6个赞

如果您想在条形长度较小的情况下稍微改变条形颜色,您可以使用fill-opacity并保持fill相同!这样,如果值较浅,则颜色会不太明显且较浅。

opacity只需创建一个带有 range 的新比例即可[0.3, 1]。我选择 0.3 是因为 0 不透明度意味着条形图不可见,而您通常不希望这样。我添加了一个单独的值d.height来表示条形的整个可见高度,该高度与 start 分开(但相当于d.Min + d.All + d.Max)。现在,只需将该属性应用于每个条形即可完成。

您可以选择将范围设置为[0, 1]而不用于d3.extent域 - 这可能会导致类似的结果,尽管您可以通过思想实验发现一些差异。

现在该fill-opacity属性已在每个柱上设置。因此同一堆栈中的条具有相同的fill-opacity值。但请注意,这完全是可选的,您也可以应用不同的值。

// set the dimensions and margins of the graph

const margin = {

    top: 90,

    right: 20,

    bottom: 30,

    left: 40

  },

  width = 960 - margin.left - margin.right,

  height = 960 - margin.top - margin.bottom;


const svg = d3.select("#my_dataviz")

  .append("svg")

  .attr("width", width + margin.left + margin.right)

  .attr("height", height + margin.top + margin.bottom)

  .append("g")

  .attr("transform",

    "translate(" + margin.left + "," + margin.top + ")");


const y = d3.scaleBand()

  .range([0, height])

  .padding(0.1);

const x = d3.scaleLinear()

  .range([0, width]);

const z = d3.scaleOrdinal()

  .range(["none", "lightsteelblue", "steelblue", "darksteelblue"]);

const opacity = d3.scaleLinear()

  .range([0.3, 1]);


d3.csv("https://gist.githubusercontent.com/JimMaltby/844ca313589e488b249b86ead0d621a9/raw/f328ad6291ffd3c9767a2dbdba5ce8ade59a5dfa/TimeBarDummyFormat.csv", d3.autoType, (d, i, columns) => {

      var i = 3;

      var t = 0;

      for (; i < columns.length; ++i)

        t += d[columns[i]] = +d[columns[i]];

      d.total = t;

      d.height = d.total - d.Start;

      return d;

    }


  ).then(function(data) {

    const keys = data.columns.slice(3); // takes the column names, ignoring the first 3 items = ["EarlyMin","EarlyAll", "LateAll", "LateMax"]


    // List of groups = species here = value of the first column called group -> I show them on the X axis

    const Groups = d3.map(data, d => d.Group);

    y.domain(data.map(d => d.Ser));

    x.domain([2000, d3.max(data, d => d.total)]).nice();

    z.domain(keys);

    opacity.domain(d3.extent(data, d => d.height));

    console.log(opacity.domain());


    svg.append("g")

      .selectAll("g")

      .data(d3.stack().keys(keys)(data))

      .enter().append("g")

      .attr("fill", d => z(d.key))

      .selectAll("rect")

      .data(d => d)

      .enter()

      .append("rect")

      .attr("y", d => y(d.data.Ser))

      .attr("x", d => x(d[0]))

      .attr("height", y.bandwidth())

      .attr("width", d => x(d[1]) - x(d[0]))

      .attr("fill-opacity", d => opacity(d.data.height));


    svg.append("g")

      .attr("transform", "translate(0,0)")

      .call(d3.axisTop(x));


    svg.append("g")

      .call(d3.axisLeft(y));

  });

.bar {

  fill: rgb(70, 131, 180);

}

<script src="https://d3js.org/d3.v5.min.js"></script>

<div id="my_dataviz"></div>

编辑:知道您想按组对条形进行着色,我将使用相同的逻辑,但进行一些调整:

首先,我转而z处理fill-opacity(我仍然用它来强调不同的群体),并使用新的group颜色序数比例。这个规模的关键就是预先存在的领域d.Group

// set the dimensions and margins of the graph

const margin = {

    top: 90,

    right: 20,

    bottom: 30,

    left: 40

  },

  width = 960 - margin.left - margin.right,

  height = 960 - margin.top - margin.bottom;


const svg = d3.select("#my_dataviz")

  .append("svg")

  .attr("width", width + margin.left + margin.right)

  .attr("height", height + margin.top + margin.bottom)

  .append("g")

  .attr("transform",

    "translate(" + margin.left + "," + margin.top + ")");


const y = d3.scaleBand()

  .range([0, height])

  .padding(0.1);

const x = d3.scaleLinear()

  .range([0, width]);

const z = d3.scaleOrdinal()

  .range([0.25, 0.5, 0.75, 1]);

const group = d3.scaleOrdinal()

  .range(["darkgreen", "darkred", "steelblue", "purple"]);


d3.csv("https://gist.githubusercontent.com/JimMaltby/844ca313589e488b249b86ead0d621a9/raw/f328ad6291ffd3c9767a2dbdba5ce8ade59a5dfa/TimeBarDummyFormat.csv", d3.autoType, (d, i, columns) => {

      var i = 3;

      var t = 0;

      for (; i < columns.length; ++i)

        t += d[columns[i]] = +d[columns[i]];

      d.total = t;

      return d;

    }


  ).then(function(data) {

    const keys = data.columns.slice(3); // takes the column names, ignoring the first 3 items = ["EarlyMin","EarlyAll", "LateAll", "LateMax"]


    y.domain(data.map(d => d.Ser));

    x.domain([2000, d3.max(data, d => d.total)]).nice();

    z.domain(keys);

    group.domain(data.map(d => d.Group));


    svg.append("g")

      .selectAll("g")

      .data(d3.stack().keys(keys)(data))

      .enter().append("g")

      .attr("fill-opacity", d => z(d.key))

      .selectAll("rect")

      .data(d => d)

      .enter()

      .append("rect")

      .attr("y", d => y(d.data.Ser))

      .attr("x", d => x(d[0]))

      .attr("height", y.bandwidth())

      .attr("width", d => x(d[1]) - x(d[0]))

      .attr("fill", d => group(d.data.Group));

      


    svg.append("g")

      .attr("transform", "translate(0,0)")

      .call(d3.axisTop(x));


    svg.append("g")

      .call(d3.axisLeft(y));

  });

.bar {

  fill: rgb(70, 131, 180);

}

<script src="https://d3js.org/d3.v5.min.js"></script>

<div id="my_dataviz"></div>

编辑2:如果你想自己指定颜色,我会使用颜色键映射:


// set the dimensions and margins of the graph

const margin = {

    top: 90,

    right: 20,

    bottom: 30,

    left: 40

  },

  width = 960 - margin.left - margin.right,

  height = 960 - margin.top - margin.bottom;


const svg = d3.select("#my_dataviz")

  .append("svg")

  .attr("width", width + margin.left + margin.right)

  .attr("height", height + margin.top + margin.bottom)

  .append("g")

  .attr("transform",

    "translate(" + margin.left + "," + margin.top + ")");


const y = d3.scaleBand()

  .range([0, height])

  .padding(0.1);

const x = d3.scaleLinear()

  .range([0, width]);

const z = d3.scaleOrdinal()

  .range([0.25, 0.5, 0.75, 1]);

const group = d3.scaleOrdinal()

  .range([

    { Start: "none", Min: "lightgreen", All: "green", Max: "darkgreen" },

    { Start: "none", Min: "indianred", All: "red", Max: "darkred" },

    { Start: "none", Min: "lightsteelblue", All: "steelblue", Max: "darksteelblue" }

  ]);


d3.csv("https://gist.githubusercontent.com/JimMaltby/844ca313589e488b249b86ead0d621a9/raw/f328ad6291ffd3c9767a2dbdba5ce8ade59a5dfa/TimeBarDummyFormat.csv", d3.autoType, (d, i, columns) => {

      var i = 3;

      var t = 0;

      for (; i < columns.length; ++i)

        t += d[columns[i]] = +d[columns[i]];

      d.total = t;

      return d;

    }


  ).then(function(data) {

    const keys = data.columns.slice(3); // takes the column names, ignoring the first 3 items = ["EarlyMin","EarlyAll", "LateAll", "LateMax"]


    y.domain(data.map(d => d.Ser));

    x.domain([2000, d3.max(data, d => d.total)]).nice();

    z.domain(keys);

    group.domain(data.map(d => d.Group));


    svg.append("g")

      .selectAll("g")

      .data(d3.stack().keys(keys)(data))

      .enter().append("g")

      .each(function(e) {

        d3.select(this)

          .selectAll("rect")

          .data(d => d)

          .enter()

          .append("rect")

          .attr("y", d => y(d.data.Ser))

          .attr("x", d => x(d[0]))

          .attr("height", y.bandwidth())

          .attr("width", d => x(d[1]) - x(d[0]))

          .attr("fill", d => group(d.data.Group)[e.key]);

      });

      


    svg.append("g")

      .attr("transform", "translate(0,0)")

      .call(d3.axisTop(x));


    svg.append("g")

      .call(d3.axisLeft(y));

  });

.bar {

  fill: rgb(70, 131, 180);

}

<script src="https://d3js.org/d3.v5.min.js"></script>

<div id="my_dataviz"></div>


查看完整回答
反对 回复 2023-08-24
  • 1 回答
  • 0 关注
  • 110 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信