論文一覧に戻る 📚 用語集トップ 🗺 概念マップ
📚 用語解説
📚 用語解説
縦持ち・横持ち
Long / Wide format
データ処理

🔖 キーワード索引

この用語と一緒に検索・参照されやすいタグ。 関連ページに飛ぶときの手がかりにも使えます。

#データ処理#縦持ち#横持ち#tidy#pivot

data long wide」は統計データ分析の文脈で扱う重要概念のひとつ。 本ページでは「data long wide」を取り巻く中核キーワードを以下にチップで一覧化する。 各キーワードは関連する概念・手法・道具立てを含み、 文献検索や学習計画の起点になる。

data long wide統計分析SSDSE-B-2026前提条件適用範囲落とし穴関連手法Python 実装検証方法

これらのキーワードは「data long wide の理解 → 適用 → 検証」のプロセスを構成する。 各章で詳しく解説する。

💡 30秒で分かる結論

🍰 まずはやさしく

データの並べ方の2つのパターンのことです。

分析や表示をスムーズにするために使います。

スマホの連絡先リストのような表を想像してください。

まずは結論から短くまとめました。

縦持ち(long)横持ち(wide)は、 同じ情報を異なる形に並べたテーブルの 2 つの正準形態

ここまでが要点です。 ただし実際に使う前に、 このページの「⚠️ よくある落とし穴」で挙げた 「型混在」の縦持ち/pivot の重複値エラー/「人間用と機械用を混同」 には必ず目を通してください。 つまずくのは知識が無いときより、 知ってはいたが確認を飛ばしたときです。

📍 文脈:「縦持ち・横持ち」はどんな場面で出てくる?

🍰 まずはやさしく

データの形を使い分ける場面の話です。

ソフトに合わせて形を変えるために使います。

部活の成績をグラフにする時に必要になります。

どんな場面でこの考え方が役立つか説明します。

SSDSE-B-2026 は典型的な横持ち(年×県の各行に 100 列の指標)。 一方、 ggplot や seaborn は縦持ちを前提。 「変換できる」ことが分析の前提条件。

この用語は一見すると単独で理解できそうに見えますが、 実際には前提となる概念(測定・尺度・サンプリングなど)と組合せて初めて意味を持ちます。 「定義を覚える」より「どんな問いに答える道具なのか」を捉えるのが効率的です。

🎨 直感で掴む

🍰 まずはやさしく

データの見た目をイメージで捉えることです。

人間と機械のどちらが読みやすいか分けるためです。

買い物リストを縦に書くか横に書くかの違いです。

直感的に理解するための例を挙げます。

「縦持ち・横持ち」を最初に学ぶときは、 厳密な定義よりイメージを優先しましょう。 以下は具体例・比喩を用いた直感的理解の入口です。

💡 学習のコツ:上の比喩は厳密ではない点に注意。 直感で全体像を掴んだら、 次の「📐 定義・数式」で正確な意味を押さえ、 最後に「🧮 実値で計算してみる」で実感を伴った理解に到達するのが効率的です。

🎨 直感で掴む — 同じデータを「縦に並べる」か「横に広げる」か

エクセルでデータを管理するとき、 多くの人は「行 = 都道府県」「列 = 2018年, 2019年, …」と 横持ち(wide) で書きがちです。 一方、 統計ソフトや BI ツールは 縦持ち(long)、 つまり「行 = 都道府県 × 年 × 指標」を 1 行ずつ並べる形を好みます。 内容は同じでも形が違うと、 集計・可視化・統計モデルの当てはめやすさが大きく変わる ── これが long / wide 変換の本質です。

SSDSE-B-2026 で見比べる

SSDSE-B-2026 は もともと wide 形式 です(1 行 = 年度 + 都道府県、 列 = 各指標)。 これを「年 × 都道府県」のクロス表に変換すると もう一段違う wide、 「年・指標・値」を 1 行ずつ並べると long になります。 同じ情報なのに見た目は別物。

📥 入力データ(SSDSE-B-2026 抜粋・wide)

SSDSE-B-2026 Code Prefecture A1101 A1303 A4101 2023 R01000 北海道 5092000 1681000 24430 2022 R01000 北海道 5140000 1686000 26407 2021 R01000 北海道 5183000 1686000 28762 2023 R13000 東京都 14086000 3205000 86348 ...(564 行 × 112 列)

この「年度 × 都道府県 × 多数の指標」が wide 形式。 ある 1 つの指標(例: A1101 総人口)に注目すると、 これを (行=都道府県, 列=年度) のクロス表に変えるのが「wide-to-wider pivot」、 あるいは (行=年度×都道府県×指標, 列=値) の long に変えるのが「tidy 形式」への変換です。

📐 定義・数式

🍰 まずはやさしく

データの並べ方のきまりのことです。

間違いなく分析を行うために使います。

テストの点数表を正しく作るルールのようなものです。

縦持ちにするための3つの条件を学びます。

直感の次は、 厳密な定義を確認します。 数式は言語の一種で、 一度書き慣れれば「言葉より速く伝えられる」便利な道具。 慣れていない方は、 各記号が何を表すかを「🔬 数式を言葉で読み解く」で 1 つずつ確認してください。

【tidy data の 3 原則】
$$ \text{Tidy} \iff \begin{cases} 1\text{ var} = 1\text{ col} \\ 1\text{ obs} = 1\text{ row} \\ 1\text{ unit} = 1\text{ table} \end{cases} $$
この 3 つを満たせば縦持ち。 pandas・seaborn・ggplot 等の後工程が極めて滑らかになる。
📌 読み方のコツ:数式を見たら「左辺は何を定義しているか」「右辺の各項は何の合計・積・比か」を声に出して読み下してみる。 これだけで理解が大きく進みます。

📐 定義と tidy data の 3 原則

Hadley Wickham(pandas / tidyverse の設計思想を提唱)は「tidy data」を以下の 3 条件で定義しました。 これが long 形式の理論的根拠です。

  1. 1 変数 = 1 列: 1 つの観測対象(年度・指標・値)に 1 列を割り当てる
  2. 1 観測 = 1 行: 1 つの観測結果(東京都の 2023 年の総人口)が 1 行
  3. 1 観測単位 = 1 テーブル: 異なる粒度の観測を混在させない(県データと市データを別テーブルに)
【pandas melt / pivot の対応】
$$ \text{wide} \xrightarrow{\text{melt}} \text{long} \xrightarrow{\text{pivot}} \text{wide} $$

wide → long は pd.melt または df.stack()。 long → wide は pd.pivot_table または df.unstack()。 列名と行名(インデックス)の 役割の入れ替え がこの変換の本質。

🔬 数式を言葉で読み解く(変換の対応)

id_vars(melt の引数)
「縦に変換しない列」。 通常は識別子(都道府県・年度)を指定。 例: id_vars=['Code', 'Prefecture', 'SSDSE-B-2026']
value_vars(melt の引数)
「縦に変換する列」。 指標群を指定。 省略すると id_vars 以外すべて。
var_name / value_name
変換後の「指標名列」と「値列」の名前。 デフォルトは 'variable' と 'value'。 SSDSE 用には '指標' / '値' などに rename しておくと可読性が上がる。
index / columns / values(pivot の引数)
「行に来る列」「列に来る列」「セルに入る値」。 long → wide では index に識別子、 columns に展開したいキー、 values に数値を指定。
aggfunc(pivot_table 限定)
同じ (index, columns) の組合せに複数値があるときの集約関数。 デフォルト mean。 SSDSE は年・県で一意なので不要だが、 集約データなら必須。

