テーブルデータを扱うときの基本語。 後半は、 このページで SSDSE-B-2026(564 行 × 112 列)を使って実際に操作しているもの。
🍰 まずはやさしく
表のような形のデータのことです。
情報を整理して分析するために使います。
部活の出席簿のような形式です。
まずは結論から簡単に説明します。
テーブルデータ(tabular data)は、 行=サンプル・列=特徴量の矩形構造を持つデータ。 Excel・CSV・DB テーブルの共通形式。
df.to_csv('out.csv', encoding='utf-8-sig') で BOM 付き UTF-8(Excel でも文字化けしない)。df.filter(regex='^A1') で列名パターンマッチ、 df.select_dtypes('number') で数値列のみ、 など。 SSDSE-B-2026 は 112 列(指標は 109 列)あるので、 分析対象列を絞ることが重要。iterrows 禁止(10-100x 遅い)、 ② ベクトル化された pandas/numpy 関数を使う、 ③ dtype を最適化(int64 → int32 でメモリ半分)、 ④ Parquet 形式で読み書き。df.melt で long に変換すると応用範囲が広がる。🍰 まずはやさしく
分析で最もよく使われる形式です。
現実の多くの問いに答えるために使います。
スマホで見るランキング表のようなものです。
どんな場面で使うのかを解説します。
SSDSE-B-2026 は典型的なテーブルデータ(564行 × 112列)。 画像・音声・テキストより地味だが最頻出。
🍰 まずはやさしく
エクセルのシートのような見た目です。
データの構造を直感的に理解するために使います。
都道府県ごとの人口リストのような例です。
行と列の仕組みについて詳しく読みます。
SSDSE-B-2026 を Excel で開くと、 1 列目に年度(列名 SSDSE-B-2026)、 2 列目に Code(地域コード)、 3 列目に Prefecture(都道府県名)、 4 列目以降に A1101 総人口, A1301 年少人口, A1303 高齢人口, A4101 出生数, ... の 109 の指標列が並び、 3 行目以降に 47 都道府県 × 12 年度(2012〜2023)= 564 行のデータ — これがテーブルデータの典型例。 「1 行 = 1 都道府県という観測単位」「1 列 = 1 変数」が決定的な構造。
fit(X, y) にそのまま渡せる形。🍰 まずはやさしく
数学的なルールで決めた定義です。
計算機に正しく伝えるために使います。
テストの点数表を数式にするイメージです。
記号を使った正確な書き方を学びます。
テーブル表現:$$D = \{(x_i, y_i)\}_{i=1}^n, \quad x_i \in \mathbb{R}^p$$
セル参照:$$X_{ij} = \text{行 } i \text{ 列 } j \text{ の値}$$
列平均:$$\bar X_j = \frac{1}{n}\sum_{i=1}^n X_{ij}$$
列分散:$$s_j^2 = \frac{1}{n-1}\sum_{i=1}^n (X_{ij} - \bar X_j)^2$$
標準化:$$Z_{ij} = \frac{X_{ij} - \bar X_j}{s_j}$$
相関行列:$$R_{jk} = \frac{1}{n-1}\sum_i Z_{ij} Z_{ik}$$
結合(inner join):$$T_1 \bowtie_{key} T_2 = \{(r_1, r_2) : r_1 \in T_1, r_2 \in T_2, r_1.k = r_2.k\}$$
group by 集約:$$g(T, k) = \{(k_v, \text{agg}(\{t : t.k = k_v\})) : k_v \in \pi_k(T)\}$$
「Tidy Data」とは、 分析しやすいテーブルデータの形式を定義した規約:(1) 各変数が 1 列、 (2) 各観測が 1 行、 (3) 各観測単位の型が 1 テーブル。 SSDSE-B-2026 はこの原則によく従っており、 1 行が 1 都道府県、 1 列が 1 指標、 観測単位は 47 都道府県で統一。 例外として「2020 / 2022 / 2024」のように年が複数ある場合、 「Year」を新しい列にして長形式にする(df.melt)か、 1 行 = (Pref, Year) のペアにするのが Tidy です。
pandas.DataFrame は内部的に BlockManager という構造で、 同じ dtype の列をまとめた「ブロック」(NumPy 2D 配列)で保持します。 47 都道府県 × 5 数値列の SSDSE-B-2026 なら、 1 つの (47, 5) float64 ブロックでメモリ上に格納されます。 これにより列方向の演算が高速。 Prefecture 列のような文字列は別ブロックに分離。 pandas 2.x からは Apache Arrow ベースの新バックエンドも選択可能で、 文字列効率と相互運用性が向上。
テーブルデータの保存形式:① CSV:可読・汎用・遅い、 ② Parquet:列志向バイナリ、 圧縮・型保持・高速、 ③ Feather:Arrow ベース、 R/Python 相互運用、 ④ Excel:人間が編集可能だが大規模に弱い。 SSDSE-B-2026 は CSV で配布されていますが、 大規模分析時は df.to_parquet('cache.parquet') でキャッシュすると、 型を保ったまま圧縮して保存でき、 大きな表ほど読み込みが速くなります(564 行の SSDSE-B-2026 ではどちらも一瞬です)。
pandas DataFrame の各行・各列には インデックスが付き、 高速な検索・結合・集約を可能にします。 デフォルトは RangeIndex(0, 1, 2, ...)。 SSDSE-B-2026 を df.set_index('Prefecture') すると、 都道府県名で行を指定できるようになります(df.loc['東京都'])。 「年 × 都道府県」のような階層的データには MultiIndex を使い、 df.loc[(2024, '東京都')] のようにアクセスします。
複数テーブルを結合する操作:① pd.concat:縦方向(行追加)または横方向(列追加)の単純連結、 ② pd.merge:SQL ライクなキー結合(inner / outer / left / right)、 ③ df.join:インデックスベースの結合(merge のシュガー)。 SSDSE-A(市区町村別データ)を都道府県単位に集約してコード体系を揃えれば、 pd.merge で B 系列と結合した拡張テーブルが作れます(A は市区町村コードなので、 そのまま on='Code' では結合できない点に注意)。
統計学では欠損のメカニズムを 3 タイプに分類:(1) MCAR(Missing Completely At Random):欠損が完全に無作為、 (2) MAR(Missing At Random):他の観測変数で説明可能、 (3) MNAR(Missing Not At Random):欠損自体が情報を持つ。 SSDSE-B-2026 自体は欠損ゼロだが、 市区町村レベルの統計では小規模自治体での欠損が MNAR の可能性があり(「小さすぎて統計に出ない」)、 単純な平均代入は偏りを生む。 多重代入法(multiple imputation)や指示変数追加で対処。
テーブルデータの「カテゴリ列」(性別・職業・地域など)を数値化する手法:① One-Hot エンコーディング:k 個のカテゴリを k 個の 0/1 列に展開、 ② Label エンコーディング:0, 1, 2, ... の整数化(順序情報がある場合のみ)、 ③ Target エンコーディング:目的変数の平均で置換(リーク注意)、 ④ Frequency エンコーディング:出現頻度で置換。 SSDSE の地方区分(東北・関東・関西...)を One-Hot 化すれば、 線形モデルでも地域効果を学習できます。
Kaggle・自治体分析の実務では、 テーブルデータに対し XGBoost / LightGBM / CatBoost が圧倒的に強い。 ニューラルネットはテーブルデータでは GBDT に勝てないと言われ続けている。 SSDSE-B-2026 の出生数予測のような課題でも、 線形回帰から LightGBM に替えると当てはまりが改善することがある(どれだけ改善するかは特徴量の組み方と県の数しだいで、 47 行では交差検証で確かめる必要がある)。 GBDT は「決定木の弱学習器を逐次的に積み上げる」アンサンブル手法で、 非線形・特徴量相互作用・カテゴリ変数を自動的に扱う点が強み。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | import os os.makedirs('data/processed', exist_ok=True) # 書き出し先を先に作る import pandas as pd import numpy as np # 1) データ読み込み(CP932、 2 行目はメタ情報なのでスキップ) df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df[df['SSDSE-B-2026'] == 2023] # 先頭列 = 年度。 最新 2023 年度 47 県に絞る print(f'shape: {df.shape}, columns: {len(df.columns)}') # 2) スキーマ確認 print('\n[dtypes]') print(df.dtypes.value_counts()) # 3) 欠損値チェック missing = df.isnull().sum() print(f'\n[欠損値あり列]') print(missing[missing > 0].sort_values(ascending=False)) # 4) Tidy 化:wide → long(複数年データを縦持ちにする想定) # df_long = df.melt(id_vars=['Prefecture'], var_name='Indicator', value_name='Value') # 5) 主要列の抜粋 key_cols = ['A1101', 'A1303', 'A4101', 'A4103', 'E4501', 'L3221'] df_key = df[['Prefecture'] + key_cols].copy() print(f'\n抜粋: {df_key.shape}') # 6) Index 設定 df_key = df_key.set_index('Prefecture') # 7) 派生列の追加 df_key['粗出生率_‰'] = df_key['A4101'] / df_key['A1101'] * 1000 df_key['高齢化率_%'] = df_key['A1303'] / df_key['A1101'] * 100 # 8) 地方区分の追加(カテゴリ列) def region(pref): if pref in ['北海道']: return '北海道' if pref in ['青森県','岩手県','宮城県','秋田県','山形県','福島県']: return '東北' if pref in ['茨城県','栃木県','群馬県','埼玉県','千葉県','東京都','神奈川県']: return '関東' return 'その他' df_key['地方区分'] = df_key.index.map(region) # 9) One-Hot エンコーディング df_enc = pd.get_dummies(df_key, columns=['地方区分'], prefix='Region') print(f'\n[One-Hot 後] shape: {df_enc.shape}') # 10) Z-score 正規化 num_cols = df_enc.select_dtypes(include=[np.number]).columns df_norm = df_enc.copy() df_norm[num_cols] = (df_enc[num_cols] - df_enc[num_cols].mean()) / df_enc[num_cols].std() print(f'\n[正規化後] mean (主要): {df_norm[key_cols].mean().to_dict()}') # 11) Parquet で保存(高速読み込み用) df_enc.to_parquet('data/processed/ssdse_b_2026_clean.parquet') print('保存: data/processed/ssdse_b_2026_clean.parquet') # 12) ピボット例 # 仮:年次データなら pivot で都道府県 × 年の行列に変換 # pivot = df_long.pivot(index='Prefecture', columns='Year', values='Value') # 13) groupby 集約 region_summary = df_key.groupby('地方区分').agg({ 'A1101': 'sum', 'A4101': 'sum', '粗出生率_‰': 'mean' }) print(f'\n[地方別集計]\n{region_summary}') |
💬 元ファイルは 564 行(47 県 × 12 年度)で、 2023 年度に絞って初めて 47 行 112 列になる。 型は int64 104 列・float64 6 列・object 2 列(Code と Prefecture)で欠損 0 件。 One-Hot 後の 12 列は、 抜粋 6 列 + 派生 2 列 + 地方区分 4 種のダミー 4 列。 正規化後の平均が -5.7e-17 のような値になるのは、 浮動小数の丸めで 0 になりきらないだけで実質 0。 地方別集計では関東 4,353 万人が全国 1 億 2,435 万人の 35.0% を占め、 粗出生率は関東 5.60‰ に対し東北 4.83‰、 北海道 4.80‰ と地方差が出る。
SSDSE-B-2026 を「テーブルデータ」として正しく扱うフルパイプライン。 読み込み → スキーマ確認 → 欠損処理 → 派生列 → カテゴリ化 → 正規化 → 保存 → 集約。
| 操作 | pandas API | SQL 対応 | SSDSE-B-2026 用例 |
|---|---|---|---|
| 行抽出 | df[df['A1101'] > 5e6] | WHERE | 人口 500 万人超の県 |
| 列選択 | df[['A1101','A4101']] | SELECT | 人口と出生数だけ取得 |
| グループ集約 | df.groupby('Region').sum() | GROUP BY | 地方別人口集計 |
| 並べ替え | df.sort_values('A1101') | ORDER BY | 人口順に並び替え |
| 結合 | pd.merge(df1, df2, on='Code') | JOIN | B 系列と A 系列を結合 |
| ピボット | df.pivot_table(...) | PIVOT(一部) | 都道府県 × 年で出生数行列 |
| 縦持ち化 | df.melt() | UNPIVOT | 複数指標を tidy 化 |
| 欠損補完 | df.ffill() | COALESCE | 欠損を直前値で埋め |
df.melt(id_vars='Pref', var_name='Year', value_name='Value'))pd.merge(df_b, df_a, on='Prefecture', how='left')。 ただし SSDSE-A は市区町村単位なので、 県単位に集約してから結合しないと行数が膨張する)df.groupby('Region')['A1101'].sum())各セルに「単一の値」が入る。 SSDSE-B-2026 は 1NF を満たす:各セルに 1 つの数値が入っており、 配列やリストは含まれない。
複合主キーの一部だけに依存する列が無い。 SSDSE-B-2026 の主キーは (Code, 年度) の複合キーで、 厳密には Prefecture 列が Code のみに依存する部分関数従属があるが、 表示用ラベルとして意図的に許容された設計。
主キー以外の列同士の依存が無い。 SSDSE-B では「人口」と「出生数」が相関しているが、 これは 統計的相関であって 関数従属ではないので 3NF を満たす。
業務システム(OLTP)では正規化が重要だが、 データ分析(OLAP)では 非正規化(フラットテーブル)の方が読み込みが速い。 SSDSE-B-2026 は分析用に意図的にフラット化された配布形式。 「(都道府県 × 年度) × 109 指標」の 1 つの大テーブルに統合されている。
テーブルデータ処理ライブラリの選択肢が増えています。 SSDSE-B-2026 規模なら pandas で十分ですが、 GB クラスのデータでは polars / DuckDB が圧倒的に速い。
| ライブラリ | 特徴 | SSDSE 規模 | 大規模 |
|---|---|---|---|
| pandas | 最大シェア、 API 豊富 | 十分 | 遅い |
| polars | Rust 実装、 並列処理 | オーバースペック | 高速 |
| DuckDB | SQL 互換、 列指向 | SQL 慣れている人向け | 超高速 |
| Dask | pandas 互換 + 分散 | 不要 | TB 級に対応 |
| Vaex | メモリマップ + 遅延評価 | 不要 | 単一マシン高速 |
1 2 3 4 | import pandas as pd df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df[df['SSDSE-B-2026'] == 2023] # 最新 2023 年度の 47 県に絞る print(f'shape: {df.shape}') |
💬 47 行 112 列。 元の CSV は 12 年度分 564 行なので、 年度で絞らないと同じ県が 12 回現れ、 平均も相関も年度をまたいで混ざる。 skiprows=[1] は 2 行目の日本語ラベル(「総人口」など)を飛ばすためで、 これを忘れると全列が文字列型になる。
1 2 | print(df.columns.tolist()[:10]) print(df.dtypes.value_counts()) |
💬 先頭 3 列がキー(年度・Code・Prefecture)、 4 列目以降が指標。 object 2 列はまさに Code と Prefecture で、 int64 104 + float64 6 = 110 列が数値。 float64 になるのは合計特殊出生率(A4103)のように小数を含む指標だけで、 人数や金額は整数のまま読める。
1 2 | missing = df.isnull().sum() print(missing[missing > 0]) |
💬 空の Series が返る、 つまり欠損のある列が 1 つも無い。 SSDSE は公開前に整備されているので dropna や fillna が要らない。 ただし「欠損 0」は「値が正しい」とは別で、 この後の派生列で桁や比率が破綻していないかは自分で確かめる必要がある。
1 | print(df.describe()) |
💬 110 列分の要約が横に長すぎて途中が「...」で省略される。 SSDSE-B-2026 列は全行 2023 なので std 0.0 と出ており、 年度で絞れた確認になる。 総人口 A1101 は平均 265 万人に対し最大 1,409 万人(東京)で、 mean が 50% 点の 155 万人を大きく上回る右裾の長い分布。 その他の消費支出 L322110 は平均 5.5 万円で min〜max が 3.6〜6.8 万円と、 人口系と違ってばらつきが小さい。
1 2 | large = df[df['A1101'] > 5_000_000] print(f'人口 500 万人超: {len(large)} 県') |
💬 9 県。 #7 の上位 10 県の表で 9 位の北海道が 5,092,000 人、 10 位の静岡県が 3,555,000 人なので、 500 万人の線はちょうど 9 位と 10 位の間に入る。 境界値をどこに置くかで件数が変わるので、 閾値は表と突き合わせて決める。
df['粗出生率'] = df['A4101'] / df['A1101'] * 1000 df['高齢化率'] = df['A1303'] / df['A1101'] * 100
def region(pref):
if pref == '北海道': return '北海道'
if pref in ['青森県','岩手県','宮城県','秋田県','山形県','福島県']: return '東北'
return 'その他'
df['Region'] = df['Prefecture'].map(region)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import pandas as pd # Region という列は SSDSE-B-2026 に無いので、都道府県コードから作る def _region(code): n = int(str(code).lstrip('R')) // 1000 if n == 1: return '北海道' if n <= 7: return '東北' if n <= 14: return '関東' if n <= 23: return '中部' if n <= 30: return '近畿' if n <= 35: return '中国' if n <= 39: return '四国' return '九州沖縄' df['Region'] = df['Code'].apply(_region) summary = df.groupby('Region').agg({'A1101': 'sum', 'A4101': 'sum'}) print(summary) |
💬 8 地方の合計。 関東 4,353 万人が最大で、 最小の四国 358 万人の 12 倍。 出生数も関東 252,911 人が全国 727,269 人の 34.8% で、 人口シェア 35.0% とほぼ一致する。 表が北海道→東北の順にならないのは、 groupby が Region の文字コード順(中国・中部・九州沖縄…)で並べるため。 順序を固定したいなら Categorical にする。
1 2 | top10 = df.nlargest(10, 'A1101')[['Prefecture', 'A1101']] print(top10) |
💬 東京 1,409 万人が 1 位、 2 位の神奈川 923 万人とは 486 万人差。 左端の 144, 156, 312 はもとの CSV の行番号で、 各県が 12 年度分ずつ並んでいるため東京(13 番目の県)は 12 × 12 = 144 行目に来る。 絞り込んだ後も index は振り直されないので、 位置で参照したいなら reset_index する。
1 2 3 | df_long = df[['Prefecture', 'A1101', 'A4101', 'E4501']].melt( id_vars='Prefecture', var_name='Indicator', value_name='Value') print(df_long.head()) |
💬 47 県 × 3 指標 = 141 行の縦持ち。 head の 5 行がすべて A1101 なのは、 melt が指標ごとに 47 行ずつ順に積むため。 元は 1 県 1 行だった情報が「県・指標名・値」の 3 列に展開され、 指標を増やしても列ではなく行が増える形になる。
1 2 | pivot = df_long.pivot(index='Prefecture', columns='Indicator', values='Value') print(pivot.head()) |
💬 pivot で元の横持ちに戻る。 ただし行順が三重県・京都府・佐賀県…と元の北海道始まりではなくなっているのは、 pivot が index を文字コード順に並べ直すため。 値は同じで(北海道 5,092,000 人、 出生 24,430 人、 高校生 109,290 人)、 melt → pivot は情報を失わない往復になっている。
df.to_parquet('data/processed/ssdse_b_2026_clean.parquet')
df.to_csv('data/processed/ssdse_b_2026_clean.csv', index=False)
SSDSE-B-2026 のテーブル構造を実例で確認します。 1 行 = 1 (Year, Prefecture) の組、 列 = 観測指標 (A1101 総人口、 A1303 高齢人口〈65 歳以上〉 など) の長形式 (long format) になっていることが特徴。 欠損密度・主キー一意性も後の Step で実測します。
SSDSE-B-2026 の典型的な構造:
| Year | Prefecture | A1101 (総人口) | A1303 (高齢人口65+) | ... |
|---|---|---|---|---|
| 2023 | 北海道 | 5092000 | 1681000 | ... |
| 2023 | 青森県 | 1184000 | 417000 | ... |
| ... | ... | ... | ... | ... |
これで shape=(564, 112) のテーブル。 すぐに pandas で読める。
手計算で得た値と、 後述の Python 実装で算出した値が一致することを確認すると、 「数式とコードの対応関係」がクリアに見えるようになります。
合成テーブル (5 行 × 4 列) で欠損を除いた密度を計算する。
| id | age | score | group |
|---|---|---|---|
| 1 | 25 | 80 | A |
| 2 | NaN | 72 | B |
| 3 | 30 | NaN | A |
| 4 | 28 | 90 | B |
| 5 | NaN | 85 | A |
1 2 3 4 5 6 7 | import pandas as pd import numpy as np df = pd.DataFrame({'id':[1,2,3,4,5], 'age':[25,np.nan,30,28,np.nan], 'score':[80,72,np.nan,90,85], 'group':['A','B','A','B','A']}) total = df.size missing = df.isna().sum().sum() density = 1 - missing/total print(f"密度: {density:.2f}") |
💬 手計算 (Step 2) 0.85 と Python 出力が完全一致。
公的統計(SSDSE-B-2026)を題材に、 最小限の Python コードで動作させます。 ファイルパス(data/raw/SSDSE-B-2026.csv)は自分の環境に合わせて変更してください。 まずはこのまま動かすことが理解の最短ルートです。
🎯 このコードでやること:テーブルデータとして SSDSE-B-2026 を読み込み、 形状・dtype 分布・先頭 5 行で「47 県 × 年度」の二次元構造を確認する。
📥 入力データ:data/raw/SSDSE-B-2026.csv(2 行目は日本語の項目名ラベルなので skiprows=[1]、 エンコーディングは cp932)
1 2 3 4 5 6 7 | import pandas as pd df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) print(df.shape) # (564, 112) print(df.dtypes.value_counts()) print(df.head()) # 行=都道府県×年、 列=指標 |
📤 実行結果:
💬 読み方:行数 564 = 47 県 × 12 年度。 dtype は数値列 110(int64 104 + float64 6)+ 文字列 2(Code・Prefecture)。 これがテーブルデータの典型的な分布。 head() の 5 行がすべて北海道で、 年度が 2023 → 2019 と新しい順に並ぶのは、 1 県の 12 年度分が降順で続いてから次の県に移る並びだからで、 総人口も 5,259,000 人(2019)から 5,092,000 人(2023)へ減っている様子が行方向に読める。
▶ 実行 を押せばこのページの中でそのまま動きます(ライブラリもデータも同梱済みで、 準備は要りません)。 手元の Python に移して動かすときは pip install numpy pandas が必要です。 読んでいるデータは data/raw/SSDSE-B-2026.csv。 日本語を含むので encoding='cp932' の指定を落とさないでください。
テーブルデータを扱うときに、 SSDSE-B-2026 のような実データを前にした初学者が 必ずと言っていいほど踏む 4 つの罠。 1 度経験すれば次から防げますが、 県コードがゼロ落ちして「01 北海道」が「1 北海道」になっていた、 などの事故は分析結果まで巻き戻して修正することになります。
df.duplicated().sum() で 0 を確認。len(df) を比較する習慣を持つ。melt/pivot で都度切り替える。melt/pivot で行き来する必要があるのか?df.duplicated(subset=['地域コード','年度']).sum() がなぜ品質指標として有効か?解答例: 1. 行=県、 列=指標 (A1101 等)、 セル=東京 14086000 (人、 2023 年度)。 2. PK は (地域コード, 年度)、 FK は地域コード参照。 3. Wide は人間可読、 Long は機械処理向き。 4. 主キー重複 / 一対多関係 / NaN 取扱い をチェック。 5. MAR の代表は古い年度欠損。 6. 公式コードブック / メタデータ / プロバイダー問合せ。 7. str.split + explode。 8. PK 一意性が JOIN の前提だから。 9. 行・列を選び直すと観点が切り替わるから。 10. OCR/特徴抽出/アノテーション/カテゴリ集計。
テーブルデータ (行 × 列の 2 次元構造) を中心に、 ファイル形式 (CSV / TSV / Parquet / Excel) ・ 操作言語 (pandas / SQL / R data.frame) ・ 関連概念 (主キー / 正規化 / 結合) を整理する。 SSDSE-B-2026 は典型的なテーブルデータで、 564 行 (47 都道府県 × 12 年度) × 112 列 (指標) の形式。
テーブルデータの概念マップは「行 (レコード) × 列 (フィーチャー)」の 2 次元構造を中心に置き、 SSDSE-B-2026 (564 行 × 112 列、 主キー=(都道府県コード, 年度)) を典型例として周辺ノードに「画像 / 音声 / テキストなどの非構造データ」「Parquet / CSV / RDB の物理形式」「pandas / Polars / DuckDB の処理エンジン」「Kaggle テーブルコンペ (Tabular Playground)」を配置する。
テーブルデータ分析は「データ取り込み (CSV/JSON/RDB) → 前処理 (欠損・スケーリング) → 特徴量生成 → モデリング → 解釈」の一連パイプラインの中核。 SSDSE-B-2026 のような公的統計テーブルは、 1 行 = 1 (都道府県, 年度) という単純構造ゆえに pandas での前処理がほぼ完結し、 LightGBM / XGBoost のような GBDT が依然として SOTA。
テーブルデータは「最も古くから扱われ、 現代でも依然 SOTA が GBDT」という安定領域。 SSDSE-B-2026 はそのまま GBDT 入力に投入できる理想的な学習教材。
SSDSE-B-2026 のようなテーブルデータを前にしたときの手法選択フロー。
df.describe() + 箱ひげ図 + 相関行列。 予測 (新しい都道府県や将来年度の A4103 を予測) → ②へ。SSDSE-B-2026 のような小さなテーブルデータでは、 過剰適合との戦いが主課題。 交差検証 (Leave-One-Out CV が現実的選択肢) で汎化性能を厳密に評価する。
下の表は SSDSE-B-2026 の実測値(2023 年度、 8 都道府県を抜粋)です。 列見出しをクリック(タップ)すると並べ替え、 行をクリックすると「1 行 = 1 観測」がレコード形式で表示されます。 スライダーで絞り込み、 プルダウンで集計を切り替えると、 表・数値・下のグラフがすべてリアルタイムに連動します。 テーブルデータの基本操作 — ソート・フィルタ・集計(group by) — を手で確かめてください。
グラフは「表示中の行」だけから計算されます。 スライダーで行を絞ると、 平均などの統計量も変わることを確認してください(棒に触れると値を表示)。
同じ「3 都道府県 × 3 年分の出生数 A4101(実測値)」でも、 持ち方で分析のしやすさが激変します。 ボタンで 横持ち(wide、 悪い例になりがち) と 縦持ち(long、 tidy) を切り替えて、 「1 行 1 観測・1 列 1 変数」の原則を体感してください。
上の操作で気づくとおり、 ソートは「行の順序の入れ替え」、 フィルタは「行の選択」、 集計は「行のグループ化 + 列への関数適用」— すべての基本操作が「行=観測・列=変数」の約束の上に成り立っています。 この約束が崩れる(例: 横持ちで「年」が列名に埋め込まれる)と、 「2021→2023 の変化率を計算する」「年をまたいで平均する」といった操作が急に書きにくくなります。 テーブルデータは画像・音声・テキストと並ぶデータ形式の一つですが、 統計・機械学習のライブラリ(pandas の DataFrame、 scikit-learn の fit(X, y))が最初に想定する最も基本のデータ形式です。
df.head() と df.tail() で構造を必ず目視確認。pd.to_numeric(s, errors='coerce') で数値化し、 変換できなかった値を 欠損値として洗い出すのが定石。df.dtypes)が先。Code 列で merge できるのはこのおかげ。df.sort_values()、 フィルタ = ブールインデックス df[df['A1101'] >= 5_000_000]、 グループ別集計 = groupby + 集計関数、 横→縦変換 = df.melt()(逆は df.pivot())。 上で触った操作は、 そのまま pandas の 4 大操作に対応する。本ページの各セクションと重複しない別の切り口だけを、 直感・落とし穴・発展の順に簡潔に補います。 いずれも SSDSE-B-2026(実測 566 行=ヘッダ 1 + メタ説明 1 + データ 564 行、 112 列、 47 都道府県 × 12 年度 2012〜2023、 skiprows=[1] で読む)を土台にしています。
テーブルは数学の行列に似ていますが、 決定的に違うのは行と列が非対称な点です。 行(観測)は交換可能な標本の集合で、 SSDSE-B-2026 の 564 行をシャッフルしても情報は 1 ビットも失われません。 一方、 列(変数)は固定された意味を持つ属性で、 A1101(総人口)と A4101(出生数)の列を入れ替えれば意味が壊れます。 行列なら行も列も対称に転置できますが、 テーブルは「行=観測・列=変数」という役割の非対称性ゆえに単なる 2 次元配列とは区別されます(詳しくは 整然データ)。
もう一つの直感は閉性(closure)です。 選択・絞り込み・結合・group by 集約 — どの操作も「表を入れると表が出る」。 564 × 112 の表を都道府県で集約しても、 別の(より小さな)表が返ります。 この閉性があるからこそ、 pandas のメソッド連鎖や SQL のサブクエリのように操作を数珠つなぎにできます。 これは関係モデル(RDB)が「関係代数」として演算体系を持つことの実務的な表れです。
sum() すると二重計上になります。 取り込み直後に「1 つの表に観測粒度は 1 種類か(都道府県だけか、 全国行が混じっていないか)」を df['Prefecture'].unique() で確認する癖を。df_a['A4101'] + df_b['A4101'] は、 両者のインデックスがずれているとラベル一致しない行が NaN になります。 別々に絞り込んだ表を足すときは reset_index(drop=True) か、 明示的に .values を使う。df[df['A1101']>5e6]['A4101'] = 0 は一時コピーへの代入で、 元の表に反映されず SettingWithCopyWarning が出ます。 必ず df.loc[df['A1101']>5e6, 'A4101'] = 0 と単一の .loc で行・列を同時指定する。== でフィルタしたり結合キーにすると、 丸め誤差で一致し損ねます。 キーには整数コード(Code)を使い、 比較は np.isclose か round() 後に行う。scipy.sparse の CSR/CSC 形式で持てば密行列比で約 1/47 のメモリで済みます。 表形式 ML でも疎表現は有効な武器です(scipy.sparse — 本用語集に単独ページなし)。pd.read_csv(chunksize=...) の分割処理や、 Polars・DuckDB の遅延評価・ストリーミングを使います。 564 行なら一瞬ですが、 「同じ列構造で数億行になっても壊れない書き方」を意識すると、 DataFrame と ワイド/ロングの設計がそのままスケールします(Polars/正規化 — 本用語集に単独ページなし)。テーブルデータの構造を 4 つの要素に分けて読み解きます。 テーブルデータは「行 × 列」の 2 次元構造で、 一般に \( D = \{(x_i, y_i)\}_{i=1}^n \)、 \( x_i \in \mathbb{R}^p \) と書かれます。 ここで n は行数(サンプル数)、 p は特徴量数(列数)。 SSDSE-B-2026 の 1 年度分では n = 47(都道府県)、 p ≒ 110(人口・経済・教育指標など)の典型的な構造化データです(全体は 564 行 = 47 県 × 12 年度)。 この一見シンプルな構造の背後には、 ① 行(観測単位)、 ② 列(変数)、 ③ 値(観測量)、 ④ メタデータ(型・単位・欠損ルール)という 4 つの要素が連携しています。
要素 ①「行(観測単位、 サンプル、 record)」:1 行が「1 つの観測対象」を表します。 SSDSE-B-2026 では 1 行 = 1 (都道府県, 年度) の組で、 2023 年度分に絞れば 47 行で全国が完結します。 「行が何を表すか」が明確でないテーブルは tidy ではないとされ、 分析が困難になります。 例えば「東京都の出生数 2020、 2021、 2022」が 3 列に並ぶ場合、 1 行 = 1 都道府県(広形式、 wide format)。 一方「東京都・2020・出生数」が 1 行となる場合、 1 行 = 1 観測時点(長形式、 long format)。 どちらが適切かは分析目的次第ですが、 「1 行 = 1 観測単位」のルールを守ることが Tidy Data の原則(Wickham, 2014)です。
要素 ②「列(変数、 特徴量、 column)」:1 列が「1 つの測定量」を表します。 SSDSE-B-2026 の A1101 列は総人口、 A4101 列は出生数、 という具合に、 各列が明確な意味を持ちます。 列名はコード(A1101)と人間可読名(総人口)の両方を持つのが SSDSE の特徴。 dtype(型)も列ごとに固定されるべきで、 同じ列に「文字列」と「数値」が混在するのはアンチパターン。 列の単位(人、 円、 %)はメタデータとして文書化する必要があります。
要素 ③「値(セル、 観測量、 value)」:行 × 列の交差点に 1 つの値が入ります。 「(東京都, 2023, A1101) = 14,086,000」のように、 行のインデックスと列のインデックスで一意に値が決まります。 値が無い場合は 欠損値(NaN)として扱い、 0 や空文字とは区別する必要があります。 SSDSE の都道府県横断指標では欠損は稀ですが、 市区町村レベルになると小規模自治体で大学数や工場数が欠損することがあり、 NaN として扱われます。
要素 ④「メタデータ(スキーマ、 型、 単位、 ルール)」:テーブルデータの解釈に必要な「データについてのデータ」。 SSDSE-B-2026 では、 ① 列の意味(A1101 = 総人口)、 ② 単位(人)、 ③ 観測年(2023)、 ④ 出典(人口動態統計)、 ⑤ 欠損ルール(空欄は不明)、 ⑥ 型(int64)といったメタデータが、 CSV 先頭の説明行・別ドキュメントで提供されます。 これがないと数値の解釈が不能。 公的統計データを使う際は 必ずメタデータを確認することが研究倫理上も重要です。