フィルタとSUBTOTAL関数を使って1つの色ごとにカウントする方法と、カスタム関数(GAS)を使って一括でカウントする方法を紹介します。
フィルタとSUBTOTAL関数を使う方法
やりたいことの確認です。
データ中のC列に塗りつぶし色が設定されています。この色でデータを絞り込み、C14セルに件数を表示させるものとします。
絞り込みにフィルタを使うので出力セルをデータの横ではなく下方としています。
まずC14セルに次の数式を入力します。
C14セル
=SUBTOTAL(3,C3:C10)
SUBTOTAL関数は指定した範囲の集計値を返す関数です。何を集計するかは第1引数で指定でき、「3」とすることで値の数をカウントできます(COUNTA関数に相当)。フィルタにより除外された行を集計から除くことができるのが特徴です。
ここでは第2引数(対象範囲)に見出しを含めないことと、対象をC列だけにすることが注意点です。B列を含めるとB列の値までカウントしてしまいます。
確定すると、表示されているすべての件数「8」が表示されます。
「件数」と表現していますSUBTOTAL関数がカウントしているのはあくまで値なので、セルに値が入ってないとカウントされません(この点については後でまた触れます)。
続いて表全体(見出しを含むB2:C10の範囲)を選択し、右クリックして「フィルタを作成」を選択します。
見出しにフィルタのアイコンが表示されますのでC列のアイコンをクリックし、「色でフィルタ」→「塗りつぶしの色」と進んでカウント対象とする色を選択します。
選択した色の行が抽出されるとともに、C14セルの値が「4」となりました。
あとは対象とする塗りつぶし色を選び直すことで色ごとに集計することができます。
ここで注意点です。
上でも述べたように、SUBTOTAL関数がカウントするのは抽出された値の数なので、フィルタで抽出できても値がないとカウントされません。
全てのカウント値を合計してもデータの全件数に満たない場合はこれが原因であると考えられます。
この例の場合は値が入っているB列(B3:B10)を第2引数にすればカウントできます。
ちなみに「=SUBTOTAL(3,ARRAYFORMULA(C3:C10&"x"))」のような式でムリヤリ値をくっつけてカウントしようとしても、これはエラーになります。
カスタム関数(GAS)を使う方法
GAS(スクリプト)を使って関数を定義し、塗りつぶし色ごとにセルをカウントする例です。
入力値の有無にかかわらずセルの色を直接カウントできるのがメリットですが、準備が必要となる上に通常の関数とは使い勝手が異なりますので、注意点をご覧のうえお試しください。
16進数カラーコードを抽出して集計する例
先に実行例を示します。
G3セルにカスタム関数「countColors」を入力し、B2:E5の範囲の各セルについて背景色のカラーコード(#a2c4c9のような値)を抽出してセル数を集計した結果を出力させています。
対象範囲をダブルクォーテーション("")で囲んで指定するのが注意点です(注意点については改めて触れます)。
各カラーコードがどの色かわかりにくいのが難点ですが、カスタム関数から色塗りをするのはハードルが高いのでここからは手作業です。
まず出力されたカラーコードをコピーして、そのセルを選択したまま塗りつぶし色のアイコンをクリックし、さらにカスタム色のアイコンをクリックします。
ダイアログが現れるので「16進数」の欄にカラーコードを貼り付けます。あとはOKをクリックするとセルがその色に塗りつぶされます。
これを各カラーコードについて繰り返せばわかりやすい結果となります。
使用上の注意点は次の通りです。
- セルに入力する際の関数名は大文字でも小文字でもOKです。
- 対象範囲は関数の引数として指定できますが、ダブルクォーテーション(")で囲む必要があります。他シートの範囲を指定する場合は「"'シート2'!B2:E5"」のような引数とします。
- 対象範囲のセル色が変更されても自動的に関数の結果が変わることはありません。関数を入力しなおす必要があります。
- カラーコードの順番は初出順(上の行、左のセルが優先)となります。
必要となるカスタム関数の作成ですが、まずメニューから「拡張機能」→「Apple Script」と選択してエディタに次のコードを入力します。
function countColors(arg) { const sheet = SpreadsheetApp.getActiveSheet(); const bgs = sheet.getRange(arg).getBackgrounds(); let bg = ''; let mp = new Map(); for(let r = 0; r < bgs.length; r++){ for(let c = 0; c < bgs[0].length; c++) { bg = bgs[r][c]; if (mp.has(bg)) { mp.set(bg, mp.get(bg)+1); } else { mp.set(bg, 1); } } } return Array.from(mp); }
あとはシートに戻って上記のように関数を入力することで使用できますが、初回実行時に承認の手続きが必要となります。
ここがちょっと面倒ですが、表示されたメッセージを確認のうえ許可していけばOKです。ただしわからない場合は表示されたメッセージ内容を検索してみてください。
以下はコードの説明です。
- 2次元配列bgsに各セルの背景色のカラーコードを格納します。
- for文による2重ループでbgsの値を読み出し、そのつど変数bgに格納します。これがMap(連想配列として使用)であるmpのキーとして存在するか確認し、なければキーとして追加(対応するバリューの初期値を1とする)、あればキーに対応するバリューにを1を加算します。
- 最後にmpを配列に変換して返却(出力)します。
当該色が初登場する行にカウント値を表示する例
先に実行例を示します。
G3セルにカスタム関数「countColColors」を入力し、C3:C10の範囲の各セルの背景色が(上から順に)初登場するものならその行にその色のカウント値を、そうでなければ空白セルを出力しています。1つ上の例と同様に、セルに値が入っていなくてもカウントできます。
対象範囲をダブルクォーテーション("")で囲んで指定することと、関数を「カウント対象範囲のうち最も上のセルと同じ行にあるセル」に入力することが注意点です。
この出力のメリットは、E列にフィルタをかけて空白以外を抽出することで(1つ上の例のように手作業で着色しなくても)色とカウント値を視覚的に確認できることです。
このままC列とE列の各3つのセルを別の範囲にコピペすれば(ただしE列からのコピペについては値を貼り付け)集計表として完成します。
使用上の注意点は次の通りです。
- セルに入力する際の関数名は大文字でも小文字でもOKです。
- 対象範囲は関数の引数として指定します。ただしダブルクォーテーション(")で囲む必要があります。他シートの範囲を指定する場合は「"'シート2'!C3:C10"」のような引数とします。
- カウントできる範囲は1列だけです。複数列を指定しても最も左の列しかカウントしません。
- 対象範囲のセル色が変更されても自動的に関数の結果が変わることはありません。関数を入力しなおす必要があります。
- カラーコードの順番は初出順(上の行が優先)となります。
必要となるカスタム関数の作成については、まずメニューから「拡張機能」→「Apple Script」と選択してエディタに次のコードを入力します。
function countColColors(arg) { const sheet = SpreadsheetApp.getActiveSheet(); const bgs = sheet.getRange(arg).getBackgrounds(); let bg = ''; let mp = new Map(); let arr = Array(bgs.length); for(let r = 0; r < bgs.length; r++){ bg = bgs[r][0]; if (mp.has(bg)) { mp.set(bg, mp.get(bg)+1); } else { mp.set(bg, 1); } } for(let r = 0; r < bgs.length; r++){ bg = bgs[r][0]; if (mp.has(bg)) { arr[r] = mp.get(bg); mp.delete(bg); } } return arr; }
そしてシートに戻って上記のように関数を入力することで使用できます。
ただし初回実行時に承認の手続きが必要となり、そこがちょっと面倒ですが、表示されたメッセージを確認のうえ許可していけばOKです。わからない場合は表示されたメッセージ内容を検索してみてください。
以下はコードの説明です。
- 配列bgsに各セルの背景色のカラーコードを格納します(bgsは2次元配列ですが以降では各番地の0番地しか使用しません)。
- for文によるループでbgsの値を読み出し、そのつど変数bgに格納します。その値がMap(連想配列として使用)であるmpのキーとして存在するか確認し、なければキーとして追加(対応するバリューの初期値を1とする)、あればキーに対応するバリューに1を加算します。
- for文でbgsの値を再度読み出し、そのつど変数bgに格納します。その値がmpのキーとして存在したら、そのキーに対応するバリューを、配列arrの対応する番地に記録し、キーとバリューの組をmpから削除します。
- 最後に配列arrを返却(出力)します。