簡體   English   中英

Y 刻度域最小值/分鍾堆積條形圖不為零; 溢出的 X 軸

[英]Y Scale domain minimum / min NOT zero for stacked bar chart; overflowing X-Axis

我對 D3 非常陌生,並試圖弄清楚當域不是 [0, someMaxNumber] 時如何調整我的堆積條形圖。 我嘗試了幾種不同的方法,但都無濟於事。

我努力了...

  1. .attr("y", sequence => yScale(sequence[1] + yMin)) :這實際上確實使事情適合,但現在我的第一個數據點不再從 100 開始,但現在高於它。 那是不正確的,所以我沒有留在那里。
  2. .attr("height", sequence => { const [lower, upper] = sequence; return yScale(lower + yMin) - yScale(upper); }) :這解決了我之前的問題(事情沒有從正確的 y 軸標記),但使粉紅色和綠色層高度不正確。

我還嘗試了許多其他修復程序,但我無法使其正常工作。 我發誓這與rect元素的高度有關。

如果您想知道,我的目標是讓 Y 軸不從 0 開始,而是從某個min開始,例如最小rect高度值的 50%。 任何幫助,將不勝感激!

更新將上面的第 2 點更改為以下代碼可以解決我的問題,但我認為應該有更好的解決方案。 請告訴我。

.attr("height", (sequence, other, otherother) => {
   const [lower, upper] = sequence;
   const firstBarAdjustment = lower === 0 ? yMin : 0;
   return yScale(lower + firstBarAdjustment) - yScale(upper);
})

當前圖像顯示溢出

class D3StackedBarChart extends React.Component<Props, State> {
    state: State = {
        data: [
            {year: 1993, males: 100, females: 95, pets: 12},
            {year: 1994, males: 80, females: 88, pets: 8},
            {year: 1995, males: 111, females: 122, pets: 32},
            {year: 1996, males: 25, females: 25, pets: 64},
            {year: 1997, males: 13, females: 45, pets: 72},
        ],
    };

