SUBTOTAL と AGGREGATE の違い — 「見えている行だけ」を集計する

· unvell team
SUBTOTAL と AGGREGATE の違い — 「見えている行だけ」を集計する

一覧にフィルターを掛けて商品を 1 つだけに絞り、合計を見る。数字が変わっていない。 SUM はフィルターの存在を知らないので、範囲の中の行を、見えていようがいまいが全部足します。画面に出ている行と合計が食い違ったまま、その画面はスクリーンショットで報告書に貼られます。

Excel の答えが SUBTOTAL、その後から出てきた上位版が AGGREGATE です。この 2 つだけが「そのセルがなぜそこに無いのか」を見ています。フィルターで消えたのか、手で隠したのか、エラー値なのか、それとも既に数えた小計なのか。

ReoGrid V5 はこの 2 つに対応しました。この記事では両者の違いを整理し、C# アプリの中で動かすところまで——フィルター、アウトライン、そして引っかかりやすいヘッドレスの場合まで見ていきます。

この記事は表計算の関数シリーズの一部です。「値を引く」 VLOOKUP / HLOOKUP / XLOOKUP、「位置を返す」 MATCH / XMATCH、「条件で絞って集計する」 SUMIF / COUNTIF と合わせてどうぞ。


まず結論

SUM などSUBTOTALAGGREGATE
フィルターで隠れた行数える常に除外常に除外
手で隠した行数える101〜111 のみ除外奇数オプションで除外
範囲内のエラー値結果もエラー結果もエラー無視できる(2/3/6/7)
入れ子の小計セル数える常に無視オプション 0〜3 で無視
使える集計自分自身11 種(AVERAGEVARP19 種MEDIAN LARGE PERCENTILE ほか)
引数SUM(範囲…)SUBTOTAL(集計方法, 範囲…)AGGREGATE(集計方法, オプション, 範囲…)

要点は 2 つです。

  1. SUBTOTAL は「見えているものの合計」。 フィルターで消えた行は数えず、番号に 100 を足すと手で隠した行も数えなくなる
  2. AGGREGATESUBTOTAL +「エラーを飛ばす」+ 8 種類の関数追加。 第 2 引数のために存在するようなものです

SUBTOTAL — 見えている行の集計

SUBTOTAL(集計方法, 範囲1, [範囲2], …)。第 1 引数でどの集計を行うかを選びます。

集計方法
1 AVERAGE2 COUNT3 COUNTA4 MAX5 MIN
6 PRODUCT7 STDEV8 STDEVP9 SUM10 VAR
11 VARP

実務で書くのはほぼ SUBTOTAL(9, …)、つまり合計です。

1〜11 と 101〜111 — たいてい逆に覚えられている

同じ 11 個の関数が 111101111 の 2 組あります。「100 番台は非表示行を無視する」と説明されることが多く、そこから「SUBTOTAL(9, …) はフィルターで消えた行も数える」と思われがちですが、そうではありません。

フィルターで消えた行はどちらの番号でも除外されます。 100 番台が追加でやることは 1 つだけ——ユーザーが手で隠した行も除外する、です。

フィルターで隠れた行手で隠した行
SUBTOTAL(9, …)除外数える
SUBTOTAL(109, …)除外除外

つまり選択の幅は狭く、判断基準は意図です。9 は「今のフィルター条件での合計」——行を手で隠すのは表示上の都合であって、数字を動かすべきではない、という立場。109 は「経緯はどうあれ画面に出ている行の合計」で、アウトラインを畳んで使うときに欲しいのはこちらです。

列は最初から関係ありません。列を隠しても何も除外されません。ReoGrid でも Excel でも、対象は行だけです。


総計が小計を二重に数えない理由

グループごとに小計を並べ、いちばん下に総計を置く。素直に SUM で書くと、値は「自分自身」と「所属グループの小計」の 2 回数えられて倍になります。

SUBTOTAL はこれを自分で処理します。範囲の中にある SUBTOTAL / AGGREGATE のセルは無視されるからです。

