在Google可视化API中使用分组聚合时保留格式

10
使用谷歌的可视化API,我使用google.visualization.data.group根据原始数据创建子表格。我的原始数据使用 {v:"US", f:"United States"} 这个技巧来显示除值以外的内容,但是当我使用聚合函数时,格式被删除,只剩下 "US" 部分。
有没有什么方法可以保留原始格式,或者在使用分组聚合创建的DataTable上轻松添加它?
示例数据:
[2010, {v:"MA", f:"Morocco"}, {v:"002", f:"Africa"}, {v:"002", f:"Northern Africa"}, 21.12724],
[2010, {v:"AW", f:"Aruba"}, {v:"019", f:"Americas  "}, {v:"019", f:"Caribbean"}, 0.98],
[2010, {v:"AF", f:"Afghanistan"}, {v:"142", f:"Asia"}, {v:"142", f:"Southern Asia"}, 0.9861],
[2010, {v:"AO", f:"Angola"}, {v:"002", f:"Africa"}, {v:"002", f:"Middle Africa"}, 5.11774],

聚合函数:

var countryData = google.visualization.data.group(
  rawData, 
  [0, 1], 
  [{'column': 4, 'aggregation': google.visualization.data.sum, 'type': 'number'}]
);

编辑:

经过进一步思考,由于无法保证每个值的格式都是一致的,所以很可能不可能通过格式进行分组。因此,最好(或者唯一可行的方法)是编写一个函数为数据的每一列添加格式。那么问题就变成了:“我该如何做到这一点?"

我真的不想仅将我的原始数据创建为未经格式化的值,然后再创建其他表格来查找每个值的格式。这需要额外的两个表格(一个是28行的地区表,一个是超过240行的国家表),然后创建两个函数来查找分组表中的每个值(该表将拥有30多年的数据,意味着数千行),以添加这些值。

这似乎是一个非常复杂的解决方案。

是否有一种使用修饰器函数来完成这项工作的方法?我能否编写一个函数来将表格中的每个值返回为{v:“US”,f:“美国”}格式的对象?或者是否有一种简单的方法来编写列格式化程序,以在原始表格中查找适当的值并采用该格式?哪种方法对我(必须编写它的人)和用户(必须加载它的人)造成的困扰最小?

编辑2:

看起来我应该可以使用类似于以下内容的格式化程序来创建新表格:

function (dt, row) {
    return {
        v: (dt.getValue(row, 1) / 1000000),
        f: (dt.getValue(row, 1) / 1000000) + 'M'
    }
}

但问题在于我不处理数字格式,因此我需要创建某种查找表,以获取值,查找查找表,然后返回相应的格式。看起来我还必须逐行遍历整个表格,这是成千上万行的。

我无法想象没有一种简单的方法可以实现这一点,而不需要进行一些蛮力循环和赋值。

编辑3:

所以我尝试了一些巧妙的方法。而不是将每行设置为值/格式,我将值/格式部分创建为字符串,并在分组后使用eval()来评估对象。这非常有效。以下是数据:

[2010, "{v: 'MA', f: 'Morocco'}", 21.13],
[2010, "{v: 'AW', f: 'Aruba'}", 0.98],
[2010, "{v: 'AF', f: 'Afghanistan'}", 0.99],
[2010, "{v: 'AO', f: 'Angola'}", 5.12],

以下是新代码:

  var countryCount = countryData.getColumnRange(0).count;

  for (var i = 0; i <= countryCount; i++) {
    countryData.setValue(i, 1, eval('(' + countryData.getValue(i,1) + ')'));
  };
问题在于当我将此输出到 Google DataTable 时,尽管使用 eval 正确地给出了以下结果,{v: 'AE',f:'United Arab Emirates'}。
>>> eval('(' + countryData.getValue(i,1) + ')')
Object v="AE" f="United Arab Emirates"

那么我在这里做错了什么呢?

2个回答

4

好的,我解决了这个问题(它是多么令人讨厌的复杂)。

我尝试了一种新的方法。我重新格式化了我的数据,然后创建了一个函数,根据字符串中的分隔符返回一个值/格式。所以我的数据现在看起来像这样:

[2010, "'MA'|'Morocco'", 21.13],
[2010, "'AW'|'Aruba'", 0.98],
[2010, "'AF'|'Afghanistan'", 0.99],
[2010, "'AO'|'Angola'", 5.12],

我随后使用这个方法来获取第一列的分割位置:
var countryCount = countryData.getNumberOfRows();

