Skip to main content
Announcements
Join us at Qlik Connect for 3 magical days of learning, networking,and inspiration! REGISTER TODAY and save!
cancel
Showing results for 
Search instead for 
Did you mean: 
cahenson2
Contributor II
Contributor II

d3 Extension - Dealing with section access/selections

I am currently building a custom box plot extension through d3. I'm pulling in my raw data as a dimension and calculating quartiles by group using d3.nest() and plotting the calculated quartiles as rectangles (min->q1 (First rectangle), q1->q2 (Second rectangle, etc.) Essentially, I'm building a plot that resembles this one from d3-graph-gallery but with rectangles instead of the horizontal line.

cahenson2_0-1668617045527.png

In my case, the individual data points are student scores and the quartile measurements pertain to the class as a whole. The students are granted access via section access so that they see only their own data. I am trying to essentially have the quartiles for the class as a whole remain while the data points are reduced down by section access to the individual student. 

I've gotten everything to calculate and plot as expected, but I have not been able to prevent the section access from affecting the quartiles. When the section access is activated, the data from the selection drills down to just the single student and henceforth no quartiles can be calculated.

Many thanks in advance for your help/consideration.

Labels (1)
1 Reply
cahenson2
Contributor II
Contributor II
Author

More details... In the extension I currently have two measures. The first measure is used to calculate quantiles for the dataset as a whole and then to translate the quantiles into a box plot.

  • (=Aggr({1<Student=>} NODISTINCT Max(Final_Score), Student, Domain)

The second measure is used to plot the individual data points on top of the box plot. 

  • (=Aggr(NODISTINCT Max(Final_Score), Student, Domain)

The set analysis and overall data structure appear correct when I look at the data in a table with the appropriate selections. In the visualization, however, initially everything appears correctly, but when I initiate a selection for Student the box plot calculated from Measure 1 disappears and only the data points for the selected student remain. I'm creating the extension in d3 and the code is included below.

Thanks in advance for any help.

 

define(["jquery""./d3.v4.min""./initialProperties""./properties""text!./CCP_Grouped_Multi.css"],
    function ($d3initPropspropscssContent) {
        "use strict"
        $('<style>').html(cssContent).appendTo('head');
        return {
            initialProperties: initProps,
            definition: props,
            support: {
                snapshot: true,
                export: false,
                exportData: false
            },

            paint: function ($elementlayout) {

                var hypercube = layout.cube1;
                console.log(hypercube);

                var margin = {
                    top: 20,
                    right: 20,
                    bottom: 60,
                    left: 40
                },
                    width = $element.width() - margin.left - margin.right,
                    height = $element.height() - margin.top - margin.bottom,
                    center = height / 2,
                    offset = 50;

                var id = "ID_D3" + layout.qInfo.qId;
                $element.attr("id"id); //assign to $element the attribute id set as "ID_D3"
                d3.select("#" + id).html(null);  //clear div so that it doesn't re-render with each run

                var dataset = layout.qHyperCube.qDataPages[0].qMatrix.map(function (d) {
                    return {
                        "d_1": d[0].qText//Domain
                        "d_2": d[1].qText//Student
                        "d_3": d[2].qNum//Box plot measure (=Aggr({1<Student=>} NODISTINCT Max(Final_Score), Student, Domain)
                        "d_4": d[3].qNum //Data points measure (=Aggr(NODISTINCT Max(Final_Score), Student, Domain)
                    }
                });

                //Calculate distribution components (Adjust with d3.nest)
                var sumstat = d3.nest() // nest function allows to group the calculation per level of a factor
                    .key(function (d) { return d.d_1; })
                    .rollup(function (g) {
                        return {
                            "min": d3.quantile(g0g => g.d_3),
                            "q1": d3.quantile(g.25g => g.d_3),
                            "med": d3.quantile(g.5g => g.d_3),
                            "q3": d3.quantile(g.75g => g.d_3),
                            "max": d3.quantile(g1g => g.d_3)
                        }
                    })
                    .entries(dataset)
                console.log("sumstat:"sumstat)

                //Create svg object
                var svg = d3.select("#" + id)
                    .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 + ")");

                // Show the X scale (Need to find consistent way to translate the position: currently shifts at resizing)
                var x = d3.scaleLinear()
                    .domain([01])
                    .range([0width])
                svg
                    .append("g")
                    .attr("class""axis")
                    .attr("transform""translate(0," + height + ")")
                    .call(d3.axisBottom(x).tickValues([01]).tickFormat(d3.format(".0%")));

                // Add X axis label:
                svg
                    .append("text")
                    .style("text-anchor""middle")
                    .attr("x"width / 2)
                    .attr("y"height + margin.bottom - 15)
                    .text("Range");

                // Show the Y scale
                var y = d3.scaleBand()
                    .range([height0])
                    .domain(["CM""CR""HX""PE"])
                    .padding(.4);
                svg.append("g")
                    .call(d3.axisLeft(y).tickSize(0))
                    .select(".domain").remove()

                // Show the boxes (shape outlines commented out)
                svg
                    .selectAll("boxes")
                    .data(sumstat)
                    .enter()
                    .append("rect")
                    .attr("y"function (d) { return y(d.key); })
                    .attr("x"function (d) { return (x(d.value.min)) })
                    .attr("width"function (d) { return (x(d.value.q1) - x(d.value.min)) })
                    .attr("height"center / 4)
                    // .attr("height", y.bandwidth())
                    // .attr("stroke", "gray")
                    .style("fill""#EEEEEE")
                    .text("Minimum");


                svg
                    .selectAll("boxes")
                    .data(sumstat)
                    .enter()
                    .append("rect")
                    .attr("y"function (d) { return y(d.key); })
                    .attr("x"function (d) { return (x(d.value.q1)) })
                    .attr("width"function (d) { return (x(d.value.med) - x(d.value.q1)) })
                    .attr("height"center / 4)
                    // .attr("height", y.bandwidth())
                    // .attr("stroke", "gray")
                    .style("fill""#CCCCCC");

                svg
                    .selectAll("boxes")
                    .data(sumstat)
                    .enter()
                    .append("rect")
                    .attr("y"function (d) { return y(d.key); })
                    .attr("x"function (d) { return (x(d.value.med)) })
                    .attr("width"function (d) { return (x(d.value.q3) - x(d.value.med)) })
                    .attr("height"center / 4)
                    // .attr("height", y.bandwidth())
                    // .attr("stroke", "gray")
                    .style("fill""#999999");

                svg
                    .selectAll("boxes")
                    .data(sumstat)
                    .enter()
                    .append("rect")
                    .attr("y"function (d) { return y(d.key); })
                    .attr("x"function (d) { return (x(d.value.q3)) })
                    .attr("width"function (d) { return (x(d.value.max) - x(d.value.q3)) })
                    .attr("height"center / 4)
                    // .attr("height", y.bandwidth())
                    // .attr("stroke", "gray")
                    .style("fill""#666666");

               
                //Adding individual data points (Change to rectangle with a value representing student store +- 5% of max-min) also: Convert to d3.nest() and sub for dataset (key/value)
                var color = d3.scaleLinear()
                    .domain([.51])
                    .range(["#e7ecfc""#1045e3"]);
                svg
                    .selectAll("indPoints")
                    .data(dataset)
                    .enter()
                    .append("circle")
                    .attr("cx"function (d) { return (x(d.d_4)) })
                    .attr("cy"function (d) { return (y(d.d_1) + (y.bandwidth() - (center / 7))) }) //Will have to adjust for flexibility
                    .attr("r"4)
                    .style("fill"function (d) { return (color(+d.d_4)) })
            }
        }
    })