2013-04-22 38 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); 
};