【问题标题】:Reduce Complexity of This Formula?降低这个公式的复杂性?
【发布时间】:2018-01-23 20:11:38
【问题描述】:

我有一个关于优化我在 Google 表格中使用的公式的问题:

=ARRAYFORMULA(
  IF(
    IFERROR(
      MATCH($B2 & A2, ($B$1:B1) & ($A$1:A2), 0),
      0
    ) = 0,
    1,
    0))

该公式通过计算列 A (ID) 中的所有唯一值(假设它出现在列 B(日期)的日期范围内)来计算列 C(计数)中的输出。

请注意计数值只有 0 和 1,并且仅当 ID 在日期范围内首次出现时才会显示 1。

下面的示例数据。

ID  Date    Count
138 Oct-13  1
138 Oct-13  0
29  Oct-13  1
29  Nov-13  1
138 Nov-13  1
138 Nov-13  0

问题是,一旦我要解析超过 10000 行,公式就会变得缓慢,并且需要一个多小时才能完成计算。我想知道是否有人对如何优化这个公式有建议,所以我不需要让它运行这么长时间。

谢谢,

【问题讨论】:

  • 只是为了澄清一下,这个公式的目的是在 Count 中放入多个 1s 等于唯一(ID,Date)对的数量吗?换句话说,对 Count 列求和会产生唯一(ID、Date)对的数量吗?
  • 抱歉没有早点澄清。它是在日期范围内第一次出现 ID 时放置 1。例如,如果 id 138Oct-13 中出现 4 次,则唯一会出现的 1 出现在当月出现 id 138 的第一行,其余的将返回 0 直到下个月出现。
  • 啊。工作表是否按日期排序?
  • 是的。最旧的日期在顶部降序到最新。
  • 日期相同的行是否按ID排序?如果不是,他们会是吗?

标签: optimization google-sheets array-formulas


【解决方案1】:

我一直在玩一些公式,我认为这个效果更好,但在 10000 行之后仍然变得很慢。

=IF(COUNTIF((FILTER($A$1:$A2, $B$1:$B2 = $B2)),$A2) = 1, 1, 0)

编辑

这是发布在 Google 产品论坛上的附加公式,只需将其放在一个单元格中,然后自动填充。这是迄今为止我找到的最佳答案。

=ArrayFormula(IF(LEN(A2:A),--(MATCH(A2:A&B2:B,A2:A&B2:B,0)=ROW(A2:A)-1),))

【讨论】:

  • 最后一个公式可能会更快地使用过滤器(未选中):=FILTER(--(MATCH(A2:A&B2:B,A2:A&B2:B,0)=ROW(A2:A)-1),len(A2:A))
  • Google 产品论坛帖子的链接会很好,只是为了提供上下文。
  • @MaxMakhrov 我认为您的公式存在解析错误
【解决方案2】:

我无法找到一个纯公式的解决方案,我可以说它优于您现有的解决方案。但是,我确实提出了一个以线性时间运行的自定义函数,因此它应该表现良好。我很想知道它与您的最终解决方案相比如何。

/**
 * Returns 1 for rows in the given range that have not yet occurred in the range,
 * or 0 otherwise.
 *
 * @param {A2:B8} range A range of cells
 * @param {2} key_col Relative position of a column to key by, e.g. the sort
 *   column (optional; may improve performance)
 * @return 1 if the values in the row have not yet occurred in the range;
 *   otherwise 0.
 * @customfunction
 */
function COUNT_FIRST_OF_GROUP(range, key_col) {
  if (!Array.isArray(range)) {
    return 1;
  }

  const grouped = {};

  key_col = typeof key_col === 'undefined' ? 0 : key_col - 1; // convert from 1-based to 0-based

  return range.map(function(rowCells) {
    const group = groupFor_(grouped, rowCells, key_col);
    const rowStr = JSON.stringify(rowCells); // a bit of a hack to identify unique rows, but probably a good compromise
    if (rowStr in group) {
      return 0;
    } else {
      group[rowStr] = true;
      return 1;
    }
  });
}

/** @private */
function groupFor_(grouped, row, key_col) {
  if (key_col < 0) {
    return grouped; // no key column; use one big group for all rows
  }

  const key = JSON.stringify(row[key_col]);
  if (!(key in grouped)) {
    grouped[key] = {};
  }
  return grouped[key];
}

要使用它,请在 Google 表格中转到 工具 > 脚本编辑器...,将其粘贴到编辑器中,然后点击保存。然后,在您的电子表格中,使用如下函数:

=COUNT_FIRST_OF_GROUP(A2:B99, 2)

它将自动填充范围内的所有行。你可以see it in action here

【讨论】:

  • 有效!感谢您发布此附加解决方案。没有ARRAYFORMULA 解决方案那么快……虽然仍然很棒:) 我尝试将execution time 记录下来,但不能。谢谢你的回答。
【解决方案3】:

如果满足某些假设,例如,1. 相同的 ID 号总是一起出现(如果不是,也许你可以先按 ID 对它们进行排序,然后再按日期排序),那么,

=ARRAYFORMULA(1*(A2:A10000&B2:B10000<>A1:A9999&B1:B9999))

如果可以识别日期,我认为您可以使用 + 而不是 &amp; 。同样,这里和那里做了各种假设。

【讨论】:

    猜你喜欢
    • 2021-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多