运行 Google 个工作表的最大 ArrayFormula

Running Max ArrayFormula for Google Sheets

我有一个手动输入的号码列表

1
100
100
10
1
1000
10
1000
1
1000
100
10

我想得到 运行 max - 初始数字数组的每个子数组的最大值。我所说的子数组是指从 [A1][A2]、从 [A1][A3]、从 [A1][A4] 等的数字。 结果如下:

1
100
100
100
100
1000
1000
1000
1000
1000
1000
1000

可以将更多条目添加到初始号码列表中。

赏金

100 点赏金给了

这是 100500 行的速度测试:

自定义公式示例:

=INDEX(IF(A3:A="","",
  runningTotal(A3:A,4)))

sample file

source code

related

代码:

/**
 * Get running total for the array of numbers
 * by makhrov.max@gmail.com
 * 
 * @param {array} numbers The array of numbers
 * @param {number} total_types (1-dafault) sum, (2) avg, (3) min, (4) max, (5) count;
 *                  1-d array or number
 * @param {number} limit number of last values to count next time. 
 *                 Set to 0 (defualt) to take all values
 * @param {array} keys (optional) array of keys. Function will group result by keys
 * @return The hex-code of cell background & font color
 * @customfunction
 */
function runningTotal(numbers, total_types, limit, keys) { 
  
  // possible types to return
  var oTypes = {
    '1': 'sum',
    '2': 'avg',
    '3': 'min',
    '4': 'max',
    '5': 'count'
  }
  // checks and defaults
  var errPre = ' ';
  if( typeof numbers != "object" ) {
    numbers = [ [numbers] ];
  }
  total_types = total_types || [1];
  if( typeof total_types != "object" ) {
    total_types = [ total_types ];
  }
  if( keys && typeof keys != "object" ) {
    keys = [ [keys] ];
  }
  if (keys) {
    if (numbers.length !== keys.length) {
      throw errPre + 'Numbers(' + 
        numbers.length + 
        ') and keys(' + 
        keys.length + 
        ') are of different length'; }
  }
  // assign types
  var types = [], type, k;
  for (var i = 0; i < total_types.length; i++) {
    k = '' + total_types[i];
    type = oTypes[k];
    if (!type) {
      throw errPre + 'Unknown total_type = ' + k;
    }
    types.push(type);
  }
  limit = limit || 0;
  if (isNaN(limit)) {
    throw errPre + '`limit` is not a Number!';
  }
  limit = parseInt(limit);

  // calculating running totals
  var result = [], 
    subres = [], 
    nodes = {}, 
    key = '-', 
    val;
  var defaultNode_ = {
      values: [],
      count: 0,
      sum: 0,
      max: null,
      min: null,
      avg: null,
      maxA: Number.MIN_VALUE,
      maxB: Number.MIN_VALUE,
      maxC: Number.MIN_VALUE,
      minA: Number.MAX_VALUE,
      minB: Number.MAX_VALUE,
      minC: Number.MAX_VALUE
    };
  for (var i = 0; i < numbers.length; i++) {
    val = numbers[i][0];
    // find correct node
    if (keys) { key = keys[i][0]; }
    node = nodes[key] || 
      JSON.parse(JSON.stringify(defaultNode_));
    /**
     * For findig running Max/Min
     * sourse of algorithm
     * https://www.geeksforgeeks.org
     * /sliding-window-maximum-maximum-of-all-subarrays-of-size-k/
     */
    // max
    //reset first second and third largest elements
    //in response to new incoming elements
    if (node.maxA<val) {
      node.maxC = node.maxB;
      node.maxB = node.maxA;
      node.maxA = val;
    } else if (node.maxB<val) {
      node.maxC = node.maxB;
      node.maxB = val;
    } else if (node.maxC<val) {
      node.maxC = val;
    }
    // min
    if (node.minA>val) {
      node.minC = node.minB;
      node.minB = node.minA;
      node.minA = val;
    } else if (node.minB>val) {
      node.minC = node.minB;
      node.minB = val;
    } else if (node.minC>val) {
      node.minC = val;
    }

    // if limit exceeds
    if (limit !== 0 && node.count === limit) {
      //if the first biggest we earlier found
      //is matching from the element that
      //needs to be removed from the subarray
      if(node.values[0]==node.maxA) {
        //reset first biggest to second and second to third
        node.maxA = node.maxB;
        node.maxB = node.maxC;
        node.maxC = Number.MIN_VALUE;
        if (val <= node.maxB) {
          node.maxC = val;
        }
      } else if (node.values[0]==node.maxB) {
        node.maxB = node.maxC;
        node.maxC = Number.MIN_VALUE;
        if (val <= node.maxB) {
          node.maxC = val;
        }
      } else if (node.values[0]==node.maxC) {
        node.maxC = Number.MIN_VALUE;
        if (val <= node.maxB) {
          node.maxC = val;
        }
      } else if(node.values[0]==node.minA) {
        //reset first smallest to second and second to third
        node.minA = node.minB;
        node.minB = node.minC;
        node.minC = Number.MAX_VALUE;
        if (val > node.minB) {
          node.minC = val;
        }
      }
      if (node.values[0]==node.minB) {
        node.minB = node.minC;
        node.minC = Number.MAX_VALUE;
        if (val > node.minB) {
          node.minC = val;
        }
      } 
      if (node.values[0]==node.minC) {
        node.minC = Number.MAX_VALUE;
        if (val > node.minB) {
          node.minC = val;
        }
      }
      // sum
      node.sum -= node.values[0];
      // delete first value
      node.values.shift();
      // start new counter
      node.count = limit-1; 
    }
    // add new values
    node.count++;
    node.values.push(val);
    node.sum += val;
    node.avg = node.sum/node.count;
    node.max = node.maxA;
    node.min = node.minA;
    // remember entered values for the next loop
    nodes[key] = node;

    // get the result depending on 
    // selected total_types
    subres = [];
    for (var t = 0; t < types.length; t++) {
      subres.push(node[types[t]]);
    }
    result.push(subres);
  }
  // console.log(JSON.stringify(nodes, null, 4));
  return result;
}

