1

假设我有一个具有以下数据格式的 csv 文件:

ID, Name, Gender, Q1
1, ABC, Male, "A1;A2"
2, ACB, Male, "A2;A3;A4"
3, BAC, Female, "A1"

我想将其转换为以下格式,以便我的数据虚拟化工具可以正确处理它:

ID, Name, Gender, Questions, Responses
1, ABC, Male, Q1, A1
1, ABC, Male, Q1, A2
2, ACB, Male, Q1, A2
2, ACB, Male, Q1, A3
2, ACB, Male, Q1, A4
3, BAC, Female, Q1, A1

使用Text to ColumnsLibreOffice 中的功能,我可以轻松地将 Q1 列A1;A2分成不同的列,例如A1, A2,但我被困在转置和重复行上。

附加信息:

  • 数据是通过谷歌表单收集的,不幸的是谷歌电子表格使用分号分隔符将多项选择问题响应存储在一个单元格中A1;A2;A3...,而我的可视化工具无法看到这个底层数据结构,只能将它们视为单个字符串,使得聚合/分组变得困难。

  • 在实际数据(调查结果)中,我有大约 5000 个条目,每个条目都有多个需要此类处理的单元格,这将产生一个包含大约 100,000 个条目的表。需要一种自动化转换的方法。

  • 我用来分析/可视化数据的工具是“ Tableau Public ”,他们有一个用于 Excel 的数据重塑器插件,可以半自动化此类任务(请参阅确保每行仅包含一个数据部分),但没有 LibreOffice 替代品。

4

2 回答 2

2

您可以使用 Google 电子表格上的 JavaScript 来转换数据,然后再导出到其他应用程序。这是我刚刚为您的示例数据编写的快速而肮脏的脚本:

function transformRows() {
  var sheet = SpreadsheetApp.getActiveSheet();
  var rows = sheet.getDataRange();
  var numRows = rows.getNumRows();
  var values = rows.getValues();

  var newSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet("Result");
  var header = values[0].slice(0, values[0].length - 1);

  header.push("Question");
  header.push("Answer");
  newSheet.appendRow(header);

  var question = values[0][values[0].length - 1];

  // Note: Code below is inefficient and may exceed 6-minute timeout for sheets with 
  //       more than 1k rows. Change it to batch updating to speed up. 
  // Ref: https://developers.google.com/apps-script/reference/spreadsheet/range#setValues%28Object%29
  for (var i = 1; i <= numRows - 1; i++) {
    var row = values[i];
    var answers = row[row.length - 1].split(";");
    for (var ansi = 0; ansi < answers.length; ansi++) {
      var newRow = row.slice(0, row.length - 1);
      newRow.push(question);
      newRow.push(answers[ansi]);
      newSheet.appendRow(newRow);
    }
  }
};

要使用它:

  1. 在打开的工作表中打开脚本编辑器(工具 -> 脚本编辑器...)
  2. 为电子表格创建一个空项目
  3. 将代码粘贴到编辑器中
  4. 保存并运行它(运行-> transformRows)
  5. 返回电子表格,将创建一个新工作表并填充转换后的数据。
于 2013-04-22T10:18:22.953 回答
0

我制作了@SAPikachu 答案的更通用版本。它可以转换任意数量的数据列,假设所有数据列都在所有非数据列的右侧。(不是最清晰的术语......)

function onOpen() {
  var ss = SpreadsheetApp.getActive();
  var items = [
    {name: 'Normalize Crosstab', functionName: 'normalizeCrosstab'},
  ];
  ss.addMenu('Normalize', items);
}

/* Converts crosstab format to normalized form. Given columns abcDE, the user puts the cursor somewhere in column D.
The result is a new sheet, NormalizedResult, like this:

a     b     c    Field Value
a1    b1    c1   D     D1
a1    b1    c1   E     E1
a2    b2    c2   D     D2
a2    b2    c2   E     E2
...

*/
function normalizeCrosstab() {
  var sheet = SpreadsheetApp.getActiveSheet(); 
  var rows = sheet.getDataRange();
  var numRows = rows.getNumRows();
  var values = rows.getValues();
  var firstDataCol = SpreadsheetApp.getActiveRange().getColumn();
  var dataCols = values[0].slice(firstDataCol-1);

  if (Browser.msgBox("This will create a new sheet, NormalizedResult. Place your cursor is in the first data column.\\n\\n" +
                     "These will be your data columns: " + dataCols,Browser.Buttons.OK_CANCEL) == "cancel") {
    return;
  }


  var resultssheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("NormalizedResult");
  if (resultssheet != null) {
    SpreadsheetApp.getActive().deleteSheet(resultssheet);
  }
  var newSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet("NormalizedResult");
  var header = values[0].slice(0, firstDataCol - 1);

  var newRows = [];

  header.push("Field");
  header.push("Value");
  newRows.push(header);

  for (var i = 1; i <= numRows - 1; i++) {
    var row = values[i];
    for (var datacol = 0; datacol < dataCols.length; datacol ++) {
      newRow = row.slice(0, firstDataCol - 1); // copy repeating portion of each row
      newRow.push(values[0][firstDataCol - 1 + datacol]); // field name
      newRow.push(values[i][firstDataCol - 1 + datacol]); // field value
      //newSheet.appendRow(newRow);
      newRows.push(newRow);
    }
  }
  var r = newSheet.getRange(1,1,newRows.length, header.length);
  r.setValues(newRows);
};
于 2014-11-17T03:31:51.693 回答