A1  1
A2  2
A3  =SUBTOTAL(9,A1:A2)     → 3
A4  4
A5  5
A6  =SUBTOTAL(9,A4:A5)     → 9
A7  =SUBTOTAL(9,A1:A6)     → 12   ← 24 にはならない
A8  =SUM(A1:A6)            → 24   ← 素の SUM は全部見る

小計が式の一部に埋まっていても同じです。=SUBTOTAL(9,A1:A2)*2 のセルも小計セルとして扱われ、やはり無視されます。判定されるのはセルであって、式の形ではありません。


AGGREGATE — 同じ仕事+エラーの扱い

AGGREGATE(集計方法, オプション, 範囲1, [範囲2], …)。順位系の関数では AGGREGATE(集計方法, オプション, 配列, k) の形を取ります。

使える関数は 11 種ではなく 19 種です。

集計方法
1 AVERAGE2 COUNT3 COUNTA4 MAX5 MIN
6 PRODUCT7 STDEV8 STDEVP9 SUM10 VAR
11 VARP12 MEDIAN13 MODE.SNGL14 LARGE15 SMALL
16 PERCENTILE.INC17 QUARTILE.INC18 PERCENTILE.EXC19 QUARTILE.EXC

1419 は末尾に k が要ります。AGGREGATE(14, 6, A1:A100, 2) で「エラーを無視して 2 番目に大きい値」です。

オプションは真理値表

第 2 引数はモード選択ではなく、独立した 3 つのスイッチを 1 つの数字に詰めたものです。

オプション入れ子の小計非表示行エラー値
0(既定)無視数える数える
1無視無視数える
2無視数える無視
3無視無視無視
4数える数える数える
5数える無視数える
6数える数える無視
7数える無視無視

ビットとして読めば暗記は要りません。0〜3 は入れ子の小計を無視、奇数は手で隠した行を無視、2/3/6/7 はエラーを無視。フィルターで消えた行はこの表の外です——どのオプションでも除外されます。SUBTOTAL と同じです。

使いたくなる理由

列のどこか 1 セルに #DIV/0! があると、その列の SUM#DIV/0! になります。正しい挙動ですが、取り込んだ 1000 行のうち 3 行が数量ゼロで割っていただけ、という場面では役に立ちません。

=SUM(D2:D1000)              → #DIV/0!
=SUBTOTAL(9,D2:D1000)       → #DIV/0!   (こちらもエラーを伝播する)
=AGGREGATE(9,6,D2:D1000)    → 計算できた行の合計

代替案は全セルを IFERROR で包むことですが、3 行のために 1000 個の数式を書き換えることになります。AGGREGATE は、その判断を本当に必要としている 1 つのセルに寄せられます。


使い分け

  • フィルター下の合計SUBTOTAL(9, …)。短く、表計算に慣れた人がそこにあると期待する式でもあります
  • アウトラインを畳んだ状態の合計SUBTOTAL(109, …)
  • 範囲にエラーが混ざっていて飛ばしたいAGGREGATE(9, 6, …)
  • 絞り込んだデータの中央値・3 番目に大きい値・四分位数AGGREGATESUBTOTAL は 11 種で止まります
  • 常に全部数えたい → 素の SUMAGGREGATE(9, 4, …) でも同じ結果になりますが、次に読む人には意図が逆に伝わります

C# アプリの中で動かす — ReoGrid V5

どちらもコア側の実装なので、以下のコードは WinForms / WPF / Avalonia で同一、UI を持たないヘッドレスでもそのまま動きます。

using unvell.ReoGrid.Core;
using unvell.ReoGrid.Core.Filtering;

var workbook = new Workbook();
var sheet = workbook.AddWorksheet("Orders");

sheet.SetText(0, 0, "商品");    sheet.SetText(0, 1, "数量");
sheet.SetText(1, 0, "りんご");  sheet.SetNumber(1, 1, 1);
sheet.SetText(2, 0, "バナナ");  sheet.SetNumber(2, 1, 2);
sheet.SetText(3, 0, "りんご");  sheet.SetNumber(3, 1, 3);
sheet.SetText(4, 0, "さくらんぼ"); sheet.SetNumber(4, 1, 4);