其他解决方案:

怎么样:

=INDEX(VLOOKUP(ROW(A:A), FILTER(
 SORTN({ROW(A:A), A:A}, 9^9, 2, 2, 1), IFNA(SORTN(ROW(A:A), 9^9, 2, A:A, 1)<
 QUERY(SORTN(ROW(A:A), 9^9, 2, A:A, 1), "offset 1", ), 1)), 2, 1))

这行不通...请参阅下面的更新


更新:

=INDEX(VLOOKUP(ROW(A:A), FILTER(SORT(SORTN({ROW(A:A), A:A}, 9^9, 2, 2, 1)), 
 COUNTIFS(UNIQUE(A:A), ">"&UNIQUE(A:A), 
 SEQUENCE(COUNTUNIQUE(A:A)+1), "<="&SEQUENCE(COUNTUNIQUE(A:A)+1))=0), 2, 1))

并考虑空行:

=INDEX(IF(A:A="",,VLOOKUP(ROW(A:A), 
 FILTER(SORT(SORTN(FILTER({ROW(A:A), A:A}, A:A<>""), 9^9, 2, 2, 1)), 
 COUNTIFS(UNIQUE(FILTER(A:A, A:A<>"")), ">"&UNIQUE(FILTER(A:A, A:A<>"")), 
 SEQUENCE(COUNTUNIQUE(A:A)), "<="&SEQUENCE(COUNTUNIQUE(A:A)))=0), 2, 1)))

=INDEX(QUERY(SPLIT(FLATTEN(ROW(A1:A12)&"×"&
 IF(ROW(A1:A12)>=TRANSPOSE(ROW(A1:A12)), TRANSPOSE(A1:A12), )), "×"), 
 "select max(Col2) group by Col1 label max(Col2)''"))

但要保持快速:

=INDEX(QUERY(SPLIT(FLATTEN(
 SEQUENCE(MATCH(9, 1/(A:A<>"")))&"×"&IF(
 SEQUENCE(MATCH(9, 1/(A:A<>"")))>=
 SEQUENCE(1, MATCH(9, 1/(A:A<>""))), TRANSPOSE(
 INDIRECT("A1:A"&MATCH(9, 1/(A:A<>"")))), )), "×"), 
 "select max(Col2) group by Col1 label max(Col2)''"))

并考虑空行:

=INDEX(IF(A:A="",, QUERY(SPLIT(FLATTEN(
 SEQUENCE(MATCH(9, 1/(A:A<>"")))&"×"&IF(
 SEQUENCE(MATCH(9, 1/(A:A<>"")))>=
 SEQUENCE(1, MATCH(9, 1/(A:A<>""))), TRANSPOSE(
 INDIRECT("A1:A"&MATCH(9, 1/(A:A<>"")))), )), "×"), 
 "select max(Col2) group by Col1 label max(Col2)''")))

是的,它适用于 MINSUMAVGCOUNT

ofc COUNT 太过分了

我可以把这个放进运行,@Max吗?

=ArrayFormula(if(A:A="",,vlookup(row(A:A),
{if(countifs(A1:A,">"&A:A,row(A:A),"<"&row(A:A))=0,row(A:A)),A:A}
,2)))

如果中间有空白单元格,您可以使用它:

 =ArrayFormula(if(A:A="",,vlookup(row(A:A),
{if((countifs(A1:A,">"&A:A,row(A:A),"<"&row(A:A))=0)*(A:A<>""),row(A:A)),A:A}
,2)))

原公式(如果非空格之间没有空格)可以简化为

=ArrayFormula(vlookup(row(A:A),
{if(countifs(A1:A,">"&A:A,row(A:A),"<"&row(A:A))=0,row(A:A)),A:A}
,2))