使用JavaScript将“长”数据转换为“宽”数组

4
在R中,有一种非常方便的方式可以使用{reshape2}包中的dcast函数将“长”数据转换为“宽”数据,并应用一个函数(如计算出现次数)。我想在javascript中使用类似的转换方法。以下是一个R示例(附带样本数据):
library(reshape2)
data.long = read.table("so_question2.csv", sep = "\t")

head(data.long)
# This is how a first few rows of the original data look like:
#    dpt    sem
# 15 Dpt.4  2014.1
# 16 Dpt.4  2014.1
# 17 Dpt.4  2014.1
# 18 Dpt.4  2014.1
# 19 Dpt.4  2014.1
# 20 Dpt.4  2014.1

data.wide = dcast(data, sem ~ dpt, length)

head(data.wide)
# This is how it looks like after the transformation; there are counts in columns now
#   sem        Dpt.1 Dpt.2 Dpt.3 Dpt.4
# 1 2012.1     0     0     0     8
# 2 2012.2     3     6     0    21
# 3 2013.1     3     4     0    29
# 4 2013.2     5     1     2    39
# 5 2014.1     5     3     7    39

为什么需要这个功能:我正在学习使用Google Charts,并希望能够在从电子表格中导入数据后对其进行转换。我知道可以在电子表格本身中进行转换;但是在电子表格中添加大量公式会很不方便。我更希望每个图表都能在其自己的脚本中进行转换。


在标准的 JavaScript 中没有类似的功能,如果想要使用这个转换操作,你需要在 JS 中编写一个 dcast() 实现。 - BadZen
一些库可能吗? - jakub
我不知道有任何类似的。"融合"数据框的概念非常特定于R语言。关系型数据库理论有类似于"宽"和"长"数据格式的概念,但这并不能帮助你... - BadZen
1个回答

2
我也遇到过这个问题!这里有一个方便的函数可以帮助你解决它。
// helper function
function addIfMissing(array, value) {
    var found = false;
    for(var i = 0; i < array.length; i++)
        if(array[i] === value)
            return array;
    array.push(value);
    return array;
}

function restructure(input) {
    var output = [], headerX = [], headerY = [], xCoor, yCoor;

    // first create non-repeating headers
    headerX.push(input[0][0]);
    headerY.push(input[0][0]);
    for(var i = 1; i < input.length; i++)
        headerX = addIfMissing(headerX, input[i][0]), headerY = addIfMissing(headerY, input[i][1]);

    // put headers into output array
    for(var i = 0; i < headerX.length; i++)
        output.push([headerX[i]]);
    output[0] = headerY;

    // find correct headers on both axes and input data
    for(var i = 1; i < input.length; i++) {
        for(var k = 1; k < headerX.length; k++)
            if(output[k][0] == input[i][0])
                xCoor = k;
        for(var j = 1; j < headerY.length; j++)
            if(output[0][j] == input[i][1])
                yCoor = j;
        output[xCoor][yCoor] = input[i][2];
    }

    return output;
}

我用以下内容进行了测试:

var input = [
    ['Season', 'Type', 'Number'],
    ['Winter', 'Sales', 1000],
    ['Winter', 'Expenses', 400],
    ['Winter', 'Profit', 200],
    ['Spring', 'Sales', 1170],
    ['Spring', 'Expenses', 460],
    ['Spring', 'Profit', 250],
    ['Summer', 'Sales', 660],
    ['Summer', 'Expenses', 1120],
    ['Summer', 'Profit', 300],
    ['Fall', 'Sales', 1030],
    ['Fall', 'Expenses', 540],
    ['Fall', 'Profit', 350]
];

var desiredOutput = [
    ['Season', 'Sales', 'Expenses', 'Profit'],
    ['Winter', 1000, 400, 200],
    ['Spring', 1170, 460, 250],
    ['Summer', 660, 1120, 300],
    ['Fall', 1030, 540, 350]
];

var output = restructure(input);

if (JSON.stringify(output) == JSON.stringify(desiredOutput)) 
    alert("Pass");

测试示例JSFiddle

我如何使用谷歌图表实现它的示例


我在尝试您的解决方案时发现了一个错误:如果我删除“Winter”、“Sales”和“Profit”行,则会得到一个冬季的结果输出行为:["Winter",400]。我期望正确的输出应该是["Winter",null,400,null]或["Winter", 0, 400, 0],但我认为使用“null”值来表示该类别的值不存在更好。 - Chris Knoll
1
更新,进一步尝试:数据的顺序很重要。当我将'type'放在一起时,如果发现所有不同的类型,唯一的真正问题是给定“季节”的数组中的最后一个元素可能未设置为“null”,但如果您访问数组中的元素,则会获得null(或可能是undefined)。如果所有输出行具有相同数量的元素,null表示不存在,那将是理想的。 - Chris Knoll

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接