sheet.SetFormula(5, 1, "SUBTOTAL(9,B2:B5)");   // 見えている行の合計
sheet.SetFormula(6, 1, "SUM(B2:B5)");          // 比較用

// 「りんご」で絞り込む
var filter = sheet.CreateAutoFilter(RangePosition.Parse("A1:B5"));
filter.SetColumnFilter(0, ["りんご"]);
filter.Apply();

double visible = sheet.GetValue(5, 1).AsNumber;   // 4  (1 + 3)
double all     = sheet.GetValue(6, 1).AsNumber;   // 10

filter.ClearAll();                                // B6 は 10 に戻る

SetFormula に渡す式に先頭の = は付けません"SUBTOTAL(9,B2:B5)" であって "=SUBTOTAL(...)" ではない、ということです。= は数式バーの入力記号であって式の一部ではありません。

フィルター適用中。ヘッダにボタンが出て、条件に合わない行が隠れている

Apply() は集計式の再計算まで面倒を見ます。ClearAll()、アウトラインの開閉、Undo も同様です。

覚えておく 1 つの呼び出し — SyncAggregateFormulas()

行の表示状態はセルの編集ではないので、依存グラフには乗りません。行を隠す操作は、どこから見ても「値が変わった」ようには見えないのです。画面のあるアプリではこれは見えません——手で隠した行は次の再描画で反映されます。ヘッドレスには再描画が無いので、自分で伝えます。

sheet.SetFormula(6, 1, "SUBTOTAL(109,B2:B5)");   // 100 番台

sheet.Rows.SetHidden(2, true);      // 「バナナ」を手で隠す
sheet.SyncAggregateFormulas();      // 再描画は来ない — 明示的に更新する

double onScreen = sheet.GetValue(6, 1).AsNumber;   // 8  (10 − 2)

表示状態が実際に動いていなければ何もしないので、まとめて隠したあとに 1 回呼ぶ、で十分です。

アウトラインと組み合わせる

var group = sheet.GroupRows(1, 4);       // データ行 4 行をグループ化
sheet.RowOutlines.Collapse(group);

sheet.GetValue(5, 1).AsNumber;           // 10 — SUBTOTAL(9) は隠れた行も数える
sheet.GetValue(6, 1).AsNumber;           //  4 — SUBTOTAL(109) は残っている行だけ

行グループが 2 つ、上のグループが畳まれている

109 が存在する理由がこれで、2 つの番号帯が交換可能ではないことがいちばん分かりやすく出る場面でもあります。

シートをまたぐ

SUBTOTAL は別シートを参照でき、そのとき見るのは参照先シートの非表示行です。

var summary = workbook.AddWorksheet("Summary");
summary.SetFormula(0, 0, "SUBTOTAL(109,Orders!B2:B5)");

sheet.Rows.SetHidden(2, true);
summary.SyncAggregateFormulas();         // 更新するのは「読む側」のシート

呼び出す相手に注意してください。式があるのは Summary なので、更新が要るのも Summary です。


Excel 互換ゆえの細かい挙動

  • MAX / MIN は対象が無いと 0 を返します(エラーではありません)。Excel と同じです。AVERAGE#DIV/0!
  • MODE.SNGL は重複が 1 つも無いと #N/A 同数のときは先に現れた値を返します
  • PERCENTILE.EXCQUARTILE.EXC は構造上、両端に届きません。QUARTILE.EXC(…, 0) は最小値ではなく #NUM! です
  • 表に無い集計方法や 07 の外のオプションは #VALUE!、データの範囲を超えた k#NUM!
  • リテラル引数も使えます(SUBTOTAL(9, 1, 2, 3)6)。型変換は SUM と同じ規則です
  • SUBTOTAL にエラーを無視する手段はありません。 必要なら答えは AGGREGATE であって、大きい番号ではありません