🔬 数式を言葉で読み解く — 数式を「言葉」に翻訳

数式を眺めるだけでは身につかないので、 各記号がどんな役割を担っているかを言葉で押さえます。 「数式を音読する習慣」がつくと、 論文や教科書を読むスピードが体感で 2 倍ほど上がります。

変数 (variable)
測られたもの(例: 死亡率)
観測 (observation)
測られた単位(例: 2023年 北海道)
値 (value)
具体的な数値
melt
横→縦変換
pivot
縦→横変換
📚 補足:同じ記号でも分野・教科書によって意味が違うことがあります(例: $\hat{y}$ は予測値だが、 統計の文脈では推定量を意味することも)。 不明確なときは、 必ずその文書の記号定義表を確認しましょう。

🧮 実値で計算してみる

数式だけでは「実感」が湧きにくいので、 具体的な数値で 1 度手計算してみると理解が定着します。 以下の例は、 本サイトで扱う SSDSE-B-2026 や公開教材に近い形式で用意しました。

同じ情報の縦/横表現:

横持ち(wide)
県=東京, 人口=14000000, 高齢化率=23.0%, 死亡率=11.0‰
縦持ち(long)
県=東京, 指標=人口, 値=14000000
県=東京, 指標=高齢化率, 値=23.0
県=東京, 指標=死亡率, 値=11.0

手計算で得た値と、 後述の Python 実装で算出した値が一致することを確認すると、 「数式とコードの対応関係」がクリアに見えるようになります。

🧮 実値で計算してみる — 北海道の人口推移を 3 形式で見る

同じ「北海道の総人口」を 3 つの形式で並べてみます。 ぱっと見の印象がどう変わるかを体感してください。

形式 1: 元データ(wide、 年度行・指標列)

年度 Code Prefecture A1101 A1303 A4101 0 2023 R01000 北海道 5092000 1681000 24430 1 2022 R01000 北海道 5140000 1686000 26407 2 2021 R01000 北海道 5183000 1686000 28762 3 2020 R01000 北海道 5224614 1664023 29523 4 2019 R01000 北海道 5259000 1673000 31020

→ 「年度ごとに 1 行」「列 = 指標」。 SSDSE-B-2026 のデフォルト形式。 多変量回帰には便利。

形式 2: long(縦長、 1 観測=1 行)

年度 Code Prefecture 指標 値 0 2023 R01000 北海道 総人口 5092000 1 2022 R01000 北海道 総人口 5140000 2 2021 R01000 北海道 総人口 5183000 3 2023 R01000 北海道 高齢者人口 1681000 4 2022 R01000 北海道 高齢者人口 1686000 5 2023 R01000 北海道 出生数 24430

→ 「指標名」「値」が分離。 ggplot / seaborn / Tableau での可視化、 SQL の GROUP BY と相性最高。 行数は元の (指標数 × 年数 × 県数) 倍に増える。

形式 3: 時系列 wide(行 = 県、 列 = 年度)

Code Prefecture 2012 2013 ... 2022 2023 R01000 北海道 5465000 5438000 ... 5140000 5092000 R02000 青森県 1350000 1337000 ... 1204000 1184000 R03000 岩手県 1306000 1299000 ... 1181000 1163000 R04000 宮城県 2329000 2333000 ... 2280000 2264000 R05000 秋田県 1063000 1050000 ... 930000 914000 (47 行 × 12 列)

→ 「年が列」になり、 ヒートマップ表示や年次変化の比較に最適。 ただし変数の数が固定の年次に縛られるため、 新年度追加時に列追加が必要。

🧮 数式に値を入れて手で計算する: long/wide 変換行数

合成データで 5 顧客 × 4 月の売上を long と wide で表示し、 行数を比較する。

Step 1: wide 形式 (4 列)

顧客1月2月3月4月
c110151220
c25869

Step 2: long 形式

wide: 5 行 × 4 列 = 20 セル (顧客列除く) long: 5 × 4 = 20 行 (顧客, 月, 売上 の 3 列) 合計セル数: wide 25 (id列含む), long 60 (id+月+売上)

🐍 Python で再現

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import pandas as pd
wide = pd.DataFrame({
  '顧客':['c1','c2','c3','c4','c5'],
  '1月':[10,5,8,12,7],
  '2月':[15,8,9,14,10],
  '3月':[12,6,7,11,8],
  '4月':[20,9,10,15,12],
})
long = wide.melt(id_vars='顧客', var_name='月', value_name='売上')
print(f"wide 形状: {wide.shape}")
print(f"long 形状: {long.shape}")

📤 実行結果

wide 形状: (5, 5) long 形状: (20, 3)

💬 手計算 (Step 2) 20 行と Python 出力が完全一致。

🐍 Python 実装

公的統計(SSDSE-B-2026)を題材に、 最小限の Python コードで動作させます。 ファイルパス(data/raw/SSDSE-B-2026.csv)は自分の環境に合わせて変更してください。 まずはこのまま動かすことが理解の最短ルートです。

📥 入力例(SSDSE-B-2026 全体:564 行 × 112 列 = 47 都道府県 × 2012〜2023 年) 年度 地域コード 都道府県 A1101(総人口) A1303(65歳以上人口) A4101(出生数) … 2023 R01000 北海道 5,092,000 1,681,000 24,430 … 2023 R13000 東京都 14,086,000 3,205,000 86,348 … 2023 R47000 沖縄県 1,468,000 350,000 12,549 … …(残り 112 列は住宅・家計・教育・医療など)
1
2
3
4
5
6
7
8
9
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
# 横→縦
long = df.melt(id_vars=['SSDSE-B-2026','Code','Prefecture'], var_name='indicator', value_name='value')
print(long.head())

# 縦→横
wide = long.pivot_table(index=['SSDSE-B-2026','Code','Prefecture'], columns='indicator', values='value').reset_index()
📤 実行例(実測) SSDSE-B-2026 Code Prefecture indicator value 0 2023 R01000 北海道 A1101 5092000.0 1 2022 R01000 北海道 A1101 5140000.0 2 2021 R01000 北海道 A1101 5183000.0 3 2020 R01000 北海道 A1101 5224614.0 4 2019 R01000 北海道 A1101 5259000.0

▶ 実行 を押せばこのページの中でそのまま動きます(ライブラリもデータも同梱済みで、 準備は要りません)。 手元の Python に移して動かすときは pip install matplotlib pandas scikit-learn seaborn が必要です。 読んでいるデータは data/raw/SSDSE-B-2026.csv。 日本語を含むので encoding='cp932' の指定を落とさないでください。

本サイトの全コードは 論文一覧ページ から実例として確認できます。 自分のデータで試したい場合は、 列名・欠損記号・単位の違いだけ調整すれば、 ほぼそのまま流用できます。

👣 ステップバイステップ実例

