在d3中向并行坐标图添加新的rccord

时间:2015-12-01 18:24:03

标签: javascript d3.js

您好我正在使用D3实现平行坐标图。下面的代码是实际的visalization页面代码。我使用输入表单从用户那里获得一些信息,我想将这些细节添加到可视化中。

例如,如果新记录的详细信息如下: 非洲裔美国人,45岁,声门上,T4,N0,1,诱导,PLRT,0.5

是否可以将此新记录添加到可视化中?

数据截图:

Ethnicity,AgeAtTx,Site,Tcategory,Nodal_Disease,ecog,Chemotherapy,Local_Therapy,Probability of Survival
white,65.93972603,supraglottic,T3,N+,0,no chemo,LP/RT alone,0.366190068
white,69.42465753,supraglottic,T3,N+,0,induction,PLRT,0.396018836
white,68.14246575,supraglottic,T3,N0,3,no chemo,LP/RT alone,0.439289384
white,40.30410959,supraglottic,T3,N+,1,no chemo,LP/RT alone,0.512773973
white,47.96438356,supraglottic,T3,N+,0,no chemo,PLRT,0.472208904
white,70.3369863,supraglottic,T3,N+,0,no chemo,LP/RT alone,0.324965753
white,60.50136986,supraglottic,T3,N+,2,no chemo,LP/RT alone,0.323424658
white,60.72328767,supraglottic,T3,N+,1,no chemo,LP/RT alone,0.321344178
white,59.36986301,supraglottic,T3,N0,1,induction,LP/chemoRT,0.646532534
other,57.64931507,supraglottic,T3,N+,1,concurrent,LP/chemoRT,0.662662671
asian/pacific islander,62.93972603,glottic,T3,N+,2,concurrent,LP/chemoRT,0.769315068

代码:

<!DOCTYPE html>

<style>
    #wrapper {
        position: relative;
        float: left;
        top: 20px;
        font-family: sans-serif;
        font-size: 10px;
    }

    #tooltip{
        font-family: sans-serif;
        font-size: 14px;
        font-weight: bold;
        color:black;
    }