    componentDidMount() {
        const {data} = this.state;
        const keys = ["males", "females", "pets"];
        const colors = {
            males: "blue",
            females: "pink",
            pets: "green",
        };

        const width = 1000;
        const height = 1000;
        const margin = {top: 80, right: 180, bottom: 80, left: 180};
        // const margin = {top: 0, right: 0, bottom: 0, left: 0};
        const padding = 0.1;

        const stackGenerator = d3.stack().keys(keys); // now a function
        const layers = stackGenerator(data); // now a function

        // Origin of an SVG is in the TOP LEFT corner
        const svg = d3
            .select("#test")
            .append("svg") // append an svg element to our div#test
            .attr("height", height - margin.top - margin.bottom)
            .attr("width", width - margin.left - margin.right)
            .attr("viewBox", [0, 0, width, height]);

        // SCALE
        const xScale = d3
            .scaleBand()
            .domain(data.map(d => d.year))
            .range([margin.left, width - margin.right])
            .padding(padding);

        // looking at second value / y value
        const extent = [
            0.5 *
                d3.min(layers, layer => d3.min(layer, sequence => sequence[1])),
            1.1 *
                d3.max(layers, layer => d3.max(layer, sequence => sequence[1])),
        ];
        const [yMin, yMax] = extent;

        const yScale = d3
            .scaleLinear()
            .domain(extent)
            .range([height - margin.bottom, margin.top]); // range from bottom up

        // AXIS
        const xAxis = g => {
            // bottom align it
            g.attr("transform", `translate(0, ${height - margin.bottom})`)
                .call(d3.axisBottom(xScale))
                .attr("font-size", "20px");
        };

        const yAxis = g => {
            g.attr("transform", `translate(${margin.left}, 0)`)
                .call(d3.axisLeft(yScale))
                .attr("font-size", "20px");
        };

        // Create tooltip
        const Tooltip = d3
            .select("#test")
            .append("div")
            .style("opacity", 0)
            .attr("class", css(styles.tooltip))
            .style("background-color", "white")
            .style("border", "solid")
            .style("border-width", "2px")
            .style("border-radius", "5px")
            .style("padding", "5px");

        // Three function that change the tooltip when user hover / move / leave a cell
        const mouseover = function(event, data) {
            Tooltip.style("opacity", 1);
            d3.select(this)
                .style("stroke", "black")
                .style("opacity", 1);
        };

        const mousemove = function(event, data) {
            const {0: start, 1: end, data: d} = data;

            Tooltip.html(`The year: ${d.year}<br> The value: ${end - start}`)
                .style("left", event.layerX + 3 + "px")
                .style("top", event.layerY - 3 + "px");
        };

        const mouseleave = function(event, data) {
            Tooltip.style("opacity", 0);
            d3.select(this)
                .style("stroke", "none")
                .style("opacity", 0.8);
        };

        // Creating Legend
        const legend = svg
            .append("g")
            .attr("class", "legend")
            .attr("transform", d => "translate(0, 0)")
            .attr("font-size", "12px")
            .attr("text-anchor", "start")
            .selectAll("g")
            .data(keys)
            .join("g") // Create 3 "g" elements that are initially empty
            .attr("transform", (d, i) => "translate(0," + i * 30 + ")");

        // Add square and their color
        legend
            .append("rect") // append a rect to each individual g
            .attr("fill", d => colors[d])
            .attr("x", width - margin.right)
            .attr("rx", 3)
            .attr("width", 19)
            .attr("height", 19);

        // Add text next to squares
        legend
            .append("text")
            .attr("x", width - margin.right + 40)
            .attr("y", 9.5)
            .attr("dy", "0.32em")
            .text(d => d);

        // Add header
        const legendHeader = d3
            .select(".legend")
            .append("g")
            .attr("transform", (d, i) => "translate(0, -20)")
            .lower()
            .append("text")
            .attr("x", width - margin.right)
            .attr("font-size", "12px")
            .text(() => {
                const text = "Master Levels";
                return text.toLocaleUpperCase();
            });

        // Get coordinates and height of legend to add border
        const {
            x: legendX,
            y: legendY,
            width: legendWidth,
            height: legendHeight,
        } = d3
            .select(".legend")
            .node()
            .getBBox();

        const borderPadding = 20;

        // Create border for legend
        // Adding a "border" manually
        const legendBox = svg
            .select(".legend")
            .append("rect")
            .lower()
            .attr("class", "legend-box")
            .attr("x", legendX - borderPadding)
            .attr("y", legendY - borderPadding)
            .attr("width", legendWidth + borderPadding * 2)
            .attr("height", legendHeight + borderPadding * 2)
            .attr("fill", "white")
            .attr("stroke", "black")
            .attr("opacity", 0.8);

        // Rendering
        // first, second, and third refer to `layers`
        // first --> layers
        // second --> edge1, edge2, and data
        svg.selectAll(".layer")
            .data(layers) // first
            .join("g") // create new element for each layer
            .attr("class", "layer")
            // .attr("class", css(styles.rectangle))
            .attr("fill", layer => colors[layer.key])
            .selectAll("rect")
            .data(layer => layer) // second
            .join("rect")
            .attr("x", sequence => xScale(sequence.data.year))
            .attr("y", sequence => yScale(sequence[1]))
            .attr("width", xScale.bandwidth())
            .attr("height", sequence => {
                const [lower, upper] = sequence;
                return yScale(lower) - yScale(upper);
            })
            .on("mouseover", mouseover)
            .on("mousemove", mousemove)
            .on("mouseleave", mouseleave);

        svg.append("g").call(xAxis);
        svg.append("g").call(yAxis);
        svg.node();
    }

    render(): React.Node {
        return (
            <View>
                <LabelLarge>{i18n.doNotTranslate("D3.js")}</LabelLarge>
                <Strut size={Spacing.xLarge_32} />
                <div id="test" />
            </View>
        );
    }
}

const styles = StyleSheet.create({
    tooltip: {
        position: "absolute",
    },
    rectangle: {
        ":hover": {
            opacity: 0.66,
        },
    },
});

export default D3StackedBarChart;

yScale domain應該在從 0 到堆棧值的最大總和的范圍內:

const maxStackValue = data.reduce((m, d) => Math.max(m, keys.reduce((s, k) => s + d[k], 0)), 0);

const yScale = d3
  .scaleLinear()
  .domain([0, maxStackValue])
  .range([height - margin.bottom, margin.top]); 