「縦持ち・横持ち」を初めて使う方向けに、 ハンズオン的な実行手順を整理します。 上の Python 実装と組み合わせて、 1 度自分の手でなぞってみることを強く推奨します。

  1. 環境準備:このページのコードは ▶ 実行 ボタンでそのまま動くので、 まずは何も入れずに試す。 手元で動かしたくなったら Python 3.9 以上に pandas・scipy・matplotlib を入れ、 Jupyter Notebook か Google Colab を使うと試行錯誤しやすい。
  2. データ取得:本サイト題材の SSDSE-B-2026 を data/raw/ に配置(または自分のデータを用意)。 列名と単位を確認。
  3. 探索的に観察df.head()df.describe()df.isna().sum() で全体像を把握。 ここで欠損や外れ値の見当を付ける。
  4. 前提検証:縦持ち・横持ち をこのデータに当てはめてよいか(このページの「⚠️ よくある落とし穴」で挙げた 「型混在」の縦持ち・pivot の重複値エラー など)を確認。 NG なら別手法を検討。
  5. 本処理:上のコードブロックを参考に、 関数を呼び出して値を取得。 中間出力をその都度プリントして合っているか確認。
  6. 結果可視化:散布図、 棒グラフ、 ヒートマップなど、 解釈しやすい図を 1〜2 枚作る。 タイトルには結論を書く。
  7. 解釈・記録:「📝 レポートでの報告」の 5 点セットに沿って Notebook に書き残す。 後の自分のために結論・限界・次の一手を明記。
  8. 共有:Notebook を GitHub や Drive に置き、 関係者にレビュー依頼。 ピアレビューで穴が見つかることが多いので大事。

この 8 ステップを 1 度回すと、 「用語を読んで分かった気になる」段階から「実際に使える」段階に進めます。 知識は身体で覚えるのが結局のところ最速です。

🐍 Python 実装①:wide → long 変換(pd.melt)

このコードでやること:SSDSE-B-2026 の wide データから、 「年度・Code・Prefecture」を識別子に保持し、 総人口・高齢者人口・出生数の 3 指標を long 形式に変換する。

📥 入力:SSDSE-B-2026.csv(564 行 × 112 列)。 必要な 6 列に絞ってから melt。

1
2
3
4
5
6
7
8
9
10
11
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
sub = df[['SSDSE-B-2026', 'Code', 'Prefecture', 'A1101', 'A1303', 'A4101']].rename(
    columns={'SSDSE-B-2026': '年度',
             'A1101': '総人口', 'A1303': '高齢者人口', 'A4101': '出生数'})

df_long = sub.melt(id_vars=['年度', 'Code', 'Prefecture'],
                  var_name='指標', value_name='値')
print(f'wide shape: {sub.shape}  long shape: {df_long.shape}')
print(df_long.head(8))

📤 実行すると次の出力が得られる

wide shape: (564, 6) long shape: (1692, 5) 年度 Code Prefecture 指標 値 0 2023 R01000 北海道 総人口 5092000 1 2022 R01000 北海道 総人口 5140000 2 2021 R01000 北海道 総人口 5183000 3 2020 R01000 北海道 総人口 5224614 4 2019 R01000 北海道 総人口 5259000 5 2018 R01000 北海道 総人口 5293000 6 2017 R01000 北海道 総人口 5325000 7 2016 R01000 北海道 総人口 5355000

💬 結果の読み方:wide の 564×6 が long で 1692×5 に。 行数が 3 倍(指標数 3)になり、 列数が 1 つ減った(指標と値が 2 列に分割されたため)。 値列に異なる指標の数値が縦に並ぶ ── これを groupby('指標').mean() 等で扱うと指標横断の集計が一発で書ける。

🐍 Python 実装②:long → wide 変換(pivot_table、 年次クロス表)

このコードでやること:先ほどの sub(wide)から、 「行 = 都道府県、 列 = 年度、 値 = 総人口」のクロス表を作る。 ヒートマップ表示や時系列分析の前処理として定番。

📥 入力:先ほどの sub(564×6)。 pivot_table の index に Prefecture、 columns に年度、 values に総人口を指定。

1
2
3
4
5
6
7
8
wide = sub.pivot_table(index=['Code', 'Prefecture'],
                       columns='年度',
                       values='総人口')
print(f'pivot shape: {wide.shape}')
print(wide.head(5))
# 年次変化率(前年比)の計算が 1 行で済む
growth = (wide[2023] / wide[2012] - 1) * 100
print('2012→2023 人口変化率 (%) Top5 増加:\n', growth.nlargest(5).round(2))

📤 実行すると次の出力が得られる

pivot shape: (47, 12) 年度 2012 2013 ... 2022 2023 Code Prefecture R01000 北海道 5465000.0 5438000.0 ... 5140000.0 5092000.0 R02000 青森県 1350000.0 1337000.0 ... 1204000.0 1184000.0 R03000 岩手県 1306000.0 1299000.0 ... 1181000.0 1163000.0 R04000 宮城県 2329000.0 2333000.0 ... 2280000.0 2264000.0 R05000 秋田県 1063000.0 1050000.0 ... 930000.0 914000.0 2012→2023 人口変化率 (%) Top5 増加: Code Prefecture R13000 東京都 6.44 R47000 沖縄県 4.04 R14000 神奈川県 1.75 R11000 埼玉県 1.59 R12000 千葉県 0.92

💬 結果の読み方:1692×5 の long が、 47×12 の年次クロス表に変換された。 2023 年と 2012 年の比較が「wide[2023] / wide[2012]」と 1 行で書ける ── これが wide 形式の威力。 増加率トップは東京都 +6.44%、 次いで沖縄県 +4.04%・神奈川県 +1.75%・埼玉県 +1.59%・千葉県 +0.92% ── 増加は大都市圏と沖縄に限られ、 大半の県は人口減少。 long のままだと shift/lag が面倒だが、 wide なら列同士の演算で済む。

🐍 Python 実装③:long で groupby・集計・可視化

このコードでやること:long 形式の利点を活かし、 groupby('指標').agg([...]) で 3 指標すべての年次推移統計を一発で計算する。

📥 入力:先ほどの df_long(1692×5、 指標列に「総人口」「高齢者人口」「出生数」)。

1
2
3
4
5
6
7
# 年度・指標別の 47 県平均
summary = df_long.groupby(['年度', '指標'])['値'].agg(['mean', 'sum']).round(0)
print('== 2023 年の 47 県平均・総和 ==')
print(summary.loc[2023])

print('== 北海道の 12 年間平均・最大・最小 ==')
print(df_long[df_long['Prefecture'] == '北海道'].groupby('指標')['値'].agg(['mean', 'max', 'min']).round(0))

📤 実行すると次の出力が得られる

== 2023 年の 47 県平均・総和 == mean sum 指標 出生数 15474.0 727269 総人口 2645809.0 124353000 高齢者人口 770830.0 36229000 == 北海道の 12 年間平均・最大・最小 == mean max min 指標 出生数 32717.0 38686 24430 総人口 5297196.0 5465000 5092000 高齢者人口 1604034.0 1686000 1422000