V4 について

この 2 つは V5 のみです。ReoGrid V4 は 4.5 / 4.6 を含めて SUBTOTAL / AGGREGATE を持っていません。使っているブックは読み込めますが、V4 の評価器は知らない関数に対して何も返さないため、そのセルはエラーではなく空欄になります。#NAME? と違い、静かに空欄になった合計はレビューで見落とされます。V4 のアプリで「一覧を絞り込んで下の合計を見る」使われ方をしているなら、移行を検討する理由になります。


まとめ

  • SUM はフィルターを知りません。 絞り込んだ一覧の下に置く合計は SUBTOTALAGGREGATE にする
  • フィルターで隠れた行はどちらでも除外され、AGGREGATE はどのオプションでも除外します。101111 が足すのは手で隠した行だけ
  • 小計セルは自動的に無視されるので、列全体を範囲にした総計がそのまま正しくなります。小計行を手で除く必要はありません
  • AGGREGATE の第 2 引数は 3 つのスイッチ(入れ子の小計・非表示行・エラー)。**AGGREGATE(9, 6, …) が、いつも欲しくなる「計算できた行の合計」**です
  • MEDIAN MODE.SNGL LARGE SMALL PERCENTILE QUARTILE も使えます。SUBTOTAL には無かった統計関数です
  • ReoGrid V5 なら WinForms / WPF / Avalonia アプリの中でも、ヘッドレスでも同じ式が動きます。自分のコードで行を隠したときは SyncAggregateFormulas() を呼んでください

V5 の新機能を見る / 30 日間の評価版を試す


関連記事

ご自身のプロジェクトで ReoGrid を試す

.NET WinForms / WPF 向けの Excel 互換スプレッドシートコンポーネント。30 日間の無償トライアルをご利用いただけます。

ニュースレター

最新リリースをメールでお届け

ReoGrid の新バージョン・新機能・技術記事のお知らせをお送りします。配信停止はいつでも可能です。

関連する記事

壊れない入力シートを作る — ReoGrid V5 の入力規則・セルのメモ・シート保護

アプリの画面に表を出した瞬間、ユーザーは数式のセルを消し、単価に「12,000円」と打ち込みます。ReoGrid V5 で追加された「データの入力規則」「セルのメモ」「シート保護」の 3 つは、役割がそれぞれ違い、組み合わせて初めて壊れない入力シートになります。C# のコードで見積テンプレートを 1 枚組み立てながら、3 つの使い分けと、Excel 互換ゆえの落とし穴(貼り付けは検査されない・パスワードは暗号ではない・選択系フラグだけ既定が逆)まで整理します。

IF のネストが 3 段を超えたら IFS — IF / AND / OR / IFERROR で条件分岐を整理する

合否判定、ランク分け、達成率の評価——業務の表は条件分岐だらけ。定番の IF はネストが深くなると括弧の対応が追えなくなる。ネストを平らにする IFS、条件を束ねる AND / OR、エラーを既定値に丸める IFERROR の使い分けを例で整理し、ReoGrid(IFS / IFERROR は V4.5 で対応)で WinForms / WPF アプリに同じ数式を載せる方法まで解説する。

「氏名を姓と名に分ける」「住所から都道府県を取り出す」— LEFT / MID / FIND / SUBSTITUTE で文字列を数式で加工する

名簿の氏名を姓と名に分割する、住所から都道府県だけ取り出す、商品コードをハイフンで分解する——業務データの文字列加工は C# なら Split 一発だが、「ユーザーが画面上で確認・修正できる形」にしたいならスプレッドシートの数式が向いている。LEFT・MID・FIND・SUBSTITUTE・TEXTJOIN の定番レシピと落とし穴(全角スペース、神奈川県・和歌山県・鹿児島県の 4 文字問題)を整理し、ReoGrid(V4.5 で対応)で WinForms / WPF アプリに同じ数式を載せる方法まで解説する。