Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/google-apps-script/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/google-sheets/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Google apps script 你如何创建一个;“反向枢轴”;在谷歌表单中?_Google Apps Script_Google Sheets_Pivot Table_Unpivot - Fatal编程技术网

Google apps script 你如何创建一个;“反向枢轴”;在谷歌表单中?

Google apps script 你如何创建一个;“反向枢轴”;在谷歌表单中?,google-apps-script,google-sheets,pivot-table,unpivot,Google Apps Script,Google Sheets,Pivot Table,Unpivot,我正在尝试生成一个“反向枢轴”函数。我已经为这样一个函数搜索了很长时间,但是找不到已经存在的函数 我有一个包含多达20列和数百行的汇总表,但是我想将其转换为一个平面列表,以便导入数据库(甚至可以使用平面数据从中创建更多透视表!) 因此,我有以下格式的数据: | Customer 1 | Customer 2 | Customer 3 ----------+------------+------------+----------- Product 1 | 1

我正在尝试生成一个“反向枢轴”函数。我已经为这样一个函数搜索了很长时间,但是找不到已经存在的函数

我有一个包含多达20列和数百行的汇总表,但是我想将其转换为一个平面列表,以便导入数据库(甚至可以使用平面数据从中创建更多透视表!)

因此,我有以下格式的数据:

          | Customer 1 | Customer 2 | Customer 3
----------+------------+------------+-----------
Product 1 |          1 |          2 |          3
Product 2 |          4 |          5 |          6
Product 3 |          7 |          8 |          9
 Customer  |  Product  | Qty
-----------+-----------+----
Customer 1 | Product 1 |   1
Customer 1 | Product 2 |   4
Customer 1 | Product 3 |   7
Customer 2 | Product 1 |   2
Customer 2 | Product 2 |   5
Customer 2 | Product 3 |   8
Customer 3 | Product 1 |   3
Customer 3 | Product 2 |   6
Customer 3 | Product 3 |   9
并需要将其转换为以下格式:

          | Customer 1 | Customer 2 | Customer 3
----------+------------+------------+-----------
Product 1 |          1 |          2 |          3
Product 2 |          4 |          5 |          6
Product 3 |          7 |          8 |          9
 Customer  |  Product  | Qty
-----------+-----------+----
Customer 1 | Product 1 |   1
Customer 1 | Product 2 |   4
Customer 1 | Product 3 |   7
Customer 2 | Product 1 |   2
Customer 2 | Product 2 |   5
Customer 2 | Product 3 |   8
Customer 3 | Product 1 |   3
Customer 3 | Product 2 |   6
Customer 3 | Product 3 |   9
我已经创建了一个函数,该函数将从
sheet1
读取范围,并将重新格式化的行附加到同一张表的底部,但是我正在尝试使其工作,以便我可以在
sheet2
上使用该函数,该函数将从
sheet1
读取整个范围

不管我怎么做,我似乎都无法让它发挥作用,我想知道是否有人能给我一些建议

以下是我到目前为止的情况:

