連続データの集計
「連続した期間(連続日・連続行・連続カテゴリ)をひとまとまりに集計したい」「移動合計や累積で推移を見たい」場面で使えるテンプレを厳選しました。初心者でも壊れにくい実務向けのコードにしています。
目的別の使い分け
- 累積(ランニングトータル): 行を追いながら合計を上書きしていく
- 移動合計・移動平均: 直近 N 件(N 日)の窓で合計/平均
- 連続ブロック集計: 同じキーや条件が続いている連続区間をひとかたまりに要約
- 連続日数の算出: 稼働/欠勤などの連続日数と開始・終了を出す
- 日付の穴埋め→日次集計: 欠番(抜け日)を補完して連続日で集計(後段の月次/週次へも展開可)
- 大量データ: 範囲→配列で処理し、一括書き戻し(高速・安定)
累積(ランニングトータル):毎行の合計を更新
Sub RunningTotal_Write()
'A=日付(昇順推奨)、B=金額、C=累積合計(出力)
Dim last As Long: last = Cells(Rows.Count, "A").End(xlUp).Row
Dim sumV As Double, r As Long
sumV = 0
For r = 2 To last
sumV = sumV + Val(Cells(r, "B").Value)
Cells(r, "C").Value = sumV
Next
Range("C2:C" & last).NumberFormat = "#,##0"
End Sub
VB- ポイント
- 並び順: 日付・時系列で昇順にしてから実行。
- 数値化:
Valで文字列混入を無害化。
移動合計・移動平均(N件窓)
Sub MovingSumAvg_WindowN()
'A=日付(昇順)、B=金額、C=直近N件合計、D=直近N件平均
Dim last As Long: last = Cells(Rows.Count, "A").End(xlUp).Row
Dim N As Long: N = 7 '例:直近7件
Dim r As Long
For r = 2 To last
Dim s As Long: s = Application.Max(2, r - N + 1)
Cells(r, "C").Value = WorksheetFunction.Sum(Range("B" & s & ":B" & r))
Cells(r, "D").Value = Cells(r, "C").Value / (r - s + 1)
Next
Range("C2:D" & last).NumberFormat = "#,##0"
End Sub
VB- ポイント
- 端の扱い: 先頭側は実データ件数で平均分母を調整。
- 日付窓(7日間)でやりたい: 下の「日付ベース移動集計」を参照。
連続ブロック集計:条件が続く区間を「開始・終了・長さ・合計」で要約
Sub Aggregate_ConsecutiveBlocks()
'A=ID/カテゴリ、B=日付(昇順)、C=状態(例:稼働/休止)、D=値(例:数量)
'集計先:シート「連続集計」に (ID, 状態, 開始日, 終了日, 日数, 合計値)
Dim last As Long: last = Cells(Rows.Count, "A").End(xlUp).Row
Dim outWs As Worksheet
On Error Resume Next
Set outWs = Worksheets("連続集計")
If outWs Is Nothing Then Set outWs = Worksheets.Add: outWs.Name = "連続集計"
On Error GoTo 0
outWs.Cells.Clear
outWs.Range("A1:F1").Value = Array("ID", "状態", "開始日", "終了日", "日数", "合計")
Dim curID As String, curState As String
Dim startDate As Date, endDate As Date
Dim sumVal As Double, r As Long, rOut As Long
rOut = 2
curID = CStr(Cells(2, "A").Value)
curState = CStr(Cells(2, "C").Value)
startDate = Cells(2, "B").Value
endDate = startDate
sumVal = Val(Cells(2, "D").Value)
For r = 3 To last
Dim id As String: id = CStr(Cells(r, "A").Value)
Dim st As String: st = CStr(Cells(r, "C").Value)
Dim dt As Date: dt = Cells(r, "B").Value
Dim v As Double: v = Val(Cells(r, "D").Value)
'IDが変わる or 状態が変わる or 日付が連続していない → ブロック終了
If id <> curID Or st <> curState Or dt <> endDate + 1 Then
'出力
outWs.Cells(rOut, 1).Value = curID
outWs.Cells(rOut, 2).Value = curState
outWs.Cells(rOut, 3).Value = startDate
outWs.Cells(rOut, 4).Value = endDate
outWs.Cells(rOut, 5).Value = endDate - startDate + 1
outWs.Cells(rOut, 6).Value = sumVal
rOut = rOut + 1
'新ブロック開始
curID = id: curState = st
startDate = dt: endDate = dt
sumVal = v
Else
'同一ブロック継続
endDate = dt
sumVal = sumVal + v
End If
Next
'最後のブロックを出力
outWs.Cells(rOut, 1).Value = curID
outWs.Cells(rOut, 2).Value = curState
outWs.Cells(rOut, 3).Value = startDate
outWs.Cells(rOut, 4).Value = endDate
outWs.Cells(rOut, 5).Value = endDate - startDate + 1
outWs.Cells(rOut, 6).Value = sumVal
outWs.Columns.AutoFit
End Sub
VB- ポイント
- 連続の定義: 「同じID・同じ状態・日付が前日+1」で連続判定。
- 並び順必須: 事前に A→C→B(ID→状態→日付)昇順でソートしてから実行。
連続日数の算出(最長連続など)をシンプルに
Sub LongestStreak_PerID()
'A=ID、B=日付(昇順)、C=フラグ(1=稼働, 0=休止)
'出力:シート「最長連続」に ID, 最長連続日数
Dim last As Long: last = Cells(Rows.Count, "A").End(xlUp).Row
Dim outWs As Worksheet
On Error Resume Next
Set outWs = Worksheets("最長連続")
If outWs Is Nothing Then Set outWs = Worksheets.Add: outWs.Name = "最長連続"
On Error GoTo 0
outWs.Cells.Clear
outWs.Range("A1:B1").Value = Array("ID", "最長連続日数")
Dim dictMax As Object: Set dictMax = CreateObject("Scripting.Dictionary")
Dim curID As String, curLen As Long, maxLen As Long
Dim prevDate As Date, r As Long
curID = CStr(Cells(2, "A").Value)
curLen = 0: maxLen = 0: prevDate = Cells(2, "B").Value
For r = 2 To last
Dim id As String: id = CStr(Cells(r, "A").Value)
Dim dt As Date: dt = Cells(r, "B").Value
Dim flag As Long: flag = Val(Cells(r, "C").Value)
If id <> curID Then
dictMax(curID) = Application.Max(dictMax.Exists(curID), maxLen)
curID = id: curLen = 0: maxLen = 0: prevDate = dt
End If
If flag = 1 Then
If dt = prevDate Or dt = prevDate + 1 Then
curLen = curLen + 1
Else
curLen = 1
End If
prevDate = dt
If curLen > maxLen Then maxLen = curLen
Else
prevDate = dt
curLen = 0
End If
Next
'最後のID
dictMax(curID) = Application.Max(dictMax.Exists(curID), maxLen)
'出力
Dim k As Variant, rOut As Long: rOut = 2
For Each k In dictMax.Keys
outWs.Cells(rOut, 1).Value = k
outWs.Cells(rOut, 2).Value = dictMax(k)
rOut = rOut + 1
Next
outWs.Columns.AutoFit
End Sub
VB- ポイント
- 定義: 稼働フラグが1の連続日をカウント。欠勤で途切れる。
- ソート: ID→日付昇順にしてから実行。
日付の穴埋め→日次連続で集計(0補完)
Sub FillMissingDates_AndSumDaily()
'A=日付、B=金額 → 欠番を0で補完し、連続日で表を作る(出力「日次補完」)
Dim rg As Range: Set rg = Range("A1").CurrentRegion
Dim v As Variant: v = rg.Value
Dim minD As Date, maxD As Date, i As Long
minD = v(2, 1): maxD = v(2, 1)
For i = 3 To UBound(v, 1)
If v(i, 1) < minD Then minD = v(i, 1)
If v(i, 1) > maxD Then maxD = v(i, 1)
Next
Dim n As Long: n = CLng(maxD - minD) + 1
Dim out() As Variant: ReDim out(1 To n + 1, 1 To 2)
out(1, 1) = "日付": out(1, 2) = "金額"
'辞書に日付→合計(同日の複数行がある可能性)
Dim sumMap As Object: Set sumMap = CreateObject("Scripting.Dictionary")
For i = 2 To UBound(v, 1)
Dim d As Date: d = DateValue(v(i, 1))
Dim amt As Double: amt = Val(v(i, 2))
If sumMap.Exists(CStr(d)) Then sumMap(CStr(d)) = sumMap(CStr(d)) + amt Else sumMap(CStr(d)) = amt
Next
Dim k As Long, cur As Date: cur = minD
For k = 1 To n
out(k + 1, 1) = cur
out(k + 1, 2) = IIf(sumMap.Exists(CStr(cur)), sumMap(CStr(cur)), 0)
cur = cur + 1
Next
With Worksheets("日次補完")
.Cells.Clear
.Range("A1").Resize(n + 1, 2).Value = out
.Columns.AutoFit
End With
End Sub
VB- ポイント
- 0補完: 連続日に整えれば移動平均や折れ線の見栄えが安定。
- 重複日: 同日複数レコードは合算してから補完。
日付ベースの移動合計(直近N日)にする
Sub MovingSum_ByDays()
'A=日付、B=金額、C=直近N日合計
Dim last As Long: last = Cells(Rows.Count, "A").End(xlUp).Row
Dim N As Long: N = 30 '例:直近30日
Dim r As Long
For r = 2 To last
Dim d0 As Date: d0 = Cells(r, "A").Value
Dim d1 As Date: d1 = d0 - N + 1
'開始日以上かつ当日以下の合計(時刻混入でも安全)
Cells(r, "C").Value = WorksheetFunction.SumIfs( _
Range("B2:B" & last), Range("A2:A" & last), ">=" & CLng(d1), Range("A2:A" & last), "<=" & CLng(d0))
Next
Range("C2:C" & last).NumberFormat = "#,##0"
End Sub
VB- ポイント
- シリアル比較:
CLng(date)で時刻付きにも強い。 - 窓ずれ: 「件数窓」ではなく「日数窓」の移動合計。
- シリアル比較:
大量データ高速版:配列で連続ブロック集計
Sub AggregateBlocks_ArrayFast()
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
Dim rg As Range: Set rg = Range("A1").CurrentRegion 'A=ID, B=日付, C=状態, D=値
Dim v As Variant: v = rg.Value
'前提:ID→状態→日付で昇順ソート済み
Dim rowsOut As Collection: Set rowsOut = New Collection
rowsOut.Add Array("ID", "状態", "開始日", "終了日", "日数", "合計")
Dim i As Long, curID As String, curSt As String
Dim startD As Date, endD As Date, sumV As Double
curID = CStr(v(2, 1)): curSt = CStr(v(2, 3))
startD = v(2, 2): endD = startD
sumV = Val(v(2, 4))
For i = 3 To UBound(v, 1)
Dim id As String: id = CStr(v(i, 1))
Dim st As String: st = CStr(v(i, 3))
Dim d As Date: d = v(i, 2)
Dim val As Double: val = Val(v(i, 4))
If id <> curID Or st <> curSt Or d <> endD + 1 Then
rowsOut.Add Array(curID, curSt, startD, endD, endD - startD + 1, sumV)
curID = id: curSt = st: startD = d: endD = d: sumV = val
Else
endD = d: sumV = sumV + val
End If
Next
rowsOut.Add Array(curID, curSt, startD, endD, endD - startD + 1, sumV)
Dim n As Long: n = rowsOut.Count
Dim out() As Variant: ReDim out(1 To n, 1 To 6)
For i = 1 To n
Dim a As Variant: a = rowsOut(i)
Dim j As Long
For j = 1 To 6: out(i, j) = a(j - 1): Next
Next
With Worksheets("連続集計")
.Cells.Clear
.Range("A1").Resize(n, 6).Value = out
.Columns.AutoFit
End With
Cleanup:
Application.Calculation = xlCalculationAutomatic
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
VB- ポイント
- セル往復ゼロ: 配列→コレクション→一括貼付で高速。
- 汎用性: キーや条件は列名に合わせて差し替え可。
よくある落とし穴と対策
- 並び順が乱れて連続判定が誤る
- 対策: 事前にキー→日付で昇順ソートする(ID→状態→日付など)。
- 時刻付きで連続判定がズレる
- 対策:
DateValueで日付に丸めるか、比較はシリアル値(CLng(date))で行う。
- 対策:
- 数値が文字列で計算されない
- 対策:
Valで安全に数値化。式は.Value = .Valueで値化。
- 対策:
- 大規模で遅い
- 対策: 配列処理に切り替え、前後で画面更新・イベント・計算を停止→復帰。
例題で練習
'例1:累積(ランニングトータル)をC列に出力
Sub Example_Running()
Call RunningTotal_Write
End Sub
'例2:直近7件の移動合計・平均
Sub Example_MovingN()
Call MovingSumAvg_WindowN
End Sub
'例3:連続ブロックを「開始・終了・日数・合計」で要約
Sub Example_Blocks()
Call Aggregate_ConsecutiveBlocks
End Sub
'例4:IDごとの最長連続日数を算出
Sub Example_LongestStreak()
Call LongestStreak_PerID
End Sub
'例5:欠番を0補完して日次連続表を生成
Sub Example_FillDaily()
Call FillMissingDates_AndSumDaily
End Sub
'例6:大量データを配列で高速ブロック集計
Sub Example_ArrayFastBlocks()
Call AggregateBlocks_ArrayFast
End Sub
VB