</style>
<link rel="stylesheet" type="text/css" href="http://mostapharoudsari.github.io/Honeybee/pc_source_files/css/d3.parcoords.css">
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src = "http://mostapharoudsari.github.io/Honeybee/pc_source_files/d3/d3.parcoords.js"></script>
<body>
        <div id="wrapper" class="parcoords" style="width:100%; height:420px; margin-top:10px;"></div>

    <script>

        var color_set = d3.scale.ordinal()
                .domain(['white', 'other', 'african-american', 'hispanic', 'asian/pacific islander'])
                .range(['#ffae19', '#4ca64c', '#4682B4', '#737373', '#ff4c4c']);


        // load default chart
        d3.csv("SurvivalProbability.csv", function (data) {

            // collect text for first column to adjust left margin
            var firstCell = data.map(function (d) {
                return d3.values(d)[0]
            });

            // find the longest text size in the first row to adjust left margin
            var textLength = 0;
            firstCell.forEach(function (d) {
                if (d.length > textLength)
                    textLength = d.length;
            });


            // get parallel coordinates
            graph = d3.parcoords()('#wrapper')
                    .data(data)
                    .margin({top: 30, left: 3 * textLength, bottom: 40, right: 0})
                    .alpha(0.6)
                    .mode("queue")
                    .rate(5)
                    .render()
                    .brushMode("1D-axes")  // enable brushing
                    //.reorderable() // I removed this for now as it can mess up with tooltips
                    .interactive();

            // add instruction text
            var instructions = "-Drag around axis to begin brush. -Click axis to clear brush. -Click a label to color data based on axis values. -Hover on each line to highlight."


            // set the initial coloring based on the 3rd column
            update_colors(d3.keys(data[0])[0]);

            // click label to activate coloring
            graph.svg.selectAll(".dimension")
                    .on("click", update_colors)
                    .selectAll(".label")
                    .style("font-size", "14px"); // change font sizes of selected lable

            //add hover event
            d3.select("#wrapper svg")
                    .on("mousemove", function () {
                        var mousePosition = d3.mouse(this);
                        highlightLineOnClick(mousePosition, true); //true will also add tooltip
                    })
                    .on("mouseout", function () {
                        cleanTooltip();
                        graph.unhighlight();
                    });
        });




        // update color and font weight of chart based on axis selection
        // modified from here: https://syntagmatic.github.io/parallel-coordinates/
        function update_colors(dimension) {
            // change the fonts to bold
            graph.svg.selectAll(".dimension")
                    .style("font-weight", "normal")
                    .filter(function (d) {
                        return d == dimension;
                    })
                    .style("font-weight", "bold");

            // change color of lines
            // set domain of color scale
            var values = graph.data().map(function (d) {
                return parseFloat(d[dimension])
            });
            color_set.domain([d3.min(values), d3.max(values)]);

            // change colors for each line
            graph.color(function (d) {
                return color_set([d[dimension]])
            }).render();
        }
        ;


        // Add highlight for every line on click
        function getCentroids(data) {
            // this function returns centroid points for data. I had to change the source
            // for parallelcoordinates and make compute_centroids public.
            // I assume this should be already somewhere in graph and I don't need to recalculate it
            // but I couldn't find it so I just wrote this for now
            var margins = graph.margin();
            var graphCentPts = [];

            data.forEach(function (d) {

                var initCenPts = graph.compute_centroids(d).filter(function (d, i) {
                    return i % 2 == 0;
                });

                // move points based on margins
                var cenPts = initCenPts.map(function (d) {
                    return [d[0] + margins["left"], d[1] + margins["top"]];
                });

                graphCentPts.push(cenPts);
            });

            return graphCentPts;
        }

        function getActiveData() {
            // I'm pretty sure this data is already somewhere in graph
            if (graph.brushed() != false)
                return graph.brushed();
            return graph.data();
        }

        function isOnLine(startPt, endPt, testPt, tol) {
            // check if test point is close enough to a line
            // between startPt and endPt. close enough means smaller than tolerance
            var x0 = testPt[0];
            var y0 = testPt[1];
            var x1 = startPt[0];
            var y1 = startPt[1];
            var x2 = endPt[0];
            var y2 = endPt[1];
            var Dx = x2 - x1;
            var Dy = y2 - y1;
            var delta = Math.abs(Dy * x0 - Dx * y0 - x1 * y2 + x2 * y1) / Math.sqrt(Math.pow(Dx, 2) + Math.pow(Dy, 2));
            //console.log(delta);
            if (delta <= tol)
                return true;
            return false;
        }

        function findAxes(testPt, cenPts) {
            // finds between which two axis the mouse is
            var x = testPt[0];
            var y = testPt[1];

            // make sure it is inside the range of x
            if (cenPts[0][0] > x)
                return false;
            if (cenPts[cenPts.length - 1][0] < x)
                return false;

            // find between which segment the point is
            for (var i = 0; i < cenPts.length; i++) {
                if (cenPts[i][0] > x)
                    return i;
            }
        }

        function cleanTooltip() {
            // removes any object under #tooltip is
            graph.svg.selectAll("#tooltip")
                    .remove();
        }

        function addTooltip(clicked, clickedCenPts) {

            // sdd tooltip to multiple clicked lines
            var clickedDataSet = [];
            var margins = graph.margin()

            // get all the values into a single list
            // I'm pretty sure there is a better way to write this is Javascript
            for (var i = 0; i < clicked.length; i++) {
                for (var j = 0; j < clickedCenPts[i].length; j++) {
                    var text = d3.values(clicked[i])[j];
                    // not clean at all!
                    var x = clickedCenPts[i][j][0] - margins.left;
                    var y = clickedCenPts[i][j][1] - margins.top;
                    clickedDataSet.push([x, y, text]);
                }
            }
            ;

            // add rectangles
            var fontSize = 14;
            var padding = 2;
            var rectHeight = fontSize + 2 * padding; //based on font size

            graph.svg.selectAll("rect[id='tooltip']")
                    .data(clickedDataSet).enter()
                    .append("rect")
                    .attr("x", function (d) {
                        return d[0] - d[2].length * 5;
                    })
                    .attr("y", function (d) {
                        return d[1] - rectHeight + 2 * padding;
                    })
                    .attr("rx", "2")
                    .attr("ry", "2")
                    .attr("id", "tooltip")
                    .attr("fill", "grey")
                    .attr("opacity", 0.9)
                    .attr("width", function (d) {
                        return d[2].length * 10;
                    })
                    .attr("height", rectHeight);

            // add text on top of rectangle
            graph.svg.selectAll("text[id='tooltip']")
                    .data(clickedDataSet).enter()
                    .append("text")
                    .attr("x", function (d) {
                        return d[0];
                    })
                    .attr("y", function (d) {
                        return d[1];
                    })
                    .attr("id", "tooltip")
                    .attr("fill", "white")
                    .attr("text-anchor", "middle")
                    .attr("font-size", fontSize)
                    .text(function (d) {
                        return d[2];
                    })
        }

        function getClickedLines(mouseClick) {
            var clicked = [];
            var clickedCenPts = [];

            // find which data is activated right now
            var activeData = getActiveData();

            // find centriod points
            var graphCentPts = getCentroids(activeData);

            if (graphCentPts.length == 0)
                return false;

            // find between which axes the point is
            var axeNum = findAxes(mouseClick, graphCentPts[0]);
            if (!axeNum)
                return false;

            graphCentPts.forEach(function (d, i) {
                if (isOnLine(d[axeNum - 1], d[axeNum], mouseClick, 2)) {
                    clicked.push(activeData[i]);
                    clickedCenPts.push(graphCentPts[i]); // for tooltip
                }
            });

            return [clicked, clickedCenPts]
        }


        function highlightLineOnClick(mouseClick, drawTooltip) {

            var clicked = [];
            var clickedCenPts = [];

            clickedData = getClickedLines(mouseClick);

            if (clickedData && clickedData[0].length != 0) {

                clicked = clickedData[0];
                clickedCenPts = clickedData[1];

                // highlight clicked line
                graph.highlight(clicked);

                if (drawTooltip) {
                    // clean if anything is there
                    cleanTooltip();
                    // add tooltip
                    addTooltip(clicked, clickedCenPts);
                }

            }
        };

    </script>
</body>

1 个答案:

答案 0 :(得分:1)

首先,我移动了您的代码,使图形成为新函数

 function makeGraph(data){
            // collect text for first column to adjust left margin
      var firstCell = data.map(function(d) {
        return d3.values(d)[0]
      });
//...old code

接下来,我从csv load

调用这个makeGraph函数
d3.csv("my.csv", function(data) {
  makeGraph(data);

现在,当您的数据发生变化时 删除旧DOM 使用新数据集调用MakeGraph。

我正在通过超时做动态值。

window.setTimeout(function(){
          var k = {Ethnicity:"african-american",
          AgeAtTx:"45",
          Site:"supraglottic",
          Tcategory:"T4",
          Nodal_Disease:"N+",
          ecog:"1",
          Chemotherapy:"induction",
          Local_Therapy:"PLRT",
          "Probability of Survival": "0.5"
          }
          data.push(k);
          //remove the old DOM
          d3.selectAll("#wrapper").selectAll("*").remove();
          //make the graph
          makeGraph(data);
        }, 5000);

工作代码here

希望这有帮助!