💬 結果の読み方:2023 年の 47 県合計総人口 ≒ 1 億 2435 万人、 高齢者人口 ≒ 3623 万人 → 高齢化率 29.1%。 北海道の出生数は 12 年間で 38,686 → 24,430 と 約 37% 減少、 一方総人口は 7% 減にとどまる ── 出生数の減少速度が圧倒的に速いことが long の集計で見える。 long 形式は「指標横断のクロス集計」を groupby 一行で書けるのが強み。

🐍 Python 実装④:pd.wide_to_long(複数指標の wide → long)

このコードでやること:「人口_2012, 人口_2013, ...」のように 列名にメタ情報が埋まった wide データを、 pd.wide_to_long で long に変換する。 SSDSE のような列名規則化されたデータには非常に強力。

📥 入力:先ほどの wide pivot を MultiIndex で複数指標化したもの。 列名が「総人口_2012」「高齢者人口_2012」のように prefix_year 形式。

1
2
3
4
5
6
7
8
9
10
# まず「総人口_2012」のような prefix_year 列名の wide を作る
wide2 = sub.pivot_table(index=['Code', 'Prefecture'], columns='年度',
                         values=['総人口', '高齢者人口'])
wide2.columns = [f'{a}_{b}' for a, b in wide2.columns]
wide2 = wide2.reset_index()

long2 = pd.wide_to_long(wide2, stubnames=['総人口', '高齢者人口'],
                       i=['Code', 'Prefecture'], j='年度', sep='_').reset_index()
print(f'wide2 shape: {wide2.shape}  long2 shape: {long2.shape}')
print(long2.head())

📤 実行すると次の出力が得られる

wide2 shape: (47, 26) long2 shape: (564, 5) Code Prefecture 年度 総人口 高齢者人口 0 R01000 北海道 2012 5465000 1422000 1 R01000 北海道 2013 5438000 1469000 2 R01000 北海道 2014 5410000 1519000 3 R01000 北海道 2015 5381733 1558387 4 R01000 北海道 2016 5355000 1602000 5 R01000 北海道 2017 5325000 1632000

💬 結果の読み方:47×26 列(識別子 2 + 指標 2 × 12 年)の wide が、 564×5 列(識別子 2 + 年度 + 指標 2)の 「半 long」 になった。 完全 long と違い、 指標は 列として残る。 これが SSDSE のような縦軸(時間)に沿った分析に最適な形式。 完全 long にしたいなら、 続けて melt を 1 回かければよい。

melt / pivot / stack / unstack / wide_to_long の使い分け

関数方向使う場面
pd.melt(df)wide → long最汎用。 列名を 1 つの「指標」列に集約
df.stack()wide → longMultiIndex 前提、 縦長 Series を返す
pd.pivot_tablelong → wide集約関数を指定可能、 重複に強い
df.unstack()long → wideMultiIndex の階層を列に展開
pd.wide_to_longprefix_year → long列名にメタ情報が埋まったデータに最適

🐍 Python 実装⑤:long で seaborn 折れ線、 wide で imshow ヒートマップ

long と wide のどちらが 可視化 に向いているかは、 図の種類で変わります。 seaborn / ggplot は long を好み、 matplotlib の imshow や Excel風ヒートマップは wide が直接使える。

このコードでやること:先ほどの df_long から 5 県(北海道・東京都・大阪府・沖縄県・秋田県)の総人口推移を sns.lineplot で 1 行描画する。 また同じデータを wide にして imshow でヒートマップ。

📥 入力df_long(1692×5)から「指標==総人口」「Prefecture in 5 県」に絞った 60 行。

1
2
3
4
5
6
7
8
9
10
11
12
13
import seaborn as sns
import matplotlib.pyplot as plt

prefs = ['北海道', '東京都', '大阪府', '沖縄県', '秋田県']
d5 = df_long[(df_long['指標']=='総人口') & (df_long['Prefecture'].isin(prefs))]

# long で折れ線(hue で自動色分け、 1 行で済む)
sns.lineplot(data=d5, x='年度', y='値', hue='Prefecture')
plt.title('5県の総人口推移'); plt.savefig('line.png'); plt.close()

# wide にしてヒートマップ
heat = d5.pivot_table(index='Prefecture', columns='年度', values='値')
print('heat shape:', heat.shape); print(heat.round(-3))

📤 実行すると次の出力が得られる

heat shape: (5, 12) 年度 2012 2013 2014 ... 2021 2022 2023 Prefecture 北海道 5465000 5438000 5410000 ... 5183000 5140000 5092000 大阪府 8861000 8856000 8845000 ... 8806000 8782000 8763000 東京都 13234000 13307000 13399000 ...14010000 14038000 14086000 沖縄県 1411000 1419000 1426000 ... 1468000 1468000 1468000 秋田県 1063000 1050000 1037000 ... 945000 930000 914000

💬 結果の読み方:long のままだと sns.lineplot(hue='Prefecture') で都道府県ごとに色分けされた折れ線が 1 行で描ける。 wide にすると imshow(heat) で 5×12 のヒートマップが描ける(東京都が右肩上がり、 秋田県が右肩下がり)。 「同じデータでも、 図の種類で適切な形が違う」を体感できる。

🛠️ 実務でよく使う long/wide 変換パイプライン

「データ受領 → 形を確認 → 必要なら変換 → 集計・可視化・モデル化」が実務の典型フロー。 SSDSE-B-2026 で具体的なステップを確認します。

ステップ 1: データ確認(形を診断)

df.shape → (564, 112) df.columns[:5] → ['SSDSE-B-2026', 'Code', 'Prefecture', 'A1101', 'A110101'] df.dtypes → SSDSE-B-2026: int64, A1101: int64, ... df.head(2) → 1 行に「1 年度×1 県×多数指標」 → wide 形式と判定

ステップ 2: 目的を 1 文で言う

ステップ 3: 必要列に絞ってから変換

112 列を全部 melt するとメモリと可読性が爆発。 まず必要列だけに df[['年度','Code','Prefecture','A1101','A1303']] のように絞り、 別名にリネームしてから変換するのが鉄則。

ステップ 4: 変換後の NaN・重複を点検

ステップ 5: 集計・可視化・モデル化

long なら groupbyseaborn、 wide なら列演算や imshow。 用途に応じて使い分けます。 変換は双方向に何度でもできるので、 「迷ったらまず形を変えてみる」と発想を切り替えられる。

よくある業務シナリオ

シナリオ受領形式操作目的推奨
エクセルから受領(横持ち)wide統計ソフトに渡すmelt で long 化
RDB から受領(縦持ち)longエクセル集計pivot_table で wide 化
API JSON(ネスト構造)複雑解析・可視化json_normalize → long
時系列データ(年が列)wideトレンド比較そのまま使うか long 化
ML 特徴量long学習データ作成pivot で wide 化

🐍 Python 実装⑥:long → wide でモデル特徴量を作る

機械学習モデルの入力には「行 = 1 サンプル、 列 = 特徴量」の wide が必須。 long のままでは sklearn に渡せない。 ここでは SSDSE-B-2026 の都道府県別データから、 「過去 12 年の人口推移を特徴量とした 2023 年予測モデル」を作るための変換を実演。

このコードでやること:long → wide で「行 = 都道府県、 列 = 各年の総人口」の特徴量行列を作り、 2023 年の総人口を回帰で予測する練習。

📥 入力df_long(1692×5、 指標==総人口に絞ると 564 行)。

