转换 csv 数据以进行分析和可视化

Posted

技术标签:

【中文标题】转换 csv 数据以进行分析和可视化【英文标题】:Transforming csv data for analysis and visualization 【发布时间】:2013-04-22 04:11:38 【问题描述】:

假设我有一个具有以下数据格式的 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

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

附加信息:

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

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

我用来分析/可视化数据的工具是“Tableau Public”,他们有一个半自动化的 Excel 数据整形插件 such tasks(参见 Make确保每一行只包含一条数据),但没有 LibreOffice 替代方案。

【问题讨论】:

我也有同样的需求,对于 Tableau 也是如此。我真的很惊讶没有标准工具可以在这两种格式之间来回转换:crosstab/wide normalized/long 【参考方案1】:

您可以使用 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);
    
  
;

使用它:

    在您打开的工作表中打开脚本编辑器(工具 -> 脚本编辑器...) 为电子表格创建一个空项目 将代码粘贴到编辑器中 保存并运行它(运行 -> transformRows) 返回电子表格,将创建一个新工作表并填充转换后的数据。

【讨论】:

干得好,虽然我应该注意谷歌应用程序脚本将在 6 分钟后超时,对于我的数据集,它会在大约 4000 行时停止处理。通过将newSheet.appendRow(...) 替换为行缓存和newSheet.getRange(...).setValues(...),我们可以批量插入操作(这很慢)并避免超时。 see doc. 感谢您的信息。我之前没有意识到这个问题。在我的代码 sn-p 中添加了一些注释来注意它。【参考方案2】:

我制作了@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);
;

【讨论】:

以上是关于转换 csv 数据以进行分析和可视化的主要内容,如果未能解决你的问题,请参考以下文章

第 16 章 下载数据

使用python进行CSV数据分析[关闭]

Python 项目实践二(下载数据)第三篇

R数据可视化-1 序言

如何用python写 数据分析工具

数据可视化-使用Python进行图表叠加