函数readRows(){
var sheet=SpreadsheetApp.getActiveSheet();
var rows=sheet.getDataRange();
var numRows=rows.getNumRows();
var values=rows.getValues();
头=值[0]

对于(var i=1;i而言,这基本上是数组操作……下面是一段代码,它执行您想要的操作,并将结果写回现有数据下面

如果您愿意,您当然可以将其修改为写在新的工作表上

function transformData(){
  var sheet = SpreadsheetApp.getActiveSheet();
  var data = sheet.getDataRange().getValues();//read whole sheet
  var output = [];
  var headers = data.shift();// get headers
  var empty = headers.shift();//remove empty cell on the left
  var products = [];
    for(var d in data){
      var p = data[d].shift();//get product names in first column of each row
      products.push(p);//store
    }
  Logger.log('headers = '+headers);
  Logger.log('products = '+products);
  Logger.log('data only ='+data);
  for(var h in headers){
    for(var p in products){  // iterate with 2 loops (headers and products)
      var row = [];
      row.push(headers[h]);
      row.push(products[p]);
      row.push(data[p][h])
      output.push(row);//collect data in separate rows in output array
    }
  }
  Logger.log('output array = '+output);
  sheet.getRange(sheet.getLastRow()+1,1,output.length,output[0].length).setValues(output);
}

要在新工作表中自动写入结果,请使用以下代码替换最后一行代码:

  var ns = SpreadsheetApp.getActive().getSheets().length+1
  SpreadsheetApp.getActiveSpreadsheet().insertSheet('New Sheet'+ns,ns).getRange(1,1,output.length,output[0].length).setValues(output);

如果您的数据有一个唯一的键列,则可能有您需要的

您的取消打印工作表将包含:

  • 键列
    =OFFSET(data!$A$1,INT((ROW()-2)/5)+1,0)
  • 列标题列
    =偏移量(数据!$A$1,0,如果(MOD(ROW()-1,5)=0,5,MOD(ROW()-1,5)))
  • 单元格值列
    =索引(数据!$A$1:$F$100,匹配(A2,数据!$A$1:$A$100,FALSE),匹配(B2,数据!$A$1:$F$1,FALSE))
其中
5
是要取消PIVOT的列数



我没有制作电子表格。我碰巧在搜索过程中遇到了它,这导致了我提出了这个问题。

我编写了一个简单的通用自定义函数,它是100%可重用的您可以取消推送/反向透视任何大小的表

在您的情况下,您可以这样使用它:
=unpivot(A1:D4,1,1,“客户”,“销售”)

所以,您可以像使用电子表格中的任何内置数组函数一样使用它

请参见以下两个示例:

资料来源如下:

/**
 * 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; i++)
  {
    // skip totally empty or only whitespace containing rows
    if (data[i].join('').replace(/\s+/g,'').length == 0 ) continue;

    // unpivot the row
    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;
}
/**
*取消pivot任何大小的透视表。
*
*@param{A1:D30}数据透视表。
*@param{1}fixColumns数据透视值开始的列数。默认值为1。
*@param{1}fixRows数据透视值开始的行数(1或2)。默认值为1。
*@param{“city”}titlePivot水平轴值的标题。默认为“column”。
*@param{“distance”[,…]}titleValue数据透视表值的标题。默认值为“value”。
*@返回未插入的表
*@customfunction
*/
函数unpivot(数据、fixColumns、fixRows、titlePivot、titleValue){
var fixColumns=fixColumns | | 1;//固定了多少列
var fixRows=fixRows | | 1;//固定了多少行
var titlePivot=titlePivot | | |'列';
var titleValue=titleValue | |“值”;
var ret=[],i,j,行,uniqueCols=1;
//我们只处理二维数组
if(!Array.isArray(data)| | data.length2)
抛出新错误(“最多允许2个固定行”);
//使用前几列中最后设置的值填充第一行中的空单元格(对于2个固定行)
var tmp='';
对于(j=0;j
=ARRAYFORMULA({“客户”、“产品”、“数量”);
例如:转置,转置,转置(
如果(B2:Z),B1:1和♠"&A2:A&“♠“&B2:Z&”♦", )), 999^99)), 999^99)), "♦")), "♠")), 
“其中Col1”“按Col1排序”)})

我认为你没有足够的数组公式答案,所以这里是另一个

试验数据(表1)

客户配方

=ArrayFormula(hlookup(int((row(indirect("1:"&Tuples))-1)/Rows)+2,{COLUMN(Sheet1!$1:$1);Sheet1!$1:$1},2))
(使用一点数学使其重复,并使用hlookup在列标题中找到正确的列)

产品配方

=ArrayFormula(vlookup(mod(row(indirect("1:"&Tuples))-1,Rows)+2,{row(Sheet1!$A:$A),Sheet1!$A:$A},2))
(使用mod和vlookup在行标题中查找正确行的类似方法)

数量公式

=ArrayFormula(vlookup(mod(row(indirect("1:"&Tuples))-1,Rows)+2,{row(Sheet1!$A:$A),Sheet1!$A:$Z},int((row(indirect("1:"&Tuples))-1)/Rows)+3))
(上述方法的扩展,用于在二维数组中同时查找行和列)

然后将这三个公式组合到一个查询中,以过滤掉数量的任何空白值

=ArrayFormula(query(
   {hlookup(int((row(indirect("1:"&Tuples))-1)/Rows)+2, {COLUMN(Sheet1!$1:$1);Sheet1!$1:$1},2),
    vlookup(mod(row(indirect("1:"&Tuples))-1,Rows)+2,{row(Sheet1!$A:$A),Sheet1!$A:$A},2),
    vlookup(mod(row(indirect("1:"&Tuples))-1,Rows)+2,{row(Sheet1!$A:$A),Sheet1!$A:$Z},int((row(indirect("1:"&Tuples))-1)/Rows)+3)},
"select * where Col3 is not null"))

注意

命名范围行和列是从数据的第一列和第一行使用counta获得的,元组是它们的乘积

=counta(Sheet1!A:A)

=counta(Sheet1!1:1)

如果需要,可以包含在主公式中,但会损失一些可读性


以下是适用于当前情况的“标准”拆分/联接解决方案(具有50K数据限制),以供参考:

=ArrayFormula(split(transpose(split(textjoin("♫",true,transpose(if(Sheet1!B2:Z="","",Sheet1!B1:1&"♪"&Sheet1!A2:A&"♪"&Sheet1!B2:Z))),"♫")),"♪"))
这也相当慢(处理2401个数组元素)。如果将计算限制在数据的实际维度上,则对于小型数据集,计算速度要快得多:

