d3JS:在线/面积图上缩小时绘制较大数据集的较低密度数据版本

eka*_*atz 5 javascript zoom large-data d3.js

我正在创建一个类似于Mike Bostock的可缩放区域图表的图表.

对于我的具体项目,我有一堆传感器,每隔30秒(温度,光线,湿度和声音)记录值.我有缩放实现工作,但是当我缩小到一年的比例时,图表的密度减慢了浏览器和图形也没有读取.

如何编辑脚本以使折线图的密度相对于缩放量发生变化?换句话说,x域控制着值线上的点数.当我放大到一小时的时间范围时,我想要全密度(每30秒录制一次),当我缩小时,我希望密度更低(每天录制一次).有任何想法吗?使用上面链接中的脚本实现将很有帮助.

谢谢!

    <!DOCTYPE html>
    <html>
      <head>
        <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
        <script type="text/javascript" src="d3/d3.js"></script>
        <script type="text/javascript" src="d3/d3.csv.js"></script>
        <script type="text/javascript" src="d3/d3.time.js"></script>
        <link type="text/css" rel="stylesheet" href="style.css"/>
        <style type="text/css">

    svg {
      font-size: 10px;
    }

    .axis {
      shape-rendering: crispEdges;
    }

    .axis path, .axis line {
      fill: none;
      stroke-width: .5px;
    }

    .x.axis path {
      stroke: #000;
    }

    .x.axis line {
      stroke: #fff;
      stroke-opacity: .5;
    }

    .y.axis line {
      stroke: #ddd;
    }

    path.line {
      fill: none;
      stroke: #000;
      stroke-width: .5px;
    }

    rect.pane {
      cursor: move;
      fill: none;
      pointer-events: all;
    }

        </style>
      </head>
      <body>
        <div id="body">
          <div id="footer">
            <span>…</span>
            <div class="hint">mousewheel to zoom, drag to pan</div>
          </div>
        </div>
        <script type="text/javascript">

    var m = [79, 80, 160, 79],
        w = 1280 - m[1] - m[3],
        h = 800 - m[0] - m[2],
        parse = d3.time.format("%Y-%m-%d").parse,
        format = d3.time.format("%Y");

    // Scales. Note the inverted domain for the y-scale: bigger is up!
    var x = d3.time.scale().range([0, w]),
        y = d3.scale.linear().range([h, 0]),
        xAxis = d3.svg.axis().scale(x).orient("bottom").tickSize(-h, 0).tickPadding(6),
        yAxis = d3.svg.axis().scale(y).orient("right").tickSize(-w).tickPadding(6);

    // An area generator.
    var area = d3.svg.area()
        .interpolate("step-after")
        .x(function(d) { return x(d.date); })
        .y0(y(0))
        .y1(function(d) { return y(d.value); });

    // A line generator.
    var line = d3.svg.line()
        .interpolate("step-after")
        .x(function(d) { return x(d.date); })
        .y(function(d) { return y(d.value); });

    var svg = d3.select("body").append("svg:svg")
        .attr("width", w + m[1] + m[3])
        .attr("height", h + m[0] + m[2])
      .append("svg:g")
        .attr("transform", "translate(" + m[3] + "," + m[0] + ")");

    var gradient = svg.append("svg:defs").append("svg:linearGradient")
        .attr("id", "gradient")
        .attr("x2", "0%")
        .attr("y2", "100%");

    gradient.append("svg:stop")
        .attr("offset", "0%")
        .attr("stop-color", "#fff")
        .attr("stop-opacity", .5);

    gradient.append("svg:stop")
        .attr("offset", "100%")
        .attr("stop-color", "#999")
        .attr("stop-opacity", 1);

    svg.append("svg:clipPath")
        .attr("id", "clip")
      .append("svg:rect")
        .attr("x", x(0))
        .attr("y", y(1))
        .attr("width", x(1) - x(0))
        .attr("height", y(0) - y(1));

    svg.append("svg:g")
        .attr("class", "y axis")
        .attr("transform", "translate(" + w + ",0)");

    svg.append("svg:path")
        .attr("class", "area")
        .attr("clip-path", "url(#clip)")
        .style("fill", "url(#gradient)");

    svg.append("svg:g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + h + ")");

    svg.append("svg:path")
        .attr("class", "line")
        .attr("clip-path", "url(#clip)");

    svg.append("svg:rect")
        .attr("class", "pane")
        .attr("width", w)
        .attr("height", h)
        .call(d3.behavior.zoom().on("zoom", zoom));

    d3.csv("flights-departed.csv", function(data) {

      // Parse dates and numbers.
      data.forEach(function(d) {
        d.date = parse(d.date);
        d.value = +d.value;
      });

      // Compute the maximum price.
      x.domain([new Date(1999, 0, 1), new Date(2003, 0, 0)]);
      y.domain([0, d3.max(data, function(d) { return d.value; })]);

      // Bind the data to our path elements.
      svg.select("path.area").data([data]);
      svg.select("path.line").data([data]);

      draw();
    });

    function draw() {
      svg.select("g.x.axis").call(xAxis);
      svg.select("g.y.axis").call(yAxis);
      svg.select("path.area").attr("d", area);
      svg.select("path.line").attr("d", line);
      d3.select("#footer span").text("U.S. Commercial Flights, " + x.domain().map(format).join("-"));
    }

    function zoom() {
      d3.event.transform(x); // TODO d3.behavior.zoom should support extents
      draw();
    }

        </script>
      </body>
    </html>