1
2
3
4
5
6
7
8
9
10
11
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score, mean_absolute_error

pop = df_long[df_long['指標'] == '総人口'].pivot_table(
    index='Prefecture', columns='年度', values='値')
X = pop[[2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022]]
y = pop[2023]
m = LinearRegression().fit(X, y)
yp = m.predict(X)
print(f'R^2 = {r2_score(y, yp):.4f}, MAE = {mean_absolute_error(y, yp):,.0f} 人')
print('X shape:', X.shape, ' y shape:', y.shape)

📤 実行すると次の出力が得られる

R^2 = 1.0000, MAE = 889 人 X shape: (47, 11) y shape: (47,)

💬 結果の読み方:long を pivot で wide 化することで「47 県 × 11 年の特徴量」と「47 県の 2023 年人口」という ML に直接使える形になった。 R²=1.0 / MAE=889 人 は 強相関の時系列を扱うと過学習傾向(前年の値が翌年をほぼ決める)。 実務では train/test 分割や時系列 CV、 特徴量の差分化が必要。 大事なのは「形を変えるだけで ML に渡せる」という事実。

long → wide の応用パターン

🧭 エッジケース集 — こんなとき long / wide はどうする

ケース A: 複数指標を同時に pivot したい

pivot_table(values=['総人口', '高齢者人口'], ...) のように複数指定すると、 結果の列は MultiIndex(指標 × 年度)になる。 平坦化するには df.columns = [f'{a}_{b}' for a,b in df.columns]

ケース B: 重複キーで pivot がエラー

df.pivot ではなく df.pivot_table(aggfunc='mean') を使う。 重複の理由を理解した上で、 mean / sum / count などを業務に合わせて選択。

ケース C: 疎データ(多くの NaN)

疎データを wide にすると NaN だらけになり、 メモリも食う。 そのまま long で保持し、 scipy.sparse 経由で疎行列にするのが現実解。

ケース D: 階層的(MultiIndex)データ

df.stack(level=...) / df.unstack(level=...) で指定階層だけを展開・畳み込む。 県 × 年 × 性別の 3 階層なら、 「性別だけ列に出す」のような部分変換が便利。

ケース E: 文字列と数値が混在

melt すると「値」列は全部 object 型になる(数値と文字列が混在するため)。 数値だけ抽出してから melt するか、 melt 後に pd.to_numeric(errors='coerce') で変換。

ケース F: 年度が文字列「2023年」

「2023年」のような文字列を pivot の columns に使うと、 並びがアルファベット順になり時系列が崩れる。 df['年度'] = df['年度'].str.replace('年','').astype(int) で数値化してから pivot。

ケース G: 観測単位が混ざっている

県データと市データを 1 つのテーブルに混ぜないこと(tidy 原則 3)。 別テーブルにして、 必要に応じて concatmerge で結合。

ケース H: 縦持ちの SQL DB から long を取り出す

RDB の正規形は基本 long に近い。 SELECT prefecture, year, metric, value FROM ssdse_long で取り出し、 Python 側で pivot するか、 SQL の PIVOT 構文(Oracle/SQL Server)を使う。

📌 最終チェック — 1 ページサマリ

long / wide 変換は単なる「形の変更」ではなく、 問題を解きやすくする思考の切り替え です。 本ページで身に付けた感覚を 1 ページにまとめます。

long の哲学

wide の哲学

学習者へのメッセージ

「データを受け取ったら、 まず形を疑う」が本ページで一番伝えたいこと。 wide で受け取って long で集計、 long で受け取って wide で ML、 という 双方向の変換 ができる状態を目指してください。 SSDSE-B-2026 のような実データで何度も練習すれば、 自然に身に付きます。

本ページの 6 つのコードブロックは、 順番に Jupyter のセルに貼り付ければそのまま動きます(SSDSE-B-2026.csv を data/raw/ 配下に置くこと)。 「読むだけ → 動かして読む → 改造して読む」の 3 段階で、 long / wide 変換が「呼吸のように」できるようになります。

次のステップは、 pandasgroupby / merge / resample を体系的に押さえ、 tidy data 原則を時系列・パネルデータ・テキストデータに適用していくこと。 本ページはその起点です。

📋 pandas long/wide チートシート

よく使うイディオムを 1 ページに集約。 印刷して机に貼っておくと作業効率が上がります。

A. wide → long

コード用途
df.melt(id_vars=['id'])最汎用、 id 以外を縦化
df.melt(id_vars=['id'], value_vars=['a','b'])特定列だけ縦化
df.set_index('id').stack()MultiIndex 経由で縦化
pd.wide_to_long(df, stubnames=['pop'], i='id', j='year', sep='_')prefix_suffix 列名規則

B. long → wide

コード用途
df.pivot(index='id', columns='var', values='val')基本、 重複不可
df.pivot_table(index='id', columns='var', values='val', aggfunc='mean')重複を集約
df.pivot_table(index='id', columns='var', values=['val1','val2'])複数指標を同時 pivot
df.set_index(['id','var']).unstack('var')MultiIndex 経由で wide 化

C. 中間処理

コード用途
df.columns = [f'{a}_{b}' for a,b in df.columns]MultiIndex 列名の平坦化
df.reset_index()index を通常列に戻す
df.rename(columns={'old':'new'})列名のリネーム
df.dropna() / df.fillna(0)NaN 処理
df.drop_duplicates()重複行を削除

D. SSDSE-B-2026 専用イディオム

コード用途
pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])日本語列名を保持して読み込み
df[df['SSDSE-B-2026'] == 2023]特定年抽出
df.pivot_table(index=['Code','Prefecture'], columns='SSDSE-B-2026', values='A1101')県×年の人口クロス表
df.melt(id_vars=['SSDSE-B-2026','Code','Prefecture'])全 109 指標を long 化

E. パフォーマンス Tips

F. 他言語・他フレームワークの対応

環境wide → longlong → wide
pandas (Python)df.melt()df.pivot_table()
tidyr (R)pivot_longer()pivot_wider()
polars (Python)df.melt()df.pivot()
SQL (Oracle/SQL Server)UNPIVOTPIVOT
Sparkmelt() (3.4+)groupBy().pivot()
Excelパワークエリ「ピボット解除」ピボットテーブル機能

どの環境でも概念は同じ。 pandas で身に付けた感覚は他言語にそのまま転用できる。 R を主に使う人は pivot_longer / pivot_wider、 SQL を主に使う人は UNPIVOT / PIVOT を覚えておけば十分。

G. 学習者向け練習問題(SSDSE-B-2026)

  1. SSDSE-B-2026 から「2023 年 47 都道府県の総人口」を wide で抽出し、 「人口が多い順」に並べたランキング表を作成せよ
  2. 同じデータを long にして、 「都道府県別の 12 年間平均総人口」を groupby で計算せよ
  3. 総人口の年次クロス表(行=県、 列=年)から、 「2012-2023 で人口が増加した県」をリストアップせよ
  4. 3 指標(総人口・高齢者人口・出生数)を long 形式にして、 各指標の年次トレンドを seaborn で 1 図に重ねよ
  5. 「県 × 年 × 指標」の MultiIndex を作り、 unstack で「指標を列に出す」変換を試せ

