在 Google 表格中逆透视 - 多 header 列和行
Unpivot in Google Sheets - multipe header columns and rows
我有一个摘要 table,大约有 20 列和最多一百行,但是我想将它转换成一个平面列表,以便我可以导入到数据库中。
This solution 在我的情况下无法正常工作,我对 JS 的了解远远不足以正确调整它。
Example sheet 中有三个选项卡:
- 源数据 - 我当前拥有的虚拟数据
- 期望的结果 - 我想将源数据转换成什么
- 我得到的结果 - 使用上述 the solution 时得到的结果
Sheet 已共享,因此您可以尝试测试脚本(菜单 > 脚本 > 运行)。它会自动创建一个新标签。
这是我在学习 JS 和谷歌搜索后得出的结论。如果有人可以建议如何制作它 shorter/cleaner/simpler - 我洗耳恭听。可能远非完美,但它确实满足了我的需要。
function transpose(){
var source = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('SOURCE DATA');
var ss = SpreadsheetApp.getActiveSpreadsheet();
var numColumns = source.getLastColumn();
var numRows = source.getLastRow();
Logger.log('numColumns = ' +numColumns);
Logger.log('numRows = ' +numRows);
//GET NUMBER OF HEADERS (PRODUCTS)
var products = []; // get product models in the first row
for (var b = 2; b <= numColumns; b++){
if (source.getRange(1, b).getValue() != "") {
products.push([source.getRange(1, b).getValue()]); //store
}
}
// PRODUCTS and SITES INTO COLUMNS
var output = [];
var sites = []; // get sites list
for (var a = 3; a <= numRows; a++){
if (source.getRange(a, 1).getValue() != "") {
sites.push([source.getRange(a, 1).getValue()]); //store
}
}
for(var p in products){
for(var s in sites){
var row = [];
row.push(sites[s]);
row.push(products[p]);
output.push(row);//collect data in separate rows in output array
}
}
var date = Utilities.formatDate(new Date(), SpreadsheetApp.getActive().getSpreadsheetTimeZone(), "M/d/yyyy");
Logger.log('Date = ' +date)
ss.insertSheet(date,0).getRange(1,1,output.length,output[0].length).setValues(output);
var newSheet = ss.getSheetByName(date);
// COPY REGIONS
var numProducts = products.length; // number of models
Logger.log('numProducts = ' +numProducts);
var i = 1;
var j = 3 // first column number to copy
do {
var colC = newSheet.getRange("C1:C").getValues();
var copyToCell = colC.filter(String).length+1;
Logger.log('copyTo R = ' +copyToCell);
source.getRange(3,2,numRows-2,1).copyTo(newSheet.getRange(copyToCell,3), {contentsOnly:true});
i++;
source.getRange(3,j,numRows-2,2).copyTo(newSheet.getRange(copyToCell,4), {contentsOnly:true});
j+=2;
}
while (i <= numProducts);
while (j < numColumns);
// SORT BY SITE AND PRODUCT
newSheet.getDataRange().sort([1, 2]);
}
我认为您可以使用 CONCAT
从位置和区域获取唯一的行键,并使用 my answer here 中的方法,不需要额外的脚本。
如果您对键连接做了一些花哨的事情(例如 CONCAT(A3, "~", A4)
),您甚至可以在逆轴 table 中提取您的原始位置和区域(例如 REGEXEXTRACT(A1, "(\w+)~")
和 REGEXEXTRACT(A1, "~(\w+)")
).
我在 Google 电子表格中编写了这个简单的通用自定义函数,用于取消透视/反向透视。 注意:这是一个自定义函数,与任何其他内置电子表格函数一样,因此您不必 运行 它,您可以像 unique()
一样使用它或任何其他数组函数。
在您的情况下,只需使用以下代码即可:=unpivot(A1:H6,2,2,"product","MIN","MAX")
这是代码:
/**
* Unpivot a pivot table of any size.
*
* @param {A1:D30} data The pivot table.
* @param {1} fixColumns Number of columns, after which pivoted values begin. Default 1.
* @param {1} fixRows Number of rows (1 or 2), after which pivoted values begin. Default 1.
* @param {"city"} titlePivot The title of horizontal pivot values. Default "column".
* @param {"distance"[,...]} titleValue The title of pivot table values. Default "value".
* @return The unpivoted table
* @customfunction
*/
function unpivot(data,fixColumns,fixRows,titlePivot,titleValue) {
var fixColumns = fixColumns || 1; // how many columns are fixed
var fixRows = fixRows || 1; // how many rows are fixed
var titlePivot = titlePivot || 'column';
var titleValue = titleValue || 'value';
var ret=[],i,j,row,uniqueCols=1;
// we handle only 2 dimension arrays
if (!Array.isArray(data) || data.length < fixRows || !Array.isArray(data[0]) || data[0].length < fixColumns)
throw new Error('no data');
// we handle max 2 fixed rows
if (fixRows > 2)
throw new Error('max 2 fixed rows are allowed');
// fill empty cells in the first row with value set last in previous columns (for 2 fixed rows)
var tmp = '';
for (j=0;j<data[0].length;j++)
if (data[0][j] != '')
tmp = data[0][j];
else
data[0][j] = tmp;
// for 2 fixed rows calculate unique column number
if (fixRows == 2)
{
uniqueCols = 0;
tmp = {};
for (j=fixColumns;j<data[1].length;j++)
if (typeof tmp[ data[1][j] ] == 'undefined')
{
tmp[ data[1][j] ] = 1;
uniqueCols++;
}
}
// return first row: fix column titles + pivoted values column title + values column title(s)
row = [];
for (j=0;j<fixColumns;j++) row.push(fixRows == 2 ? data[0][j]||data[1][j] : data[0][j]); // for 2 fixed rows we try to find the title in row 1 and row 2
for (j=3;j<arguments.length;j++) row.push(arguments[j]);
ret.push(row);
// processing rows (skipping the fixed columns, then dedicating a new row for each pivoted value)
for (i=fixRows;i<data.length && data[i].length > 0 && data[i][0];i++)
{
row = [];
for (j=0;j<fixColumns && j<data[i].length;j++)
row.push(data[i][j]);
for (j=fixColumns;j<data[i].length;j+=uniqueCols)
ret.push(
row.concat([data[0][j]]) // the first row title value
.concat(data[i].slice(j,j+uniqueCols)) // pivoted values
);
}
return ret;
}
在 google sheet 中使用 Flatten 函数,该函数仍未记录。
我有一个摘要 table,大约有 20 列和最多一百行,但是我想将它转换成一个平面列表,以便我可以导入到数据库中。
This solution 在我的情况下无法正常工作,我对 JS 的了解远远不足以正确调整它。
Example sheet 中有三个选项卡:
- 源数据 - 我当前拥有的虚拟数据
- 期望的结果 - 我想将源数据转换成什么
- 我得到的结果 - 使用上述 the solution 时得到的结果
Sheet 已共享,因此您可以尝试测试脚本(菜单 > 脚本 > 运行)。它会自动创建一个新标签。
这是我在学习 JS 和谷歌搜索后得出的结论。如果有人可以建议如何制作它 shorter/cleaner/simpler - 我洗耳恭听。可能远非完美,但它确实满足了我的需要。
function transpose(){
var source = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('SOURCE DATA');
var ss = SpreadsheetApp.getActiveSpreadsheet();
var numColumns = source.getLastColumn();
var numRows = source.getLastRow();
Logger.log('numColumns = ' +numColumns);
Logger.log('numRows = ' +numRows);
//GET NUMBER OF HEADERS (PRODUCTS)
var products = []; // get product models in the first row
for (var b = 2; b <= numColumns; b++){
if (source.getRange(1, b).getValue() != "") {
products.push([source.getRange(1, b).getValue()]); //store
}
}
// PRODUCTS and SITES INTO COLUMNS
var output = [];
var sites = []; // get sites list
for (var a = 3; a <= numRows; a++){
if (source.getRange(a, 1).getValue() != "") {
sites.push([source.getRange(a, 1).getValue()]); //store
}
}
for(var p in products){
for(var s in sites){
var row = [];
row.push(sites[s]);
row.push(products[p]);
output.push(row);//collect data in separate rows in output array
}
}
var date = Utilities.formatDate(new Date(), SpreadsheetApp.getActive().getSpreadsheetTimeZone(), "M/d/yyyy");
Logger.log('Date = ' +date)
ss.insertSheet(date,0).getRange(1,1,output.length,output[0].length).setValues(output);
var newSheet = ss.getSheetByName(date);
// COPY REGIONS
var numProducts = products.length; // number of models
Logger.log('numProducts = ' +numProducts);
var i = 1;
var j = 3 // first column number to copy
do {
var colC = newSheet.getRange("C1:C").getValues();
var copyToCell = colC.filter(String).length+1;
Logger.log('copyTo R = ' +copyToCell);
source.getRange(3,2,numRows-2,1).copyTo(newSheet.getRange(copyToCell,3), {contentsOnly:true});
i++;
source.getRange(3,j,numRows-2,2).copyTo(newSheet.getRange(copyToCell,4), {contentsOnly:true});
j+=2;
}
while (i <= numProducts);
while (j < numColumns);
// SORT BY SITE AND PRODUCT
newSheet.getDataRange().sort([1, 2]);
}
我认为您可以使用 CONCAT
从位置和区域获取唯一的行键,并使用 my answer here 中的方法,不需要额外的脚本。
如果您对键连接做了一些花哨的事情(例如 CONCAT(A3, "~", A4)
),您甚至可以在逆轴 table 中提取您的原始位置和区域(例如 REGEXEXTRACT(A1, "(\w+)~")
和 REGEXEXTRACT(A1, "~(\w+)")
).
我在 Google 电子表格中编写了这个简单的通用自定义函数,用于取消透视/反向透视。 注意:这是一个自定义函数,与任何其他内置电子表格函数一样,因此您不必 运行 它,您可以像 unique()
一样使用它或任何其他数组函数。
在您的情况下,只需使用以下代码即可:=unpivot(A1:H6,2,2,"product","MIN","MAX")
这是代码:
/**
* Unpivot a pivot table of any size.
*
* @param {A1:D30} data The pivot table.
* @param {1} fixColumns Number of columns, after which pivoted values begin. Default 1.
* @param {1} fixRows Number of rows (1 or 2), after which pivoted values begin. Default 1.
* @param {"city"} titlePivot The title of horizontal pivot values. Default "column".
* @param {"distance"[,...]} titleValue The title of pivot table values. Default "value".
* @return The unpivoted table
* @customfunction
*/
function unpivot(data,fixColumns,fixRows,titlePivot,titleValue) {
var fixColumns = fixColumns || 1; // how many columns are fixed
var fixRows = fixRows || 1; // how many rows are fixed
var titlePivot = titlePivot || 'column';
var titleValue = titleValue || 'value';
var ret=[],i,j,row,uniqueCols=1;
// we handle only 2 dimension arrays
if (!Array.isArray(data) || data.length < fixRows || !Array.isArray(data[0]) || data[0].length < fixColumns)
throw new Error('no data');
// we handle max 2 fixed rows
if (fixRows > 2)
throw new Error('max 2 fixed rows are allowed');
// fill empty cells in the first row with value set last in previous columns (for 2 fixed rows)
var tmp = '';
for (j=0;j<data[0].length;j++)
if (data[0][j] != '')
tmp = data[0][j];
else
data[0][j] = tmp;
// for 2 fixed rows calculate unique column number
if (fixRows == 2)
{
uniqueCols = 0;
tmp = {};
for (j=fixColumns;j<data[1].length;j++)
if (typeof tmp[ data[1][j] ] == 'undefined')
{
tmp[ data[1][j] ] = 1;
uniqueCols++;
}
}
// return first row: fix column titles + pivoted values column title + values column title(s)
row = [];
for (j=0;j<fixColumns;j++) row.push(fixRows == 2 ? data[0][j]||data[1][j] : data[0][j]); // for 2 fixed rows we try to find the title in row 1 and row 2
for (j=3;j<arguments.length;j++) row.push(arguments[j]);
ret.push(row);
// processing rows (skipping the fixed columns, then dedicating a new row for each pivoted value)
for (i=fixRows;i<data.length && data[i].length > 0 && data[i][0];i++)
{
row = [];
for (j=0;j<fixColumns && j<data[i].length;j++)
row.push(data[i][j]);
for (j=fixColumns;j<data[i].length;j+=uniqueCols)
ret.push(
row.concat([data[0][j]]) // the first row title value
.concat(data[i].slice(j,j+uniqueCols)) // pivoted values
);
}
return ret;
}
在 google sheet 中使用 Flatten 函数,该函数仍未记录。