2013-04-22 41 views
1

转化CSV数据说我有以下数据格式的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不同的列,但我停留在转置和重复的行。

附加信息:

  • 数据通过谷歌的形式被收集,不幸的是Google电子表格存储用分号分隔状A1;A2;A3...在一个单元选择题问题的答复,而我的可视化工具无法看到此基础数据结构,只把它们当作一个单一的字符串,使得聚合/分组困难。

  • 在实际数据(调查结果)我有围绕5000个条目,每个与需要这样的处理的多个小区,其将导致大约100,000的条目的表。需要一种自动化转换的方法。

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

+0

我也有同样的需求,也适用于Tableau。我真的很惊讶,也有不适合来回转换这两种格式之间的标准工具:交叉/宽<->标准化/长 – 2014-11-16 22:47:18

回答

2

在导出到其他应用程序之前,您可以使用Google Spreadsheet上的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. Retur在电子表格中,将创建一个新工作表并填充转换后的数据。
+0

伟大的工作,但我要指出的6分钟后谷歌应用程序脚本会超时,我的数据集它停止处理大约4000行。通过更换'''newSheet.appendRow(...)'''具有行缓存和'''newSheet.getRange(...)。setValues方法(...)'',我们可以批量插入操作(很慢)并避免超时。 [见文档](https://developers.google.com/apps-script/best_practices#batchOperations)。 – bitinn 2013-04-22 14:14:17

+0

感谢您的信息。我以前没有意识到这个问题。在我的代码片段中添加了一些注释来记录它。 – SAPikachu 2013-04-23 00:19:34

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