解答例は本ページの Python 実装①〜⑥に大半が含まれています。 すべてコピペで動くので、 まず動かしてから自分で書き直す ── これが習得の近道です。

H. よくある質問

Q. long と wide はどっちが「正しい」?
A. どちらも正しい。 用途次第。 tidy data の理論では long を「分析の基本形」とするが、 表示や ML 特徴量では wide が必要。 双方向に変換できる状態が理想。
Q. SQL の正規形と long は同じ?
A. 概念的に近い。 RDB の第 1 正規形は「列の中に複数値を持たない」で、 long に近い性質。 ただし完全に同じではなく、 long はファクトテーブル(観測値)に近い。
Q. ExcelからPythonに移行したい人へ
A. まず「Excel の表 = wide」だと意識。 Python に取り込んだら、 集計目的なら melt で long 化、 表示目的なら wide のまま。 ピボットテーブル機能 = pandas の pivot_table と完全に対応。

📘 long/wide 変換の深掘り(melt / pivot / wide_to_long)

long と wide の往復は pandas の melt / pivot / wide_to_long の 3 つで完結する。 題材は SSDSE-B-2026 の年次パネル(都道府県 × 年 × 指標)で、 wide は「年が列」、 long は「年が行」に対応する。 実際に動かせるコード例は上の「🐍 Python 実装①〜⑥」がそのまま雛形になる(すべて SSDSE-B-2026 の実データ・実測値で動作を確認済み)。 ここでは「どこに変換するか」「列名の意味は何か」「欠損はどう扱うか」という判断軸を、 対応表と原則として整理しておく。

A. pandas / R / tidyverse 対応表(決定版)

変換方向pandastidyversereshape2 (旧)
wide → longdf.melt(id_vars=...)pivot_longer()melt()
long → widedf.pivot(index=, columns=, values=)pivot_wider()dcast()
列名にパターン(年など)pd.wide_to_long(df, stubnames=, i=, j=)pivot_longer(names_pattern=)
集計付き wide 化pivot_table(aggfunc=)pivot_wider(values_fn=)dcast(..., fun.aggregate=)

SSDSE-B-2026 で「年度・Code・Prefecture」を id_vars に、 A1101(総人口)などの指標列を縦に積むのが melt、 それを「行=Prefecture・列=年度」に戻すのが pivot、 「総人口_2012」のような列名規則を一括で縦持ちにするのが wide_to_long ── という対応になる(実演は Python 実装①〜④)。

B. 目的別・long / wide 早見表

やりたいこと 向いている形式 理由
2 変量の散布図を描く横持ち (wide)列をそのまま x/y に渡せる
同じ変数のヒストグラム縦持ち (long)フィルタ後に hist() 一発
グループ別箱ひげ図縦持ち (long)seaborn の x/y/hue 引数に合う
相関係数の行列計算横持ち (wide)df.corr() がそのまま使える
時系列プロット (複数系列)縦持ち (long)sns.lineplot(x='年', y='値', hue='指標')
機械学習モデルの入力 (sklearn)横持ち (wide)特徴量列が並ぶ X 行列形式が前提
GroupBy 集計 (mean/sum)縦持ち (long)「指標 × カテゴリ」の集計が直感的
人間が表として読む横持ち (wide)「1 行 = 1 主体」で目で追いやすい
DB に保存 (正規化)縦持ち (long)新しい指標追加で列を増やさず済む

C. melt / pivot の落とし穴(dtype・NaN・重複)

D. tidy data と「観測単位」の関係

Hadley Wickham が 2014 年に提唱した tidy data(整然データ)の 3 原則は、 縦持ち形式と深く関係する。

  1. 各変数は 1 つの列を成す: 「総人口」「年少人口」「高齢者人口」のような変数は、 それぞれ独立した 1 列とする。 1 列に 2 つの情報を混ぜない。
  2. 各観測は 1 つの行を成す: 「北海道の 2023 年の総人口」のような個々の観測が、 それぞれ 1 行ずつ独立して並ぶ。
  3. 各観測単位は 1 つのテーブルを成す: 「都道府県別データ」と「市区町村別データ」は別テーブルに分け、 必要に応じて join する。

SSDSE-B-2026 の元の形式は「1 行 = 年度 × 都道府県、 列に各指標」という横持ちで、 「観測単位 = (年度, 都道府県)」と捉えれば tidy として成立する。 しかし「観測単位 = (年度, 都道府県, 指標)」と捉え直すと、 縦持ちのほうが tidy になる。 つまり tidy か否かは「何を観測単位とみなすか」に依存し、 縦持ちが絶対的に tidy なわけではない。

E. 縦持ち・横持ちのメモリ効率

一般に「列数が少なく行数が多い縦持ち」は、 文字列の id 列(都道府県名・指標名など)が繰り返されるため、 同じ情報でも横持ちよりサイズが大きくなりやすい。 反対に横持ちは「疎な観測」では NaN が並んでメモリを浪費する。

形式 セル数 id 列の繰り返し 疎データの扱い 向くサイズ規模
横持ち (wide)行 × 変数列少ない (1 回ずつ)NaN が増える密 (dense)、 変数 << 観測
縦持ち (long)行 × 3 列 (id, 変数, 値)多い (各観測ごと)観測のある行だけで済む疎 (sparse)、 変数 >> 観測
疎行列 (sparse matrix)非ゼロ要素のみなし最も効率的超疎 (TF-IDF, 推薦)

ユーザー × 商品の購買履歴のように 99% が 0 になる超疎データは、 scipy.sparse の CSR/CSC 形式(実質的に「(行, 列, 値)」のトリプレット=縦持ちに近い)で保持するのが定石だ。

F. SQL・Excel・R tidyr との対応

縦持ち ⇄ 横持ちの概念は言語を越えて共通で、 呼び名だけが異なる。 pandas で身に付けた感覚はそのまま転用できる。

操作 pandas R (tidyr) SQL Excel
横持ち → 縦持ちmelt() / stack()pivot_longer()UNPIVOTパワークエリ「ピボット解除」
縦持ち → 横持ちpivot() / pivot_table()pivot_wider()PIVOT / GROUP BY + CASEピボットテーブル機能
複数指標の縦持ち化wide_to_long()pivot_longer(names_sep=...)複数列の UNION

Excel ピボットの (行, 列, 値, 集計方法) は、 pandas の pivot_table の (index, columns, values, aggfunc) と 1 対 1 に対応する。 SQL で縦持ち保存しておけば、 集計の都度 GROUP BY + CASE WHEN で必要な横持ちに整形できる。

G. データパイプラインでの縦横の使い分け

実務のデータパイプラインでは、 工程ごとに縦持ち・横持ちの使い分けが定石化している。

工程 推奨形式 理由
データ収集 (API・センサー)縦持ち新指標追加が容易、 スキーマ変更不要
データ保管 (DB・データレイク)縦持ち or 正規形疎なデータでもメモリ効率が良い
前処理・クレンジング縦持ち欠損値・型変換が groupby で楽
探索的データ解析 (EDA)両方使い分け可視化は縦、 相関は横
特徴量エンジニアリング横持ち列演算 (差分・比率) が直感的
モデル学習 (sklearn)横持ちX 行列 = サンプル × 特徴量が必須
結果の可視化 (seaborn / plotly)縦持ちx/y/hue が縦持ち前提
ダッシュボード・レポート横持ち人間が読むには横持ちが直感的

