【问题标题】:Optimize javascript loop on CSV data优化 CSV 数据的 javascript 循环
【发布时间】:2017-06-17 21:08:40
【问题描述】:

我正在使用 d3.js 通过加载外部 .CSV 文件来绘制图表。 到目前为止,我的代码在少量数据上运行良好,但是当我加载包含数千行的较大文件时,它会杀死页面。

数据有一个使用列,该列是全天每 30 分钟的一个值,将持续数月。

参见Plunker 示例。

var avgClientArr = [];
var dateArr = [];
var dateGroupArr = [];

function csvParseClient() {
    d3.xhr('client.csv').get(function(err, response) {
        var dirtyCSV = response.responseText;
        var initialClientKeys = /TYPE,DATE,START TIME,END TIME,USAGE,UNITS,NOTES/i;
        var newClientKeys = "TYPE,x,startTime,endTime,y,UNITS,NOTES";
        var csvDataClient = dirtyCSV.replace(initialClientKeys, newClientKeys);
        var validData = csvDataClient.substr(csvDataClient.indexOf(newClientKeys));
        var csvData = d3.csv.parse(validData);

        csvData.customForEach(function(val, i) {
            // filter data
            //var keep = ['x', 'startTime', 'endTime', 'UNITS', 'y'];
            //for (var key in val[i]) {
            //    if (keep.indexOf(key) === -1) {
            //        delete val[i][key];
            //    }
            //}

            // parse data
            var date = val.x;
            var usage = val.y;
            var startTime = val.startTime;
            var endTime = val.endTime;
            var x = new Date(date);
            var y = parseFloat(usage);

            dateArr.push({
                "date": x,
                "usage": y
            })
            dateGroupArr = groupBy(dateArr, 'date');
        })
console.log(dateGroupArr);
        var objDates = objectValues(dateGroupArr);

        objDates.customForEach(function(f) {
                var avg = f.reduce(function(a, b) {
                    return a + b.usage;
                }, 0) / f.length;
                var date = f.reduce(function(a, b) {
                    return new Date(b.date);
                }, 0);
                avgClientArr.push({
                    "x": date,
                    "y": avg
                })
            })
            //console.log("avgClientArr", avgClientArr);
        document.getElementById('arrayDiv').innerHTML = '<pre>' + JSON.stringify(avgClientArr, null, 4) + '</pre>';
    })
}

function groupBy(arr, key) {
    var reducer = (grouped, item) => {
        var group_value = item[key]
        if (!grouped[group_value]) {
            grouped[group_value] = []
        }
        grouped[group_value].push(item)
        return grouped
    }
    return arr.reduce(reducer, {})
}

function objectValues(object) {
    var values = []
    for (var property in object) {
        if (object.hasOwnProperty(property)) {
            values.push(object[property])
        }
    }
    return values
}

function foreach(fn) {
    var arr = this;
    var len = arr.length;
    for (var i = 0; i < len; ++i) {
        fn(arr[i], i);
    }
}

Object.defineProperty(Array.prototype, 'customForEach', {
    enumerable: false,
    value: foreach
});

var t0 = performance.now();
csvParseClient();
var t1 = performance.now();
console.log("Call csvParseClient() " + (t1 - t0) + " milliseconds.");

我需要做什么

我需要返回为y 的全天使用平均值,以及返回为x 的每一天的日期。

我的过程很慢

  1. 从 CSV 文件中的指定行开始循环,因为前几行有不需要的数据。
  2. 对唯一日期进行分组并将该日期的每个使用值存储在一个对象中。
  3. 平均每个日期的使用值。
  4. 输出一个对象数组,属性x 为日期,y 为平均使用值。

如果你能给我任何关于如何让这个运行更快的帮助,那就太好了!

【问题讨论】:

  • 在 CSV 文件中放入较少的数据,即在浏览器中处理之前对其进行聚合。
  • papaparse.com 似乎是一个有用的库,它具有流功能。我尝试学习足够的 clojurescript 来解决这个问题,但还没有做到。

标签: javascript arrays csv d3.js


【解决方案1】:

我通过使用 d3 nest()rollup() 函数解决了这个问题,它简单而且非常快。

d3.nest()
.key(function(d) {
    return d.x;
})
.rollup(function(d) {
    var avg = d3.mean(d, function(g) {return g.y; });
    return avg;
}).entries(dateArr); 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-24
    • 2015-02-17
    • 2021-04-16
    相关资源
    最近更新 更多