在 Google 表格中逆透视 - 多 header 列和行

Unpivot in Google Sheets - multipe header columns and rows

我有一个摘要 table,大约有 20 列和最多一百行,但是我想将它转换成一个平面列表,以便我可以导入到数据库中。

This solution 在我的情况下无法正常工作,我对 JS 的了解远远不足以正确调整它。

Example sheet 中有三个选项卡:

  1. 源数据 - 我当前拥有的虚拟数据
  2. 期望的结果 - 我想将源数据转换成什么
  3. 我得到的结果 - 使用上述 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")

您可以在此处找到示例:https://docs.google.com/spreadsheets/d/12TBoX2UI_Yu2MA2ZN3p9f-cZsySE4et1slwpgjZbSzw/edit#gid=422214765

这是代码:

/**
 * 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 函数,该函数仍未记录。