=ArrayFormula(split(transpose(split(textjoin("♫",true,transpose(if(Sheet1!B2:index(Sheet1!B2:Z,counta(Sheet1!A:A),counta(Sheet1!1:1))="","",Sheet1!B1:index(Sheet1!B1:1,counta(Sheet1!1:1))&"♪"&Sheet1!A2:index(Sheet1!A2:A,counta(Sheet1!A:A))&"♪"&Sheet1!B2:index(Sheet1!B2:Z,counta(Sheet1!A:A),counta(Sheet1!1:1))))),"♫")),"♪"))
更新:在V8发动机上使用:

/**
 * Unpivots the given data
 *
 * @return Unpivoted data from array
 * @param {object[][]} arr 2D Input Array
 * @param {object[][]=} headers [optional] Custom headers for output
 * @customfunction
 */
function unpivotFast(arr, headers) {
  const custHeader = arr.shift();
  custHeader.shift();
  const out = arr.flatMap(([prod, ...qty]) =>
    qty.map((num, i) => [custHeader[i], prod, num])
  );
  if (headers) out.unshift(headers[0]);
  return out;
}
用法:

=UNPIVOTFAST(A1:F4,{A1,"Month","Sales"})
=UNPIVOT(A1:F4,1,{A1,"Month","Sales"})//Outputs 1 static and 2 unpivoted columns from 1 static and 4+ pivoted columns 

使用and的数组操作-最低限度方法:

/**
 * Unpivots the given data
 *
 * @deprecated
 * @return Unpivoted data from array
 * @param {A1:F4} arr 2D Input Array
 * @param {3} numCol Number of static columns on the left
 * @param {A1:C1} headers [optional] Custom headers for output
 * @customfunction
 */
function unpivot(arr, numCol, headers) {
  var out = arr.reduce(function(acc, row) {
    var left = row.splice(0, numCol); //static columns on left
    row.forEach(function(col, i) {
      acc.push(left.concat([acc[0][i + numCol], col])); //concat left and unpivoted right and push as new array to accumulator
    });
    return acc;
  }, arr.splice(0, 1));//headers in arr as initial value
  headers ? out.splice(0, 1, headers[0]) : null; //use custom headers, if present.
  return out;
}
用法:

=UNPIVOTFAST(A1:F4,{A1,"Month","Sales"})
=UNPIVOT(A1:F4,1,{A1,"Month","Sales"})//Outputs 1 static and 2 unpivoted columns from 1 static and 4+ pivoted columns 

这里还有另一种选择:

=arrayformula
(
   { "PRODUCT","CUSTOMER","QTY";
     split 
     ( transpose ( split 
                   ( textjoin("✫" ,false,filter(Sheet2!A2:A,Sheet2!A2:A<>"") & "✤" &
                              filter(Sheet2!B1:1,Sheet2!B1:1<>""))
                     ,"✫",true,false)),"✤",true,false
     ),
     transpose ( split ( textjoin ( "✤", false, transpose ( filter 
     ( 
       indirect( "Sheet2!B2:"  & MID(address(1,COUNTA( Sheet2!B1:1)+1), 2,
                                     FIND("$",address(1,COUNTA( Sheet2!B1:1)+1),2)-2)
               )   
       , Sheet2!A2:A<>""
       ))),"✤",true,false)
     )
   }
 )
=数组公式
(
{“产品”、“客户”、“数量”;
分裂
(转置(拆分)
(textjoin)✫,false,筛选器(Sheet2!A2:A,Sheet2!A2:A“”)✤" &
过滤器(活页2!B1:1,活页2!B1:1“”)
,"✫“,对,错”),”✤“,对,错
),
转置(拆分(textjoin)(“✤,false,转置(过滤器
( 
间接(“表2!B2:”&MID(地址(1,COUNTA)(表2!B1:1)+1),2,
查找(“$”,地址(1,COUNTA(Sheet2!B1:1)+1),2)-2)
)   
s
function PVT() {
  var ss=SpreadsheetApp.getActive();
  var sh2=ss.getSheetByName('Sheet2');
  var sh3=ss.getSheetByName('Sheet3');
  sh3.clearContents();
  var vA=sh2.getRange(2,1,sh2.getLastRow()-1,sh2.getLastColumn()).getValues();
  pObj={};
  vA.forEach(function(r,i){if(!pObj.hasOwnProperty(r[1])){pObj[r[1]]={};}if(!pObj[r[1]].hasOwnProperty(r[0])){pObj[r[1]][r[0]]=r[2];}else{pObj[r[1]][r[0]]+=r[2];}});
  var oA=[];
  var ikeys=Object.keys(pObj);
  var jkeys=Object.keys(pObj[ikeys[0]]);
  var hkeys=jkeys.slice();
  hkeys.unshift(''); 
  oA.push(hkeys);
  ikeys.forEach(function(ik,i){var row=[];row.push(ik);jkeys.forEach(function(jk,j){row.push(pObj[ik][jk]);});oA.push(row);});
  sh3.getRange(1,1,oA.length,oA[0].length).setValues(oA);
}