Сохранение форматирования с помощью группировки по агрегации в Google Visualization API

Используя API визуализации Google, я использую google.visualization.data.group для создания подтаблиц на основе моих необработанных данных. Мои необработанные данные используют прием {v:"US", f:"United States"} для отображения чего-то другого, кроме значения, но когда я использую функцию агрегирования, форматирование устраняется, оставляя только часть "США".

Есть ли способ сохранить исходное форматирование или простой способ добавить его обратно в таблицы данных, созданные с помощью группового агрегирования?

Образец данных:

[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'}]
);

Редактировать:

При дальнейшем размышлении, вероятно, невозможно сгруппировать по формату, поскольку нет гарантии, что формат для каждого значения будет согласованным. Имея это в виду, вероятно, лучше (или только возможно) написать функцию, которая добавит форматы в каждый столбец моих данных. Таким образом, возникает вопрос: «Как, черт возьми, мне это сделать?»

Я действительно предпочел бы не создавать необработанные данные только как неформатированные значения, а затем дополнительные таблицы для поиска форматов для каждого значения. Для этого потребуются дополнительные 2 таблицы (одна для регионов с 28 строками, одна для стран с более чем 240 строками), а затем создание двух функций для просмотра каждого значения в сгруппированной таблице (в которой будут данные за 30+ лет, что означает тысячи строк) добавление значений.

Это кажется действительно сложным решением.

Есть ли способ сделать это с помощью функций-модификаторов? Могу ли я написать функцию для возврата каждого значения в таблице в виде форматированного объекта {v: "US", f: "United States"}? Или есть простой способ написать средство форматирования столбцов, которое будет искать соответствующее значение в моей исходной таблице и принимать этот формат? Что вызвало бы наименьшую головную боль как у меня (кто должен это написать), так и у пользователей (кто должен это загрузить)?

РЕДАКТИРОВАТЬ 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, он показывает {v: 'AE', f: 'Объединенные Арабские Эмираты'}, несмотря на то, что проверка результата с помощью eval правильно дает мне:

>>> eval('(' + countryData.getValue(i,1) + ')')
Object v="AE" f="United Arab Emirates"

Так что я делаю неправильно здесь?


person jmac    schedule 28.12.2012    source источник


Ответы (2)


Я сам только что столкнулся с этой проблемой. Я решил использовать модификатор, чтобы изменить значение на отформатированное значение, используя исходную таблицу данных, чтобы найти отформатированные значения. Это не очень эффективно, но работает, и компьютеры работают быстро.

Сначала создайте функцию поиска:

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);

Ваши столбцы источника и назначения одинаковы, но в некоторых случаях они могут отличаться.

Конечно, в идеале все это должно было происходить автоматически.

person andykellr    schedule 11.02.2015

Хорошо, я понял это (насколько это было ужасно сложно).

Я попробовал новый подход. Я переформатировал свои данные, а затем создал функцию для возврата значения/формата на основе разделителя в этой строке. Итак, мои данные теперь выглядят так:

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

Затем я использую это, чтобы получить расположение разделителя для столбца 1:

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
    }
      }

Проблема в том, что я определяю столбец 1 своих данных как «строку», но firebug сообщает мне, что объект, возвращаемый функцией splitFormat(), является объектом (поскольку я полагаю, что это массив). Даже если я устанавливаю исходный datatable с компонентом v: и f:, он не хочет принимать возвращаемое значение объекта массива, поскольку FireBug дает мне следующий очень полезный совет:

"Error: Type mismatch. Value [object Object] does not match type string in column index 1 (table.I.js,137)"

Проблема в том, что хотя вы можете определить DataTable, используя синтаксис {v:, f:}, вы не можете вернуть этот синтаксис обратно в таблицу, потому что значение для этого столбца установлено как строка. Вместо этого я использовал свойство «setFormattedValue» DataTable, чтобы исправить проблему:

  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);
    };

Это правильно дало мне соответствующие значения для обоих, хотя это немного интенсивно для больших наборов данных. Если кто-нибудь знает более простой способ сделать это, то я был бы более чем счастлив услышать это.

person jmac    schedule 07.01.2013