for (var i = 0; i <= countryCount; i++) {
  var stringToSplit = countryData.getValue(i,1);
  var dividerLocation = stringToSplit.indexOf("|");
  alert("Divider: " + dividerLocation + ", String: " + stringToSplit);
  countryData.setValue(i, 1, splitFormat(dividerLocation, stringToSplit));
};

然后我使用这个函数来拆分字符串:

  function splitFormat(dividerLocation, stringToSplit) {
    // alert("entered splitFormat Function");
    var stringValue = "";
    var formatValue = "";
    stringValue = stringToSplit.substring(0, dividerLocation);
    formatValue = stringToSplit.substring(dividerLocation + 1)
    alert("v: " + stringValue + ", f: " + formatValue);
    return {
      v: stringValue,
      f: formatValue
    }
      }

问题在于我将数据的第一列定义为“字符串”,但Firebug告诉我从splitFormat()函数返回的对象是一个对象(我猜是因为它是一个数组)。即使我使用v:和f:组件设置原始数据表,它也不接受返回的数组对象值,因为FireBug给出了以下非常有帮助的建议:
"Error: Type mismatch. Value [object Object] does not match type string in column index 1 (table.I.js,137)"

问题在于,虽然您可以使用 {v: , f:} 语法定义 DataTable,但是您无法将该语法返回到表格中,因为该列的值设置为字符串。相反,我使用了 DataTable 的 "setFormattedValue" 属性来解决这个问题:
  function drawVisualization() {
    var countryTable = new google.visualization.Table(document.getElementById('table'));

    var countryCount = countryData.getNumberOfRows() - 1;

    for (var i = 0; i <= countryCount; i++) {
      var stringToSplit = countryData.getValue(i,1);
      var dividerLocation = stringToSplit.indexOf("|");
      var stringValue = stringToSplit.substring(0, dividerLocation);
      var stringFormat = stringToSplit.substring(dividerLocation + 1);
      countryData.setValue(i, 1, stringValue);
      countryData.setFormattedValue(i, 1, stringFormat);
    };

这个方法可以正确地为我提供两个合适的值,尽管对于大数据集来说有点费力。如果有人知道更简单的方法,请告诉我。

2

我最近也遇到了这个问题。我决定使用修饰符将值更改为使用原始dataTable查找格式化值的格式化值。虽然这不是非常高效,但它可以工作,计算机速度很快。

首先创建一个查找函数:

function getFormatForValue(dataTable, column, value) {

    // we need to spin through column in the dataTable looking
    // for the matching value and then return the formatted value
    var rowcount = dataTable.getNumberOfRows();
    for (var i=0; i<rowcount; i++) {
        if (dataTable.getValue(i, column) === value) {

            // we found it, this will look much better
            return dataTable.getFormattedValue(i, column);    
        }
    }

    // better than nothing
    return value;
}

然后在一个修饰器中调用它,更改您原始的组调用:

var countryData = google.visualization.data.group(
    rawData, 
    [
     {
      'column': 0,
      'modifier': function(value) { return getFormatForValue(rawData, 0, value); },
      'type': 'string'
     },
     {
      'column': 1,
      'modifier': function(value) { return getFormatForValue(rawData, 1, value); },
      'type': 'string'
     }
    ], 
    [{'column': 4, 'aggregation': google.visualization.data.sum, 'type': 'number'}]
);

更新:看起来您需要保留值和格式化后的值。在我显示饼图的情况下,我不关心保留原始值。我猜这对您可能行不通,但我会将此答案留在这里,供其他像我一样有更简单情况的人参考。
我再花了几分钟时间,这里提供一种替代方案,可以在保留原始单元格值的同时复制格式化后的值。
创建一个使用查找函数的副本函数:
function copyFormattedValues(oldDataTable, oldColumn, newDataTable, newColumn) {

    var rowcount = newDataTable.getNumberOfRows();
    for (var i=0; i<rowcount; i++) {
        var value = newDataTable.getValue(i, newColumn);
        var formatted = getFormatForValue(oldDataTable, oldColumn, value);
        newDataTable.setFormattedValue(i, newColumn, formatted);
    }

 }

在您的情况下,为每个要复制的列调用一次。
copyFormattedValues(rawData, 0, countryData, 0);
copyFormattedValues(rawData, 1, countryData, 1);

您的源列和目标列相同,但在某些情况下它们可能不同。

当然,理想情况下,所有这些都应该自动完成。


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