Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stack layout does not support a mix of positive and negative bars #2265

Closed
barrybecker4 opened this issue Mar 19, 2015 · 13 comments
Closed

stack layout does not support a mix of positive and negative bars #2265

barrybecker4 opened this issue Mar 19, 2015 · 13 comments
Milestone

Comments

@barrybecker4
Copy link

This may be my lack of understanding and not an actual limitation, but it seems that the stack layout only supports showing stacked bars when the bars are all positive (or all negative). I would like to show a stacked bar chart where bar segments with positive height extend above the x axis, and those with negative segments extend below the x axis.

The reason I don't think this is possible with the current stack layout is because it involves maintaining a baseline for positive and negative bars separately (not just y0). If each series had only positive or negative bars, then the current layout would work, but I want to allow for any of the series to have a mix of positive and negative values.

I think this limitation could be removed with just a small change in the stack layout code.

If the signature of the out function was
function out(d, y0, change, y)
instead of
function out(d, y0, y)

and
out.call(stack, series[i][j], o += points[i - 1][j][1], points[i][j][1]);
was changed to
out.call(stack, series[i][j], o, points[i - 1][j][1], points[i][j][1]);

then I could store [positiveBase, negativeBase] in y0 instead of just a single scalar, and maintain separate y0 values for positive stacks and negative stacks. Is that correct? Would this be a good change. I realize that there would be backwards compatibility issues.

@barrybecker4 barrybecker4 changed the title stack layout does not support negative bars stack layout does not support a mix of positive and negative bars Mar 20, 2015
@barrybecker4
Copy link
Author

I made a copy of d3.layout.stack and implemented my suggestion locally. It worked nicely. I can now show stacked bars with positive and negative bars in the same stack. I just need to supply "offset" and "out" functions that maintain y0 as a [positiveBase, negativeBase].

      .offset(function(data) {
        var j = -1;
        var len = data[0].length; 
        var y0 = [];
        while (++j < len) {
            // baseline for positive and negative bars respectively.
            y0[j] = [0, 0];
        }
        return y0;
    })
      .out(function(d, y0, change, y) {
        // update either the positive or negative baseline depending on the sign of the bar segment.
        var posOrNeg = (change >= 0) ? 0 : 1;
        y0[posOrNeg] += change;
        d.y0 = y0.slice();
        d.y = y;
       });

I hope this small change can be incorporated into a future version of D3.

@geriux
Copy link

geriux commented Apr 15, 2015

Thank you for this @barrybecker4, it would be great if this was included any time soon.

@novtor
Copy link

novtor commented Apr 24, 2015

Got same problem. Thank you @barrybecker4 for the idea.
I got it in another way (worked in my case, need to be tested):

var buildOut = function(dataSeriesCount) {
    var currentXOffsets = [];
    var current_xIndex = 0;
    return function(d, y0, y){
        if(current_xIndex++ % dataSeriesCount === 0){
            currentXOffsets = [0, 0];
        }
        if(y >= 0) {
            d.y0 = currentXOffsets[1];
            d.y = y;
            currentXOffsets[1] += y;
        } else {
            d.y0 = currentXOffsets[0] + y;
            d.y = -y;
            currentXOffsets[0] += y;
        }
    }
}

And then just call
d3.layout.stack().out(buildOut(seriesCount))
instead of
d3.layout.stack()

The point is to store positive and negative values in a closure: currentXOffsets. No backward incopatibilities.
seriesCount is the number of stacks in each x point.

@IonDen
Copy link

IonDen commented Aug 28, 2015

+1

@mbostock mbostock added the req label Oct 22, 2015
@mbostock mbostock added this to the Icebox milestone Oct 22, 2015
@mbostock
Copy link
Member

Sounds useful, although presumably this only makes sense with the “zero” offset? When there’s a non-zero baseline then you don’t really have a place to put negative values.

@barrybecker4
Copy link
Author

Thanks @novtor on that improved solution. It's much better to do it that way instead of trying to modify the layout code as I did originally. In my case, I always use a 0 offset, but maybe this could work if currentXOffsets is initialized in a way that matches the offset function.
Given @novtor 's solution, this issue can probably be closed, since that solution does not require a d3 change.

@seedhisadak
Copy link

@novtor

Thanks. Where to make this call d3.layout.stack().out(buildOut(seriesCount)). I have 2 series(cash_in, cash_out and I was attempting to show a stacked chart, with positive for cash_in and negative for cash_out). Thanks in Advance if you could help.

var selection = svg.selectAll(".series")
        .data(data)
      .enter().append("g")
        .attr("class", "series")
        .attr("transform", function (d) { return "translate(" + xBar(d.date) + ",0)"; });

selection.selectAll("rect")
      .data(function (d) { return d.mapping; })
    .enter().append("rect")
      .attr("width", xBar.rangeBand())
      .attr("y", function (d) { return y(d.y1); })
      .attr("height", function (d) { return y(d.y0) - y(d.y1); })
      .style("fill", function (d) { return color(d.name); })
      .style("stroke", "grey");

@novtor
Copy link

novtor commented Oct 26, 2015

@seedhisadak , just follow an example of d3 layout stack (like here http://bl.ocks.org/mbostock/1134768) and then to create the layout, do
d3.layout.stack().out(buildOut(seriesCount))
instead of
d3.layout.stack()

@seedhisadak
Copy link

@novtor That helps. Thank you very much

@notechsolution
Copy link

@novtor Thanks a lot for your help. it works.

@mbostock mbostock modified the milestones: 4.0, Icebox Jul 6, 2016
@mbostock
Copy link
Member

mbostock commented Jul 6, 2016

The d3.stack layout was rewritten for 4.0, so closing this issue.

@mbostock mbostock closed this as completed Jul 6, 2016
@mattbatman
Copy link

The stack layout rewrite doesn't specifically "solve" this issue, right? It just changes the optimal solution(s)?

I've been working to convert a stacked bar chart of mixed positive and negative values from D3 3.x to 4.0, and it seems like some custom adjustment still needs to be made, either to the returned array from d3.stack() or a custom stack function needs to be used in its place.

@mbostock
Copy link
Member

It is possible to use d3.stack with positive and negative values, so in that sense it is already supported and this issue is closed. But moreover the code now resides in d3-shape, so a discussion about what new features may be useful in improving the design (I do not claim that the current design is optimal—things can always be improved) should happen over there rather than here. If you have a specific suggestion for improving the design of d3.stack please open a new issue over in the d3-shape repo; otherwise I do not have any concrete changes planned in this area for d3.stack.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

9 participants