本ページのトピックを 1 タップで移動できます。 「集計」を学ぶうえで押さえたい論点はすべてここに集約しました。
複数行のデータを groupby などのキーで束ね、 合計・平均・件数などの要約統計量に変換する操作。
df.groupby(key).agg(...) が中心。 SQL でいう GROUP BY と完全に対応。「集計」とは、 たくさんある行を意味のあるまとまりに束ねて、 数値を 1 行に要約すること。 たとえば SSDSE-B-2026 には 47 都道府県 × 約 110 列(人口・出生・労働力 等)が並んでいる。 これを「地方ブロックごとの合計人口は?」「東日本 vs 西日本で大学数の平均はどちらが大きい?」と問うとき、 私たちは 都道府県 47 行 → 地方ブロック 8 行 に 行数を圧縮している。 これが集計の本質。
日常生活の比喩でいえば、 集計は 「家計簿の月締め」 に近い。 1 日ごとのレシート(個票)を、 「食費」「交通費」のカテゴリで縦に積み上げて月別合計に変換する。 元のレシート 1 枚 1 枚は残らないが、 全体傾向が見える。 同じように、 SSDSE では「47 都道府県の個別値」を 1 枚 1 枚のレシートに見立て、 「地方ブロック」というカテゴリで束ねる。
集計には主に 3 タイプある:
大事なのは、 「合計」と「平均」と「件数」で見える物語がまったく違う ということ。 たとえば「関東は人口合計で日本一」だが「平均年収」では東京単独が突出していて、 神奈川・千葉・埼玉と平均すると東京単独より下がる。 だから集計関数の選び方が分析の質を決める。
SSDSE-B-2026 を使った具体的な集計例は、 後段「🧮 実値で計算してみる」で扱う。 そこでは 47 都道府県を 8 つの地方ブロックに分けて合計人口・平均出生率を計算 する。
歴史的背景:集計は統計学の起源そのもの。 17 世紀のジョン・グラントが「ロンドン死亡記録」を週次集計したのが近代統計の出発点とされ、 19 世紀のフローレンス・ナイチンゲールも野戦病院の死因を集計してインフォグラフィックを発明した。 つまり集計は 「データを意思決定可能な情報に変える」 最古かつ最強の技法。
グループ \(g\) に属する観測値の集合を \(S_g = \{x_i : i \in g\}\)、 その要素数を \(n_g\) とする。 代表的な集計関数は次の通り。
$$ \text{sum}(g) = \sum_{i \in g} x_i, \qquad \text{mean}(g) = \frac{1}{n_g}\sum_{i \in g} x_i $$ $$ \text{count}(g) = n_g, \qquad \text{var}(g) = \frac{1}{n_g - 1}\sum_{i \in g}(x_i - \text{mean}(g))^2 $$ $$ \text{min}(g) = \min_{i \in g} x_i, \qquad \text{max}(g) = \max_{i \in g} x_i $$複数キーでの集計(多段 groupby)は、 集合の 分割を入れ子にする操作と等しい。 たとえば「地方ブロック × 性別」で人口を集計する場合:
$$ A_{b,s} = \sum_{i : \text{block}(i)=b,\ \text{sex}(i)=s} \text{pop}_i $$割合(シェア)の計算は集計値を全体合計で割る正規化操作:
$$ \text{share}(g) = \frac{\text{sum}(g)}{\sum_{g'} \text{sum}(g')} \times 100\ [\%] $$「集計」と双対の概念に 結合(join)がある。 集計が 「縦方向の圧縮」 なら、 結合は 「横方向の拡張」。 多くの分析パイプラインは read → join → groupby → agg の順で組まれる。
SSDSE-B-2026.csv の 47 都道府県を、 8 つの地方ブロック(北海道・東北・関東・中部・近畿・中国・四国・九州沖縄)にグループ化し、 総人口 A1101 を合計 する例を見てみる。 ここでは概数(万人)で示す。
| 地方ブロック | 含まれる都道府県(数) | 総人口(万人) | 平均人口(万人) | 全国シェア(%) |
|---|---|---|---|---|
| 北海道 | 1 | 509 | 509.2 | 4.1 |
| 東北 | 6 | 837 | 139.5 | 6.7 |
| 関東 | 7 | 4357 | 622.4 | 34.8 |
| 中部 | 9 | 2095 | 232.8 | 16.7 |
| 近畿 | 7 | 2237 | 319.6 | 17.9 |
| 中国 | 5 | 713 | 142.6 | 5.7 |
| 四国 | 4 | 359 | 89.8 | 2.9 |
| 九州沖縄 | 8 | 1404 | 175.5 | 11.2 |
| 全国合計 | 47 | 12511 | 266.2 | 100.0 |
表から:関東ブロック単独で全国人口の約 35% を占める(4357/12511)。 これは個別の 47 行を眺めるだけでは見えにくい構造。 集計が「視点を変える」ことが分かる典型例。
同様に 出生数 A4101 を集計すれば「東日本 vs 西日本での子ども数の偏り」、 大学数 E4501 を集計すれば「高等教育インフラの地域偏在」が浮かび上がる。
SSDSE-B-2026 の列 A1303(65 歳以上人口)/A1101(総人口)から県別高齢化率を計算し、 地方ブロックごとに平均をとると下表のようになる(概数)。
| 地方ブロック | 高齢化率の平均 (%) | 最小県 (%) | 最大県 (%) |
|---|---|---|---|
| 北海道 | 33.0 | 33.0 | 33.0 |
| 東北 | 34.5 | 32.0 | 38.5 |
| 関東 | 28.6 | 23.5 | 31.5 |
| 中部 | 31.0 | 25.5 | 35.0 |
| 近畿 | 30.5 | 27.0 | 34.0 |
| 中国 | 34.0 | 30.5 | 37.5 |
| 四国 | 35.0 | 33.0 | 37.0 |
| 九州沖縄 | 31.5 | 23.0 | 35.0 |
同じ集計でも 「合計」 なら関東が日本一だが、 「平均高齢化率」 なら四国・東北・中国が上位に来る。 集計関数を変えると見える社会構造が反転する のがよく分かる。
SSDSE-B-2026 を読み込んで、 47 都道府県を地方ブロックで集計する完全コード。
import pandas as pd
# 1) SSDSE-B-2026 読み込み (1行目が英語コード、2行目が日本語ラベル、3行目以降データ)
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
# 2) 都道府県を地方ブロックにマッピング
block_map = {
'北海道': '北海道',
'青森県': '東北', '岩手県': '東北', '宮城県': '東北', '秋田県': '東北', '山形県': '東北', '福島県': '東北',
'茨城県': '関東', '栃木県': '関東', '群馬県': '関東', '埼玉県': '関東', '千葉県': '関東', '東京都': '関東', '神奈川県': '関東',
'新潟県': '中部', '富山県': '中部', '石川県': '中部', '福井県': '中部', '山梨県': '中部',
'長野県': '中部', '岐阜県': '中部', '静岡県': '中部', '愛知県': '中部',
'三重県': '近畿', '滋賀県': '近畿', '京都府': '近畿', '大阪府': '近畿', '兵庫県': '近畿', '奈良県': '近畿', '和歌山県': '近畿',
'鳥取県': '中国', '島根県': '中国', '岡山県': '中国', '広島県': '中国', '山口県': '中国',
'徳島県': '四国', '香川県': '四国', '愛媛県': '四国', '高知県': '四国',
'福岡県': '九州沖縄', '佐賀県': '九州沖縄', '長崎県': '九州沖縄', '熊本県': '九州沖縄',
'大分県': '九州沖縄', '宮崎県': '九州沖縄', '鹿児島県': '九州沖縄', '沖縄県': '九州沖縄',
}
df['Block'] = df['Prefecture'].map(block_map)
# 3) groupby で集計(複数の集計関数を同時適用)
agg = df.groupby('Block').agg(
都道府県数=('Prefecture', 'count'),
総人口=('A1101', 'sum'),
平均人口=('A1101', 'mean'),
出生数合計=('A4101', 'sum'),
大学数合計=('E4501', 'sum'),
)
print(agg.sort_values('総人口', ascending=False))
# 4) 全国シェアを追加
agg['人口シェア(%)'] = agg['総人口'] / agg['総人口'].sum() * 100
print(agg)
2 軸集計とピボットテーブル
# 仮の年齢区分列を追加(65歳以上人口 A1303 / 総人口 A1101 で高齢化率)
df['高齢化率'] = df['A1303'] / df['A1101']
df['高齢区分'] = pd.cut(df['高齢化率'], bins=[0, 0.25, 0.30, 1.0], labels=['若い', '標準', '高い'])
# 2軸の集計
two_way = df.groupby(['Block', '高齢区分'], observed=True).size().unstack(fill_value=0)
print(two_way)
# ピボットテーブル形式
pv = df.pivot_table(
index='Block',
values=['A1101', 'A4101', 'E4501'],
aggfunc=['sum', 'mean'],
)
print(pv)
# Excel/CSV に書き出し
pv.to_csv('outputs/region_pivot.csv', encoding='utf-8-sig')
カスタム集計関数(lambda・apply)
# 各地方ブロックで「人口最大の県」を取り出す
def top_pref(group):
idx = group['A1101'].idxmax()
return group.loc[idx, 'Prefecture']
top_by_block = df.groupby('Block').apply(top_pref)
print(top_by_block)
# 北海道 -> 北海道、関東 -> 東京都、近畿 -> 大阪府、九州沖縄 -> 福岡県 …
# 加重平均:人口で重み付けした高齢化率(県を単純平均しない)
def weighted_aging(group):
return (group['A1303'].sum() / group['A1101'].sum()) * 100
w_aging = df.groupby('Block').apply(weighted_aging)
print(w_aging.round(2))
transform:集計結果を元の行に戻す
# 各県の人口を「自県が所属する地方ブロック総人口」で割って、 ブロック内シェアを計算
df['ブロック内人口シェア(%)'] = df.groupby('Block')['A1101'].transform(lambda x: x / x.sum() * 100)
print(df[['Prefecture', 'Block', 'A1101', 'ブロック内人口シェア(%)']].head(10))
# 関東ブロックでは東京都が約 32%、 神奈川県が約 21% を占めることがわかる
補足:df.groupby(...).transform(...) は集計結果を 元の行数のまま 返すので、 「県の値をブロック平均と比較したい」「ブロック内偏差を計算したい」ようなケースに最適。
df.copy() でバックアップを取る癖もつけたい。sum() はデフォルトで NaN を 0 として無視する。 SSDSE で欠損のある列を集計すると 暗黙のうちに観測数が変わる。 df.groupby(...).agg('count') で件数も同時に確認するのが鉄則。astype('category') にしておかないと 順序が辞書順になり、 地方ブロックを北海道→東北→…で並べたいのに北海道→九州→…になることがある。 表示順を明示する pd.Categorical([...], categories=[...], ordered=True) が便利。(group['A1303'].sum() / group['A1101'].sum()) のように分子分母を別々に sum する。集計(aggregation)は統計学の起源そのものといっても過言ではない。 1662 年、 ジョン・グラント(John Graunt)はロンドン市の死亡記録を週次・年次で 集計 し『死亡表に関する自然的および政治的観察』を出版。 これが 近代統計学の最古の著作とされる。 個別の死亡情報を「年齢階級」「死因」というキーで束ねたことが、 都市衛生政策の意思決定を可能にした。
19 世紀には、 フローレンス・ナイチンゲール(Florence Nightingale)がクリミア戦争の野戦病院死因を月別・原因別に集計し、 ローズダイアグラム(polar area chart) で可視化した。 「戦闘での死より、 衛生環境の不備による感染症死のほうが圧倒的に多い」という事実が、 集計と可視化の組み合わせで初めて社会に伝わった。
計算機時代に入ってからの集計の主役は SQL(1970 年代、 IBM)であり、 そこで GROUP BY 句が標準化された。 2000 年代以降の R の dplyr、 Python の pandas は SQL の集計セマンティクスを継承しつつ、 data frame という形でメモリ上の柔軟な集計を可能にした。 SSDSE のような中規模公的統計(数十 MB クラス)は、 pandas の groupby で十分快適に扱える時代に入った。
「集計」が実務でどう使われるか、 SSDSE-B-2026 を題材にした具体的なシナリオで見てみる。
SSDSE-B-2026 の人口・出生数・転入転出から「地方ブロック別の人口流動表」を作る。 集計列:A1101(総人口)、 A4101(出生数)、 A5101(転入)、 A5102(転出)。 集計後の表をベースに、 「自然増減=出生数−死亡数」「社会増減=転入−転出」を算出し、 地方創生戦略の優先順位付けに使える。
列 E4501(大学数)・E4601(大学学生数)・A1101(人口)を地方ブロックで集計。 「人口あたり大学学生数」を比較し、 東京一極集中の度合いを定量化。 集計値だけでなく シェア(%) と 人口比 の 2 つの観点で見るのがコツ。
SSDSE の I 列(医療施設・診療所・保育所)を集計対象に、 ブロック別の「人口千人あたり医療施設数」を算出。 単純合計だけでは「人口の多い関東が圧倒的」になるが、 正規化(人口で割る) をすると四国・中国が上位に来ることがある。
SSDSE の L322 系列(家計の消費支出内訳:食料・住居・光熱・被服など)を地方ブロックで集計し、 ブロックごとの消費構成比を計算。 ここから k-means クラスタリング で「消費パターンが似たブロック群」を発見できる。 集計が機械学習の入力データを作るパイプラインの起点になる例。
「集計」と混同しやすい用語との比較表。 違いを明確にすることで使い分けの判断が早くなる。
| 観点 | 集計 | 似た用語 | 違い |
|---|---|---|---|
| 行数の変化 | 行が 減る(圧縮) | 結合(join)は行が 増えることもある | 集計は縦方向の圧縮、 結合は横方向の拡張 |
| キーの役割 | グループを定義する列 | 結合キーは「同じ ID を持つ行をくっつける」 | 集計キーは「同じ値の行を束ねる」 |
| 典型関数 | sum / mean / count / median | merge / join | 集計は集約関数、 結合はキーマッチ |
| SQL 対応 | GROUP BY + 集約関数 | JOIN ... ON | SQL でも別概念だが、 1 つのクエリで併用される |
| SSDSE での例 | 47 県を 8 地方ブロックに圧縮 | 都道府県別 GDP と人口を ID で merge | 集計は要約、 結合は変数追加 |
| 情報損失 | あり(個票情報は失われる) | なし(行数増・列数増のみ) | 集計後は元データに戻れない |
groupby は 縦持ちの結果(MultiIndex も可)、 pivot_table は 横持ちのクロス表を作る。 行と列を両方カテゴリで切りたい(例:地方 × 高齢区分)ときは pivot_table が読みやすい。 縦持ちで後続処理(merge / plot)したいときは groupby が便利。resample は 時系列の時間軸を集計キーにする groupby の特殊版。 SSDSE-B-2026 は年次の都道府県データなので resample は不要だが、 もし「2018-2026 の都道府県別年次パネル」を扱うなら resample('Y') が便利。dropna=True では NaN を持つ行は 集計から除外される。 「NaN もひとつのグループとして集計したい」なら groupby(key, dropna=False) を指定。 集計対象列の NaN とキー列の NaN を区別すること。transform を使う。 例:df['block_mean'] = df.groupby('Block')['A1101'].transform('mean') で「自県が所属するブロックの平均人口」を各行に展開できる。 偏差や標準化に必須。astype('category') に変換、 不要列を drop、 groupby(observed=True) でメモリ削減。 さらに大規模なら Polars / DuckDB を検討。reset_index() したか? 後続処理での MultiIndex 事故を回避。utf-8-sig にしたか? Excel で開く配慮。「集計(aggregation)」とは 個票レベルの観測値を、 グループ単位の要約統計量に変換する操作。 SSDSE-B-2026 のような 47 都道府県データを「地方ブロック 8 つ」「年齢階級」「性別」などのキーで束ね、 sum / mean / count / median といった集約関数で 1 行に要約する。 pandas では df.groupby(key).agg(...) が中心ツールであり、 SQL の GROUP BY と 1 対 1 対応。
覚えるべきは 3 点:① 集計後は 行が減る/元に戻れない。 ② 集計関数の選び方(sum vs mean vs median)で 見える物語が変わる。 ③ 加重平均が必要かどうかの判断。