Run Code Online (Sandbox Code Playgroud)

Ame*_*aBR 7

Lars和Ari是正确的,这绝对不是一个小问题.但我认为它是一个重要的,可以用于许多人(包括将来可能是我自己),因此值得花时间去弄清楚.

所以你可以跟随,这是我对迈克博斯托克的每日飞行计数图的调整,这样它可以在缩小时(而不是单个天)绘制每周/每月/每年平均每日飞行计数的图表,并且只显示可以的数据子集在任何缩放级别显示:http:
//fiddle.jshell.net/ncy5J/2/

这是我必须做的一步一步细分:

  1. 获取非常大的csv数据表作为JSFiddle脚本中的嵌入变量.我假设你不会这样做,但我提到它是因为这是一个麻烦.\n\在我可以d3.csv.parse()在字符串上运行之前,必须在每行的末尾添加一个.

  2. 创建数周,数月和数年的备用数据阵列,并计算这些时间段的平均每日值:

    • 使用d3.nest使用d3 函数interval.floor()的关键函数来获取同一年,月等的所有日期以嵌套在一起;

    • Array.forEach在具有自定义函数的嵌套数组上使用,以访问嵌套对象数组,计算其值的平均值,然后将对象创建的对象替换nest()为与原始数据的格式匹配的对象(下面的代码).

  3. 将数据绑定步骤从初始化移动到重绘功能,并将该功能更改为接受数据数组作为参数.

  4. 更新d3.behavior.zoom方法以匹配D3版本3 API(原始示例使用v2.4,其具有用于将缩放行为链接到缩放的不同方法).

  5. zoom将缩放行为调用的函数更改为

    • 从x刻度访问可见数据域(由缩放行为自动更新);

    • 计算该域所涵盖的时间范围;

    • 选择我的四个数据数组中的一个,该数组将具有该范围的适当精度级别;

    • 扫描数组以找到可见域中的元素部分(正如我在代码注释中提到的,当你一直放大时,这会有点慢;你也可以使用日期时间数学来弄清楚数组的右边部分,因为连续元素之间的时间间隔总是相同的).

    • 使用相应的数组切片调用重绘功能作为传入数据.

这是步骤2中的自定义嵌套/平均例程:

AllData.yearly = d3.nest().key(function(d){
                    return d3.time.year.floor(d.date);
                })
                .entries(data);
AllData.yearly.forEach(meanDaily);

function meanDaily(nestedObject, i, array){
    //This function is passed to the Array.forEach() method
    //which passes in:
    // (1) the element in the array
    // (2) the element's index, and
    // (3) the array as a whole

    //It replaces the element in the array 
    //(an object with properties key and values, where
    // values is an array of nested objects)
    //with a single object that has the nesting key
    //value parsed back into a date, 
    //and the mean of the nested values' value
    //as the value.  It makes sense, I swear.
    array[i] = {date:new Date(nestedObject.key), 
            value:d3.mean(nestedObject.values, 
                          function(d) {return d.value;}
                         )};
}
Run Code Online (Sandbox Code Playgroud)

zoom方法只是基本的javascript,关键部分是你可以直接从x-scale访问可见域,然后使用它来确定哪些数据点传递给draw函数.

PS以不同的平均尺度查看数据很有意思.9月11日之后航班的急剧下降在每日,每周和每月排行榜上脱颖而出,但从年平均值中消失.相反,年度平均值表明,2002年的平均每日航班数量比2001年低,这提醒人们,许多人在飞行禁令解除后不敢长途飞行.