查看它在代碼段中的工作:

 const data = [ {year: 1993, males: 100, females: 95, pets: 12}, {year: 1994, males: 80, females: 88, pets: 8}, {year: 1995, males: 111, females: 122, pets: 32}, {year: 1996, males: 25, females: 25, pets: 64}, {year: 1997, males: 13, females: 45, pets: 72}, ]; const keys = ["males", "females", "pets"]; const maxStackValue = data.reduce((m, d) => Math.max(m, keys.reduce((s, k) => s + d[k], 0)), 0); const colors = { males: "blue", females: "pink", pets: "green", }; const width = 1000; const height = 1000; const margin = {top: 80, right: 180, bottom: 80, left: 180}; // const margin = {top: 0, right: 0, bottom: 0, left: 0}; const padding = 0.1; const stackGenerator = d3.stack().keys(keys); // now a function const layers = stackGenerator(data); // now a function // Origin of an SVG is in the TOP LEFT corner const svg = d3 .select("#test") .append("svg") // append an svg element to our div#test .attr("height", height - margin.top - margin.bottom) .attr("width", width - margin.left - margin.right) .attr("viewBox", [0, 0, width, height]); // SCALE const xScale = d3 .scaleBand() .domain(data.map(d => d.year)) .range([margin.left, width - margin.right]) .padding(padding); const yScale = d3 .scaleLinear() .domain([0, maxStackValue]) .range([height - margin.bottom, margin.top]); // range from bottom up // AXIS const xAxis = g => { // bottom align it g.attr("transform", `translate(0, ${height - margin.bottom})`) .call(d3.axisBottom(xScale)) .attr("font-size", "20px"); }; const yAxis = g => { g.attr("transform", `translate(${margin.left}, 0)`) .call(d3.axisLeft(yScale)) .attr("font-size", "20px"); }; const mousemove = function(event, data) { }; const mouseleave = function(event, data) { }; // Creating Legend const legend = svg .append("g") .attr("class", "legend") .attr("transform", d => "translate(0, 0)") .attr("font-size", "12px") .attr("text-anchor", "start") .selectAll("g") .data(keys) .join("g") // Create 3 "g" elements that are initially empty .attr("transform", (d, i) => "translate(0," + i * 30 + ")"); // Add square and their color legend .append("rect") // append a rect to each individual g .attr("fill", d => colors[d]) .attr("x", width - margin.right) .attr("rx", 3) .attr("width", 19) .attr("height", 19); // Add text next to squares legend .append("text") .attr("x", width - margin.right + 40) .attr("y", 9.5) .attr("dy", "0.32em") .text(d => d); // Add header const legendHeader = d3 .select(".legend") .append("g") .attr("transform", (d, i) => "translate(0, -20)") .lower() .append("text") .attr("x", width - margin.right) .attr("font-size", "12px") .text(() => { const text = "Master Levels"; return text.toLocaleUpperCase(); }); // Get coordinates and height of legend to add border const { x: legendX, y: legendY, width: legendWidth, height: legendHeight, } = d3 .select(".legend") .node() .getBBox(); const borderPadding = 20; // Create border for legend // Adding a "border" manually const legendBox = svg .select(".legend") .append("rect") .lower() .attr("class", "legend-box") .attr("x", legendX - borderPadding) .attr("y", legendY - borderPadding) .attr("width", legendWidth + borderPadding * 2) .attr("height", legendHeight + borderPadding * 2) .attr("fill", "white") .attr("stroke", "black") .attr("opacity", 0.8); // Rendering // first, second, and third refer to `layers` // first --> layers // second --> edge1, edge2, and data svg.selectAll(".layer") .data(layers) // first .join("g") // create new element for each layer .attr("class", "layer") .attr("fill", layer => colors[layer.key]) .selectAll("rect") .data(layer => layer) // second .join("rect") .attr("x", sequence => xScale(sequence.data.year)) .attr("y", sequence => yScale(sequence[1])) .attr("width", xScale.bandwidth()) .attr("height", sequence => { const [lower, upper] = sequence; return (yScale(lower) - yScale(upper)); }) .on("mousemove", mousemove) .on("mouseleave", mouseleave); svg.append("g").call(xAxis); svg.append("g").call(yAxis);
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.0.0/d3.min.js"></script> <div id="test" />

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM