0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Excel VBA】ピボットテーブルは使わない。Dictionary(連想配列)で高速にクロス集計するテクニック

0
Posted at

Pandasの groupby + unstack の話を、**Excel VBA(マクロ)**の世界に置き換えた記事ドラフトを作成しました。

VBAでピボットテーブル機能(PivotCaches.Create など)を使わずにクロス集計を行う場合、**「連想配列(Dictionary)」**を使うのが定石です。これがPandasでいうIndex操作に相当します。

「ピボットテーブル機能は重い」「書式が勝手に変わるのが嫌」という層に刺さる、実務的な内容にしています。

記事タイトル案
【Excel VBA】ピボットテーブルは使わない。Dictionary(連想配列)で高速にクロス集計するテクニック

記事本文(ドラフト)
はじめに
Excel VBAでクロス集計を行う際、マクロの記録で生成された PivotCaches.Create や CreatePivotTable をそのまま使っていませんか?

もちろん標準機能は強力ですが、自動化の文脈では以下のような悩みが生じがちです。

ファイルサイズ肥大化: ピボットキャッシュが残ってファイルが重くなる。

書式崩れ: 更新のたびに列幅や罫線がリセットされるのを防ぐコードが必要。

配布時のトラブル: バージョン間の互換性や、ユーザーが誤ってピボットを操作してしまうリスク。

今回は、ピボットテーブル機能(オブジェクト)に頼らず、**「配列」と「連想配列(Dictionary)」**を使って、純粋な値としてクロス集計結果を算出する方法を紹介します。Python Pandasでいう groupby 処理をVBAで自作するイメージです。

ロジックの考え方:Index + Key
Pandasなどのデータフレーム操作に慣れている方なら、以下の対比が分かりやすいでしょう。

Pandas: groupby(['Date', 'Category'])['Value'].sum()

VBA: Dictionary オブジェクトの Key に 'Date_Category' という複合キーを持たせて加算する

VBAでは Dictionary をインデックス(検索キー)として利用することで、高速な集計が可能になります。

コード解説
以下のサンプルデータ(Sheet1)を集計します。

A列: 日付 (Date)

B列: カテゴリ (Category)

C列: 数値 (Value)

サンプルコード
参照設定で「Microsoft Scripting Runtime」にチェックを入れるか、以下の通りLate Binding(CreateObject)で使用します。

VBA

Sub ManualPivotAggregation()
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("Sheet1")

' 1. データを配列に取り込む(高速化の基本)
Dim lastRow As Long
lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row

Dim dataArr As Variant
' A列~C列をまとめて取得
dataArr = ws.Range("A2:C" & lastRow).Value 

' 2. Dictionaryで集計(Group By)
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")

Dim i As Long
Dim key As String
Dim dateVal As String, catVal As String, amount As Double

For i = 1 To UBound(dataArr, 1)
    dateVal = dataArr(i, 1)
    catVal = dataArr(i, 2)
    amount = dataArr(i, 3)
    
    ' 複合キーを作成(例: "2024/01/01|A")
    key = dateVal & "|" & catVal
    
    ' 集計ロジック:キーが存在すれば加算、なければ新規登録
    If dict.Exists(key) Then
        dict(key) = dict(key) + amount
    Else
        dict(key) = amount
    End If
Next i

' 3. 結果の出力(Unstack的な処理)
' ここでは簡易的に別シートのリストとして出力します
Dim outWs As Worksheet
Set outWs = ThisWorkbook.Sheets.Add

' ヘッダー
outWs.Range("A1:C1").Value = Array("Date", "Category", "Sum_Value")

Dim k As Variant
Dim parts() As String
Dim r As Long
r = 2

' Dictionaryの中身を展開
For Each k In dict.Keys
    parts = Split(k, "|") ' キーを分解
    outWs.Cells(r, 1).Value = parts(0) ' Date
    outWs.Cells(r, 2).Value = parts(1) ' Category
    outWs.Cells(r, 3).Value = dict(k)  ' Sum
    r = r + 1
Next k

MsgBox "集計完了"

End Sub
なぜこの方法が良いのか?

  1. 圧倒的に高速
    セルを1つずつループして処理すると遅いですが、上記のように Range.Value で配列に一度取り込んでからメモリ上で計算すれば、数万〜数十万行のデータでも数秒以内で完了します。ピボットテーブルの更新処理よりも速いケースが多いです。

  2. 「完全な値」として出力できる
    ピボットテーブルはあくまで「表示機能」を持ったオブジェクトですが、この方法なら純粋な「値(文字列・数値)」としてセルに出力されます。

他のブックへのコピペが容易

書式設定(塗りつぶしや罫線)が完全に自由

配布先でピボットの操作ミスが発生しない

  1. 複雑な条件分岐が可能
    ピボットテーブルの「集計フィールド」では難しいような条件(例:「AかつBの場合は係数0.8を掛けて集計」など)も、If 文一つで自由に組み込めます。

実務での応用(マトリクス表にするには?)
上記のコードはリスト形式(縦持ち)での出力ですが、クロス表(マトリクス)にしたい場合は、辞書を2つ用意する方法が一般的です。

RowDict: 行見出し(日付など)の管理

ColDict: 列見出し(カテゴリなど)の管理

ResultArr: (RowDict.Count, ColDict.Count) の2次元配列を用意して集計値を格納

これらを組み合わせることで、ピボットテーブルと全く同じ見た目を、VBAだけで自在に生成・制御できるようになります。

まとめ
手軽な分析 → 標準のピボットテーブル

定型レポートの自動化・配布 → VBA配列 + Dictionary

VBAで「ピボットテーブルの操作」と格闘して消耗している方は、一度「自前で集計してしまう」というアプローチを検討してみてはいかがでしょうか。処理速度とメンテナンス性が劇的に向上するはずです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?