假设我有一个包含以下数据格式的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 Public ”,他们有一个Excel的数据整形器插件,可以半自动化such tasks(参见确保每行只包含一个数据),但不包含LibreOffice备选方案。
答案 0 :(得分: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 :(得分: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);
};