W moim wcześniejszym pytaniu: D3.Js Ułożone wykres słupkowy - Modyfikuj stos Zamów logikę dowiedziałem się, że D3 nie ma roztworu półki do sortowania przez poszczególne wartości w stosie. D3's stack.order() może posortować tylko całą serię. Dowiedziałem się również, że będę musiał napisać niestandardową funkcję, aby to osiągnąć.

Zanim wejdę do funkcji, pozwól mi wyjaśnić motywację tutaj. Chcę pokazać proporcje obok łącznej suma, co jest tym, co D3 ułożone wykresy słupkowe są dobre. Chcę jednak naciskać na ranking. Dlatego muszę zmienić zamówienie, które poszczególne rurki są uporządkowane w każdym stosie, aby odzwierciedlić jakiekolwiek zmiany w rankingu. Największe wartości powinny znajdować się na szczycie stosu, a najmniejsze wartości powinny pojawić się na dole. Jak wspomniano w poprzednim poście, typowe wyjście do ułożonego wykresów słupkowych przypominają wszystkie ramiona w stosy oparte tylko na początkowym zamówieniu.

Kod ponownie w celach informacyjnych:

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

var height = 600;
var width = 900;

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

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

var graphGroup = svg.append('g')
  .attr('transform', "translate(" + margins.left + "," + margins.top + ")");


var data = [{
    period: 't1',
    fmc1: 2,
    fmc2: 5,
    fmc3: 6,
    fmc4: 9,
    fmc5: 10
  },
  {
    period: 't2',
    fmc1: 3,
    fmc2: 4,
    fmc3: 9,
    fmc4: 8,
    fmc5: 11
  },
  {
    period: 't3',
    fmc1: 3,
    fmc2: 5,
    fmc3: 15,
    fmc4: 12,
    fmc5: 10
  },
];

var groups = d3.map(data, function(d) {
  return (d.period)
}).keys();

var subgroups = Object.keys(data[0]).slice(1);

var stackedData = d3.stack()
  .keys(subgroups)
  (data);

//console.log(stackedData);

var yScale = d3.scaleLinear()
  .domain([0, 80])
  .range([height, 0]);

var xScale = d3.scaleBand()
  .domain(['t1', 't2', 't3'])
  .range([0, width])
  .padding([.5]);

var colorScale = d3.scaleOrdinal()
  .domain(subgroups)
  .range(["#003366", "#366092", "#4f81b9", "#95b3d7", "#b8cce4", "#e7eef8", "#a6a6a6", "#d9d9d9", "#ffffcc", "#f6d18b", "#e4a733", "#b29866", "#a6a6a6", "#d9d9d9", "#e7eef8", "#b8cce4", "#95b3d7", "#4f81b9", "#366092", "#003366"].reverse());

graphGroup.append("g")
  .selectAll("g")
  .data(stackedData)
  .enter().append("g")
  .attr("fill", function(d) {
    return colorScale(d.key);
  })
  .selectAll("rect")
  .data(function(d) {
    return d;
  })
  .enter().append("rect")
  .attr("x", function(d) {
    return xScale(d.data.period);
  })
  .attr("y", function(d) {
    return yScale(d[1]);
  })
  .attr("height", function(d) {
    return yScale(d[0]) - yScale(d[1]);
  })
  .attr("width", xScale.bandwidth());
<script src="https://d3js.org/d3.v5.min.js"></script>

Dzięki strukturze danych staje się znacznie bardziej złożone po wywołaniu {X0}} To było dla mnie wyzwanie. Zadanie jest podobne do sortowania całej tablicy tablic w oparciu o wartości obiektów w zagnieżdżonych tablicach.

Moją najlepszą próbą było:

var sortTest = stackedData.sort(stackedData[1].sort((a, b) => a[0] - b[1]));

Który zwrócił błąd:

Funkcja sortowania musi być funkcją lub niezdefiniowaną

Okazało się to znacznie trudniejsze niż sobie wyobrażałem, więc próbowałem zamówić dane przed stosem, ale doprowadziło to do niedokładnego stosu, stracono związki.

W tym podobnym pytaniu był blask nadziei: Sortowanie D3.JS Backged Bar Wykres.

Jednakże, gdy podejście nie używa d3.stack(), rozwiązanie nie miało zastosowania do mojego konkretnego przypadku lub żadnego przypadku przy użyciu d3.stack().

Pytanie

Jakie jest rozwiązanie do sortowania danych przez pojedyncze wartości, gdy dane są w formacie d3.stack()?

2
Arash Howaida 28 październik 2020, 07:00

1 odpowiedź

Najlepsza odpowiedź

Użyłbym niestandardowej funkcji stosu, jak poniżej. Próbowałem uzyskać podobną strukturę danych jako schemat słupkowy, tylko zgrupowałem wartości według klucza zamiast ich indeksu. W ten sposób mogłem ich łatwiej posortować.

var margins = {
  top: 30,
  bottom: 50,
  left: 30,
  right: 30
};

var height = 300;
var width = 600;

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

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

var graphGroup = svg.append('g')
  .attr('transform', "translate(" + margins.left + "," + margins.top + ")");

var data = [{
    period: 't1',
    fmc1: 2,
    fmc2: 5,
    fmc3: 6,
    fmc4: 9,
    fmc5: 10
  },
  {
    period: 't2',
    fmc1: 3,
    fmc2: 4,
    fmc3: 9,
    fmc4: 8,
    fmc5: 11
  },
  {
    period: 't3',
    fmc1: 3,
    fmc2: 5,
    fmc3: 15,
    fmc4: 12,
    fmc5: 10
  },
];

var groups = data.map(function(d) {
  return d.period;
});
var subgroups = Object.keys(data[0]).slice(1);

var stackedData = data.map(function(d) {
  var orderedKeys = subgroups.slice().sort(function(a, b) {
    return d[a] - d[b];
  });
  var bottom = 0;

  var result = orderedKeys.map(function(key) {
    var value = d[key];
    var result = [bottom, bottom + value];
    result.data = d;
    result.key = key;
    bottom += value;
    return result;
  });
  result.key = d.period;
  return result;
});
console.log(stackedData);

var yScale = d3.scaleLinear()
  .domain([0, 80])
  .range([height, 0]);

var xScale = d3.scaleBand()
  .domain(['t1', 't2', 't3'])
  .range([0, width])
  .padding([.5]);

var colorScale = d3.scaleOrdinal()
  .domain(subgroups)
  .range(["#003366", "#366092", "#4f81b9", "#95b3d7", "#b8cce4", "#e7eef8", "#a6a6a6", "#d9d9d9", "#ffffcc", "#f6d18b", "#e4a733", "#b29866", "#a6a6a6", "#d9d9d9", "#e7eef8", "#b8cce4", "#95b3d7", "#4f81b9", "#366092", "#003366"].reverse());

graphGroup.append("g")
  .selectAll("g")
  .data(stackedData)
  .enter()
  .append("g")
  .selectAll("rect")
  .data(function(d) {
    return d;
  })
  .enter()
  .append("rect")
  .attr("fill", function(d) {
    return colorScale(d.key);
  })
  .attr("x", function(d) {
    return xScale(d.data.period);
  })
  .attr("y", function(d) {
    return yScale(d[1]);
  })
  .attr("height", function(d) {
    return yScale(d[0]) - yScale(d[1]);
  })
  .attr("width", xScale.bandwidth());
<script src="https://d3js.org/d3.v5.js"></script>
1
Ruben Helsloot 28 październik 2020, 10:24