H. 歴史的経緯(Tidy Data の系譜)

縦持ち・横持ちの概念は、 リレーショナルデータベース理論(1970 年代の E.F. Codd による正規化理論)まで遡る。 正規化された DB テーブル(第 3 正規形以降)は基本的に縦持ち(または狭い横持ち)の形を取る。 一方、 統計学やビジネス分析の現場では「観測ごとに 1 行、 変数ごとに 1 列」という横持ちが古くから定着し、 SPSS・SAS・Excel はすべて横持ち入力を前提に設計されている。

流れが変わったのは 2014 年、 Hadley Wickham による「Tidy Data」論文(Journal of Statistical Software)の発表だ。 これにより「縦持ち = 整然データ = 解析向き」という思想が広まり、 R の tidyr や Python の pandas(特に melt)に強い影響を与えた。 現在は「収集時は縦持ちで保存、 解析時に必要に応じて横持ちに変換」が標準パターンになっている。

日本語の「縦持ち・横持ち」は、 業務システム開発で「データを縦方向に持つか横方向に持つか」というレイアウト判断が頻出したことに由来する。 英語圏では「long format / wide format」「stacked / unstacked」「molten / cast」など複数の呼び名があるが、 概念は同じである。 ライブラリごとに用語が違う点にだけ注意したい。

🎮 触って理解する — long ⇄ wide 変換シミュレータ

下の 2 つの表はまったく同じ情報です(SSDSE-B-2026 実測値:総人口 A1101、4 都道府県 × 2021〜2023 年)。 セルをタップ(クリック)すると、 long の 1 行 ⇄ wide の 1 セルという対応関係が相互にハイライトされます。 「melt」「pivot」ボタンで変換の様子をアニメーションで再生できます。

縦持ち(long)— 1 行 = 1 観測
総人口
横持ち(wide)— 指標×年が列に展開
202120222023
結合キーの違い:long は (県, 年) の 2 列がキー。 wide は 1 列がキーで、 「年」という情報は列名の中に埋まっている
セルをタップすると、 対応するセルがもう一方の表でハイライトされます。

📈 long 形式からは折れ線グラフが「直接」描ける

下のグラフは、 上の long 配列をそのまま「県」列でグループ化して描いたもの(系列 = グループ列の値)。 seaborn なら sns.lineplot(data=long, x='年', y='総人口', hue='県') の 1 行に対応します。 wide 形式だと列ごとにループを書く必要があり、 県が増えるたびにコードが変わってしまいます。 long 表の行やグラフの点をタップすると相互にハイライトされます。

🎨 直感 — 「同じ情報の 2 つの並べ方」

上で体感した通り、 melt / pivot は情報を増やしも減らしもしません。 値セルはどちらも 12 個で、 変わるのは「観測を行方向に積むか、 列方向に広げるか」というレイアウトだけ。 long は「値 1 つにつき住所ラベル(県・年)を毎行書く」ので冗長だが機械に優しく、 wide は「ラベルを行見出し・列見出しに 1 回だけ書く」ので人間の目に優しい ── これが両形式のトレードオフの核心です。

⚠️ よくある落とし穴(変換で起きること)

🚀 発展 — pandas melt / pivot と tidy data

この操作は pandas では pd.melt(wide→long)と pivot / pivot_table(long→wide)、 R tidyverse では pivot_longer / pivot_wider に対応します。 「1 変数 = 1 列、 1 観測 = 1 行」を満たす long 形式は 整然データ(tidy data)の考え方そのもので、 データフレーム上の集計可視化クレンジングのすべてが滑らかになります。 実務の定石は「保存・処理は long、 提出・閲覧の直前だけ wide」です。

⚠️ よくある落とし穴

縦持ち・横持ち変換で頻発する失敗は、 (1) 型混在で value 列が object 化、 (2) 同一 (key, var) の重複で pivot 失敗、 (3) ID 列を value に紛れ込ませて時系列が乱れる、 の 3 つに集約されます。 Wickham の Tidy Data 原則 (各変数=1 列、 各観測=1 行) を出発点にして、 melt → 集約 → pivot の順を守れば回避できます。

❌ 「型混在」の縦持ち
数値と文字列を同じ value 列に入れると型が object に。 指標ごとに別テーブルが安全。
❌ pivot の重複値エラー
縦持ちで同じ (key, var) が複数あると pivot が失敗。 aggfunc で集約。
❌ 「人間用と機械用を混同」
レポートは wide、 分析は long を意識的に切り替える。
❌ 単位混在
縦持ちで「人口(人)」と「率(%)」が同列に並ぶと意味を失う。 単位列を別に。
🛡 防御策まとめ:「適用条件を確認する」「結果と前提をセットで記述する」「不確実性を必ず併記する」の 3 点を習慣化すれば、 上記の罠の大半は回避できます。

⚠️ long / wide 変換でハマる 6 連発

❌ ① pivot で重複キーがあると ValueError
同じ (index, columns) ペアに複数値があると df.pivot はエラーで止まる。 pivot_table(aggfunc='mean') なら集約してくれる。 SSDSE は年×県で一意なので OK だが、 顧客 ID × 日付など重複が出る場面では必須。
❌ ② long にすると NaN が大量発生
疎なデータを melt すると、 観測のない (識別子 × 指標) 組合せに NaN が現れる。 dropna() で消すか、 そもそも疎データには long のまま扱う方が良い。
❌ ③ メモリ消費が long で爆発
100 指標 × 1000 万行を long にすると 10 億行。 SSDSE 規模なら問題ないが、 大規模データでは要注意。 必要な指標だけ抽出してから melt するのが鉄則。
❌ ④ 列名の型を失う
pivot 後の列名は 文字列 になりがち(2023 → '2023')。 数値演算をしたいなら df.columns = df.columns.astype(int) で戻す。
❌ ⑤ index と column の役割を逆にする
「都道府県を index に、 年度を columns に」が時系列で自然だが、 ヒートマップによっては逆の方が見やすい。 用途に応じて .T(転置)で入れ替える。
❌ ⑥ wide → long 後に元の順序を忘れる
long にすると行順が変わり、 元の時系列の並びが崩れる。 必ず sort_values(['識別子', '年度']) で並べ直してから時系列処理へ。

🗺 概念マップ

関連概念を視覚的に整理した概念マップ。

data long wide pd.melt (wide→long) pd.pivot (long→wide) tidy data 原則 groupby + agg seaborn long 入力 時系列 panel data

縦持ち (long) と横持ち (wide) は同じ情報の異なる表現で、 中央ノード「観測単位 × 変数」を起点に、 long 形式は「(都道府県, 年, 指標, 値)」の 4 列、 wide 形式は「都道府県を行・年を列」の交差表として展開される。 pandas の melt / pivot が両者を結ぶ可逆操作で、 集計・可視化・回帰モデル投入時には用途に応じて使い分ける。

🔗 隣接手法への橋渡し

long/wide 変換は EDA → 可視化 → モデリングの結節点であり、 隣接領域と密接につながる:

long ⇄ wide 変換は EDA → 可視化 → モデリングのパイプラインの結節点。

🌳 手法選択フロー

「long と wide のどちらで持つか」は次工程で決まる。 3 段で判定する。

  1. Step 1: 次工程は何か?
    • seaborn の relplot / catplot / lineplotlong 形式 (hue/col に列名を指定する設計)
    • Excel への貼付・人間の目視確認 → wide 形式 (年×指標のクロス表が読みやすい)
    • statsmodels の固定効果回帰 (PanelOLS) → long 形式 (MultiIndex (entity, time))
  2. Step 2: 値列の同質性は?
    • 全列が同じ単位・型 (例: 各年の人口) → pd.melt(id_vars='都道府県', var_name='年', value_name='人口') で long 化
    • 列ごとに単位が異なる (総人口/面積/年平均気温) → wide のまま、 long 化すると単位混在で集計事故
  3. Step 3: 欠損と重複は?
    • pivot 時に (index, columns) の組合せが重複 → pivot_table(aggfunc='mean') で集約か、 重複を解消
    • long → wide で欠損年が NaN に → fillna(0)interpolate() を意図的に選択

原則: 「分析は long、 表示は wide」。 SSDSE-B-2026 (年×都道府県×指標) は元から long に近い構造なので、 melt 不要なケースが多い。

📝 補足:直感を精密化する — 「個体」と「観測」の言葉づかい

本ページ冒頭の「🎨 直感で掴む」を、 言葉の解像度を 1 段上げて言い直しておく。 long / wide の混乱の多くは「行が何を表すか」の取り違えに由来するので、 ここを固めると以降の落とし穴が一気に見通せる。

💡 tidy との関係を 1 文で:long は「観測単位=(個体, 変数) の粒度」まで細かく割った形で、 このとき「1 変数 1 列・1 観測 1 行」を満たす。 ただしlong なら自動的に tidy data というわけではない。 「観測単位を何に取るか」で tidy か否かが決まる点は、 本ページ「D. tidy data と『観測単位』の関係」で詳述した通り。 SSDSE-B-2026 は「観測単位=(年, 県)」と見れば元の wide のままで tidy、 「観測単位=(年, 県, 指標)」と見れば long が tidy になる。

📝 補足:落とし穴の深掘り(SSDSE-B-2026 実測で検証)

本ページには既に「⚠️ よくある落とし穴」「long / wide 変換でハマる 6 連発」「C. melt / pivot の落とし穴」があるが、 ここでは特に事故が多い 5 点を、 SSDSE-B-2026(564 行 × 112 列、 うち指標 109 列)の実測値で裏取りしながら掘り下げる。

① id_vars 指定漏れ ── 識別子が「値」に溶ける

df.melt() を引数なしで呼ぶと、 全 112 列が無差別に縦積みされる。 SSDSE-B-2026 での実測は次の通り(本ページの Python 実装が動く環境でそのまま再現できる)。

df.melt() → shape (63168, 2) value dtype: object ← 事故 df.melt(id_vars=['SSDSE-B-2026','Code','Prefecture']) → shape (61476, 5) value dtype: float64 ← 正解

指定漏れ版では Prefecture が「変数」として 564 回 value 列に混入し、 文字列と数値が同居するため value 列が object 型に落ちる(=mean() 等の数値計算が即エラー)。 正しく識別子 3 列を id_vars に渡すと value は float64 のまま。 「melt したら真っ先に df['value'].dtype を見る」を癖にする。 型の話は 型変換 も参照。

② 欠損の非対称性 ── long は「消える」、 wide は「湧く」

同じ欠測でも long と wide で挙動が真逆になる。 これは「⚠️ よくある落とし穴(変換で起きること)」で触れた点の両方向の整理である。

③ 列名と値の混同 ── 列見出しは「隠れたデータ」

wide の列名「2021」「2022」「2023」は、 実は「年」という変数の値である。 列名に埋まった情報は groupby のキーにも結合キーにもできず、 集計のたびに melt で「救出」する羽目になる。 列名が「総人口_2021」のように複数の情報を連結していると救出はさらに厄介で、 pd.wide_to_longstr.split での分解が要る(本ページ Python 実装④が実演)。

④ 型・単位の混在 ── 平均が無意味になる long

「人口(人)」と「高齢化率(%)」のように単位の違う指標を 1 本の value 列に積むと、 groupby('指標').mean() は指標ごとには正しいが、 指標をまたいだ value.mean() は物理的に無意味な数になる。 long 化するときは「値が同質か」を必ず問う。 本ページ「手法選択フロー Step 2」の判定軸がこれに当たる。

⑤ 可視化 / 分析で「どちらが要るか」の取り違え

最後は形式の選択ミスそのもの。 seabornlineplot(hue=...) や箱ひげは longdf.corr()sklearnX 行列は wide が前提。 「図が描けない・モデルに渡せない」の大半は、 手法の要求形式を確認せずに変換した取り違えが原因。 可視化 の入力仕様を先に見てから形を決めるとムダな往復が減る。

📝 補足:発展 — パネルデータ・stack/unstack・正規化との関係

long / wide を「単なる整形」で終わらせず、 統計モデル・データベース理論と接続すると、 なぜ long が分析の基本形とされるのかが腑に落ちる。

A. パネルデータは long が「正準形」

SSDSE-B-2026 は「都道府県 i × 年 t」のパネルデータそのもの。 パネル分析のツールは long を要求する ── statsmodelsPanelOLSMultiIndex(entity, time)、 すなわち「(個体, 時点) を行に積んだ long」を入力形式とする。 固定効果モデルや パネル因果推論(差分の差分など)は、 個体 i と時点 t がそれぞれ 1 列として存在する long でこそ「個体ダミー」「時点ダミー」を素直に組める。 wide のままでは t が列名に埋もれてしまい、 これらのモデルに渡せない(落とし穴③の帰結)。

B. stack / unstack は melt / pivot の MultiIndex 版

本ページの対応表でも触れた通り、 stack/unstack は「インデックス階層と列階層を入れ替える」操作で、 melt / pivot の MultiIndex 特化版と捉えると迷わない。

C. 「正規化」との関係 ── 用語の衝突に注意

long 形式はデータベースの正規化と本質的に近い。 wide の「年ごとに繰り返す列(2012, 2013, …)」は、 リレーショナル理論でいう反復グループであり、 これを行に畳んで (個体, 年, 値) にする melt は、 第 1 正規形へ向かう整形とほぼ同義。 リレーショナルデータベースの正規形テーブルが自然と long(ファクトテーブル)に寄るのはこのため。

⚠️ 用語の衝突:ここでいう「正規化」はDB のスキーマ正規化であって、 特徴量を平均 0・分散 1 に揃える標準化(統計・ML の「正規化 / スケーリング」)とはまったく別概念。 文脈で読み分けること。

D. pivot と one-hot エンコーディングの地続き

long のカテゴリ列を pivot で列展開すると、 各カテゴリが 0/1(または値)の指示列になる ── これは one-hot エンコーディングと地続きの発想である。 「指標を列に出す」pivot、 「カテゴリを列に出す」one-hot、 いずれも値を列見出しへ昇格させる同じ操作の顔違い。 疎になりやすい点も共通で、 超疎なら scipy.sparse(本ページ E 節参照)に逃がすのが定石。