論文一覧に戻る 📚 用語集トップ 🗺 概念マップ
📚 用語解説
📚 用語解説
構造化データ
Structured Data
データエンジニアリング

🔖 キーワード索引

数式を言葉で読み解く SSDSE-B-2026 実値計算 Python 実装 4 要素 産業活用 6 件 関連手法比較 失敗例 演習 5 問 関連用語辞典 10 語 参考文献 拡張ハンドブック 50 連発レシピ FAQ 20 問

💡 30秒で分かる結論

🍰 まずはやさしく

整理整頓された表のようなデータです。

データの分析や前処理に使いやすいためです。

テストの点数をまとめた成績表のようなものです。

このデータの特徴と使い方を学びます。

表形式など定まった構造のデータ

structured data を 30 秒で把握する重要ポイント:

📍 あなたが今見ているもの

🍰 まずはやさしく

行と列できれいに並んだデータのことです。

データの種類をはっきりさせるために使います。

スマホの連絡先リストのような形式です。

このページで扱うデータの形を説明します。

あなたは今、 構造化データ (Structured Data) という、 行と列で整然と並び、 各列に明確なデータ型が定義された形式のデータの用語ページを見ています。 SSDSE-B-2026 (47 都道府県 × 12 年度 = 564 行 × 112 列の CSV) はその典型例で、 RDB / SQL / pandas DataFrame で扱える基本形式です。 本ページの多くの例は最新の 2023 年度に絞った 47 行の断面で説明します。 非構造化データ (画像・音声・自由文) との対比で理解します。

🎨 直感で掴む

🍰 まずはやさしく

決まった枠に値が入っているデータです。

計算や分析をスムーズに行うために使います。

部活の出席簿のように整理された表のことです。

なぜこの形が便利なのかを解説します。

構造化データは「行 = 観測、 列 = 変数」のように事前に決まった枠(スキーマ)に値が入っているデータ。 SSDSE-B-2026 はその典型で、 564 行(47 都道府県 × 12 年度 2012–2023)× 112 列(先頭列 = 年度、 続いて Code・Prefecture・A1101 総人口・A1303 65 歳以上人口・A4101 出生数・B4101 年平均気温・L3221 消費支出 …)の CSV は pd.read_csv() 1 行で読めて、 すべての値が「どの行のどの列か」一意に決まる。

対極にあるのが「自由記述のアンケート文」「画像」「ログテキスト」などの非構造化データで、 そのままでは SUM や AVG が効かず、 まず構造化が必要。 構造化されていれば SQL、 pandas、 BI ツール、 機械学習の入力にそのまま使える。 「ML プロジェクトの 8 割の時間はデータ構造化に消える」と言われる所以である。

🎮 触って理解する: 同じ 3 件の記録を「構造化 / 半構造化 / 非構造化」で操作して比べる

下のデモは、 まったく同じ情報(3 件の顧客購入記録、 架空データです)を 3 つの形式で並置したものです。 「検索」「集計」「ソート」ボタンを押すと、 3 つの形式それぞれに同じ操作を試みます。 構造化(表)は即座に正確な答えを返しますが、 非構造化(自由文)は単純な文字列検索しかできず、 表記揺れ(りんご / リンゴ / 林檎)で漏れ、 集計もソートも不能である様子を体感してください。 中間の半構造化(JSON)は「パースすれば処理できるが、 スキーマの保証は自前」という立ち位置です。

操作方法: ① 検索語を選んで「🔍 検索」 ② 「Σ 合計金額を集計」 ③ 「↕ 金額でソート」 ④ 「↺ リセット」。 下のグラフの棒に指やマウスで触れる(なぞる)と、 その行の詳細が表示されます。
✅ 構造化 (表) — 列の意味を機械が知っている
No 顧客 商品表記 商品コード 数量 単価 金額
ボタンを押すとここに結果が出ます。
🔶 半構造化 (JSON) — 構造はあるが保証は自前

      
パース (構文解析) すれば表と同じ処理が可能です。
❌ 非構造化 (自由文) — 機械には「ただの文字列」
文字列としての検索しかできません。

📊 構造化側の「金額」列 (リアルタイム更新・触れると詳細表示)

グラフの棒に触れる (タッチ / マウス) と、 その記録の詳細が出ます。

🗝 スキーマ: 「構造化 = 機械が列の意味を知っている」の正体

構造化側が正確に検索・集計・ソートできるのは、 次のスキーマ (列の名前・型・意味の約束事) が事前に決まっているからです。 「りんご」と「林檎」を同一視できたのも、 表記揺れを吸収する 商品コード 列をスキーマ設計の段階で用意してあったからです。

列名意味 (機械が「知っている」こと)
顧客文字列誰が買ったか。 グループ化キーに使える
商品コード文字列 (コード体系)表記揺れを吸収した商品の一意 ID
数量整数個数。 足し算してよい
単価整数 (円)1 個の値段。 数量と掛けてよい
金額整数 (円) = 数量 × 単価合計・平均・ソートのキーに使える

非構造化の文にも同じ情報は「書いてある」のに、 機械には 「佐藤さんがりんごを3個…」 のどこが数量でどこが単価か分かりません。 これが「構造化 = 検索・集計・ソートがアルゴリズム的に定義できる状態」という本ページの主張の、 いちばん小さな実例です。 SSDSE-B-2026 (564 行 × 112 列) が pd.read_csv() 1 行で分析可能になるのも、 同じ理屈です。

🎨 直感: 「行と列の約束事」があるだけで機械は賢くなる

上のデモが示すとおり、 構造化データの本質は「値そのもの」ではなく値の置き場所と意味の約束事 (スキーマ) にあります。 約束事があるから、 プログラムは中身を読まなくても「3 列目は足してよい数」と決め打ちで処理できる。 逆に言えば、 非構造化データの困難は情報が無いことではなく、 情報の位置と型が毎回違うことにあります。 デモの「五個」(漢数字) のように、 人間には自明でも機械には別物、 という揺れが集計を破壊します。

⚠️ よくある落とし穴: 構造化のコストとスキーマ変更

🚀 発展: 半構造化・非構造化を「構造化する」技術

📐 定義

🍰 まずはやさしく

決まった構造を持つデータの定義です。

データエンジニアリングの基本として使います。

買い物リストのように項目が決まっている形式です。

このデータの詳しいルールについて読みます。

表形式など定まった構造のデータ

英語名 Structured Data

🎯 いつ・どこで使うか

📋 前提条件・適用範囲

この用語を理解・使用するときは、 次のような前提を意識してください:

📐 形式定義の徹底: 関係モデルから現代の DataFrame まで

構造化データ (Structured Data) は、 Edgar F. Codd (1970) の関係モデル (Relational Model)を起点とし、 50 年以上にわたって精緻化されてきた概念です。 ここでは数学的な定義を 4 段階に分け、 SSDSE-B-2026 を例に逐次具体化します。

レベル 1: スキーマ (schema) — 列の名前と型の集合

スキーマ $S$ は属性 (列) の有限集合 $\{A_1, A_2, \ldots, A_n\}$ で、 各属性 $A_i$ に値域 (ドメイン) $D_i$ が割り当てられたものです。

$$S = \{(A_1, D_1), (A_2, D_2), \ldots, (A_n, D_n)\}$$

SSDSE-B-2026 のスキーマは、 たとえば $\{(\text{年度}, \text{Integer}), (\text{Code}, \text{String}), (\text{Prefecture}, \text{String}), (\text{A1101}, \text{Integer}), \ldots\}$ という形で 112 個の属性が並んでいます。

レベル 2: タプル (tuple) — 1 行

タプル $t$ はスキーマ $S$ に従う 1 つの観測単位、 すなわち各属性に値を割り当てた写像です。

$$t : S \to \bigcup_i D_i, \quad t(A_i) \in D_i$$

SSDSE-B-2026 の先頭データ行「年度=2023, Code=R01000, Prefecture=北海道, A1101=5,092,000, …」は、 112 個の属性 → それぞれの値域への写像。

レベル 3: リレーション (relation) — テーブル全体

リレーション $r$ はタプルの有限集合です。 順序を持たず、 重複も持たない (数学的集合の性質)。

$$r \subseteq D_1 \times D_2 \times \cdots \times D_n, \quad |r| = N$$

SSDSE-B-2026 全体では $N=564$ (47 都道府県 × 12 年度)、 列数 $n=112$。 2023 年度だけに絞れば $N=47$ になる。 集合性から各行は本来順序を持たないが、 実用上は 年度・Code 順 (R01000→R47000) でソートされている。

レベル 4: スキーマ + リレーション = データベース

複数のリレーションをスキーマとともに束ね、 主キー・外部キーで結合した全体がデータベース $DB$ です。

$$DB = \{(S_1, r_1), (S_2, r_2), \ldots, (S_m, r_m), \text{IC}\}$$

$\text{IC}$ は整合性制約 (Integrity Constraints): 主キー一意性、 外部キー参照、 NULL 制約、 ドメイン制約など。 SSDSE-B-2026 が単一テーブルでも、 e-Stat の他指標と Code で結合すれば多テーブル DB を構成できる。

📐 正規形 (1NF, 2NF, 3NF, BCNF) を SSDSE-B-2026 で読み解く

1NF: セル原子性

「すべてのセルが分割不能な値を 1 つだけ持つ」。 SSDSE-B-2026 はカンマや改行が混じらない → 1NF を満たす。 NG 例: "東京都, 神奈川県" のような複合値。

2NF: 部分関数従属の排除

主キーが複合キーのとき、 「主キーの一部だけから決まる属性」を別表に分離する。 SSDSE-B-2026 は 12 年度分を含むため主キーは (年度, Code) の複合キーで、 Prefecture 名は Code だけで決まる → これは部分関数従属で厳密には 2NF 違反。 都道府県マスタ (Code, Prefecture) を分離すれば 2NF を満たす。

3NF: 推移的従属の排除

「Code → 地方区分 → 地方コード」のような連鎖を、 中間表 (都道府県マスタ・地方マスタ) に分割。 SSDSE-B-2026 は地方区分を持たないので 3NF も自動成立、 しかし e-Stat の他データと結合すると問題化することがある。

BCNF: より厳しい 3NF

「すべての関数従属の左辺が候補キー」。 SSDSE-B-2026 の候補キーは (年度, Code) だが、 Prefecture が Code だけ (候補キーの真部分集合) で決まるため厳密には BCNF を満たさない。 都道府県マスタを分離すれば BCNF になる。

実務的指針: BCNF まで正規化するのは OLTP (オンラインのトランザクション処理)、 OLAP (分析) では 3NF を意図的に崩した「スター・スキーマ」「スノーフレーク・スキーマ」が標準。 SSDSE-B-2026 のような分析用統計データは、 すでに 564 行 (47 都道府県 × 12 年度) のフラットな形で配布されており、 BI ツールで即扱える。

📊 構造化データの規模別アーキテクチャ 4 階層

規模行数の目安標準ツールSSDSE-B との対応
小規模 (~10⁴)数十〜数万Excel / Google SheetsSSDSE-B-2026 は 47 行で完全にこの範囲
中規模 (~10⁶)数十万〜数百万pandas / DuckDB / SQLiteSSDSE-B を 10 年 × 1741 市区町村に展開した場合
大規模 (~10⁹)数千万〜数十億PostgreSQL / BigQuery / SnowflakeEC サイトの注文明細、 IoT 月次ログ
超大規模 (~10¹²)兆オーダーSpark / Databricks / Iceberg / LakehouseSNS の全ユーザ行動ログ、 衛星測定値

構造化データの本質は規模が変わっても不変ですが、 ツールと SLA (Service Level Agreement) が大きく変わります。 SSDSE-B-2026 で学んだ概念は、 ツールを差し替えれば 10⁹ スケールまでそのまま通用するのが強みです。

📐 結合の cardinality を数式で読み解く

SSDSE-B-2026 と SSDSE-C-2026 を Code で結合するとき、 結果の行数は次の式で決まります。

$$|r_1 \bowtie r_2| = \sum_{k} |r_1[K=k]| \cdot |r_2[K=k]|$$

数式を言葉で読み解く: 「結合キー $K$ の各値 $k$ について、 $r_1$ 側の該当行数と $r_2$ 側の該当行数を掛け、 全 $k$ で合計」。 SSDSE-B-2026 では Code は両表ともユニーク (1 行/Code) なので、 各 Code で $1 \times 1 = 1$、 合計 47 行。

4 種類の cardinality

pandas では pd.merge(b, c, on='Code', validate='one_to_one') と書くと、 N:N 結合の意図しない膨張を MergeError で早期検出できる。

🔬 数式を言葉で読み解く (詳細版)

構造化データは数学的には「リレーション (関係)」として定義されます。 集合 $D_1, D_2, \ldots, D_n$ を各列の値域 (ドメイン) とすると、 $n$ 列のテーブルは次の部分集合です:

$$R \subseteq D_1 \times D_2 \times \cdots \times D_n$$

記号の読み方

記号意味SSDSE-B-2026 での具体例
$R$リレーション (テーブル)SSDSE-B-2026 CSV 全体 (564 行)
$D_i$第 i 列の値域A1101 列 = 正の整数 (人口)
$n$列数 (アリティ)112 列
$\times$デカルト積列の組合せ集合
行 (tuple)1 観測単位1 行 = 1 都道府県の全指標

直感的な読み下し

SSDSE-B-2026 では「Code (都道府県コード)」が主キーとして 47 個の行を一意に識別します。 各列は型 (整数 / 浮動小数 / 文字列) が固定で、 「セル」(行 × 列の交差点) には 1 個の値しか入りません (第一正規形 1NF)。 この約束が成り立つから、 ソート / フィルタ / 集計 / 結合がアルゴリズム的に定義可能になります。

🔬 関連手法比較表

形式スキーマ代表例分析しやすさ
構造化事前定義済 (固定列・型)CSV / RDB / Parquet★★★★★
半構造化柔軟 (ネスト可)JSON / XML / YAML★★★☆☆
非構造化なし画像 / 音声 / 自由文★☆☆☆☆ (前処理必須)
時系列時刻 + 値の最小構造センサー・株価★★★★☆
グラフノード + エッジSNS / 知識グラフ★★★☆☆ (特殊 DB)
階層親子関係のツリー組織図 / ファイルシステム★★★☆☆

💔 構造化データ の失敗例 5 連発

  1. セル内のカンマ: Prefecture 列に「東京都,渋谷区」のようなカンマ入り文字列があり、 CSV パース失敗。 対策: 事前にエスケープ処理 (引用符)、 別列分割。
  2. 型不統一: A1101 列に「N/A」「-」のような非数値が混じり、 全列 object 型に。 対策: read_csv の na_values 指定、 事後 to_numeric(errors='coerce')
  3. 主キー重複: Code 列に同じ値が 2 行あり、 集計や結合で重複が膨らむ。 対策: df['Code'].duplicated().sum() で検知、 重複の意味を確認。
  4. 列ヘッダの全角・半角混在: 「Code」「CODE」など同義列が別々に存在。 対策: ヘッダ正規化 (str.normalize('NFKC'))。
  5. 欠損のサイレント無視: 計算で NaN を 0 に置換し、 「測定値 0」と区別不能に。 対策: fillna(0) 前に欠損意味を判断、 別列で欠損フラグ保持。

📝 演習問題 5 問

  1. Q1: SSDSE-B-2026 の shape は? (答: (564, 112)。 12 年度分を含む。 2023 年度に絞ると (47, 112))
  2. Q2: Code 列は単独で一意か? (答: いいえ。 各 Code が 12 年度分あるので重複する。 一意なのは (年度, Code) の複合キー)
  3. Q3: A1101 列の型は? (答: int64 (人口))
  4. Q4: 構造化データの第 1 正規形条件を 1 行で答えよ (答: すべてのセルに値が 1 つだけ入る (原子性))
  5. Q5: 構造化データの最大の利点は? (答: スキーマが既知なので SQL / pandas で機械的に処理できる)

📖 関連用語辞典 10 語

テーブル
構造化データの最小単位。 行 × 列。
データベース
構造化データを永続化する仕組み。
SQL
構造化データへの問合せ言語。
主キー
行を一意に識別する列。
外部キー
他テーブルを参照する列。
メタデータ
構造化データの「データの説明」。
Tidy data
1 観測 1 行の正規形。
CSV
構造化データの可搬テキスト形式。
JSON
半構造化の代表 (ネスト可)。
欠損値
構造化でも値の不在は発生する。

🔬 数式を言葉で読み解く: 関係代数の 6 つの基本演算

構造化データに対する操作は、 関係代数 (Relational Algebra) の 6 つの基本演算で完全に表現できます。 SSDSE-B-2026 を題材に、 数式と pandas / SQL の対応を逐次見ていきます。

① 選択 (Selection) $\sigma$

$$\sigma_{\phi}(r) = \{t \in r \mid \phi(t)\}$$

数式を言葉で読み解く: 「条件 $\phi$ を満たす行 $t$ だけをリレーション $r$ から選ぶ」。 SSDSE-B-2026 では「人口 (A1101) が 500 万人以上の都道府県のみ」が典型例。 pandas では df[df['A1101'] >= 5_000_000]、 SQL では WHERE A1101 >= 5000000

② 射影 (Projection) $\pi$

$$\pi_{A_1, A_2, \ldots, A_k}(r) = \{(t(A_1), t(A_2), \ldots, t(A_k)) \mid t \in r\}$$

数式を言葉で読み解く: 「指定した属性 (列) だけを残し、 他を捨てる」。 SSDSE-B-2026 で「Code, Prefecture, A1101 の 3 列だけ抜き出す」のが射影。 pandas: df[['Code','Prefecture','A1101']]、 SQL: SELECT Code, Prefecture, A1101

③ 和 (Union) $\cup$

$$r_1 \cup r_2 = \{t \mid t \in r_1 \lor t \in r_2\}$$

数式を言葉で読み解く: 「2 つのリレーションを縦に積む (重複を除去)」。 同一スキーマ必須。 たとえば SSDSE-B-2026 と SSDSE-B-2025 を縦結合して「2 年分の 94 行」を作る。 pandas: pd.concat([df2026, df2025]).drop_duplicates()、 SQL: UNION

④ 差 (Difference) $-$

$$r_1 - r_2 = \{t \mid t \in r_1 \land t \notin r_2\}$$

数式を言葉で読み解く: 「$r_1$ にあって $r_2$ にない行」。 たとえば「2026 年に新登場した都道府県 (実際にはない) は?」を調べる時に使う。 pandas: ~df['Code'].isin(df_other['Code'])、 SQL: EXCEPT

⑤ 直積 (Cartesian Product) $\times$

$$r_1 \times r_2 = \{(t_1, t_2) \mid t_1 \in r_1, t_2 \in r_2\}$$

数式を言葉で読み解く: 「全 47 都道府県 × 全 47 都道府県 = 2,209 ペア」のように全組合せを作る。 単独ではほぼ使わず、 後述の結合 (Join) の理論基盤として重要。 pandas: df.assign(key=1).merge(df.assign(key=1), on='key')

⑥ 結合 (Join) $\bowtie$

$$r_1 \bowtie_{\phi} r_2 = \sigma_{\phi}(r_1 \times r_2)$$

数式を言葉で読み解く: 「直積を作ってから条件 $\phi$ で絞る」。 たとえば SSDSE-B-2026 と SSDSE-C-2026 (生活時間データ) を Code で結合すると「人口 × 生活時間」の指標が並ぶ。 pandas: pd.merge(b, c, on='Code')、 SQL: JOIN ... ON

定理: 関係代数の 6 演算 ($\sigma, \pi, \cup, -, \times, \bowtie$) を組合せれば、 SQL の SELECT-FROM-WHERE-GROUP BY-HAVING に相当する任意の問合せを表現可能 (関係完備性 (Relational Completeness))。 構造化データの真の威力は、 この代数的閉包性にあります。

🔬 数式を言葉で読み解く: 関数従属とアーマストロングの公理

構造化データのスキーマ設計を支える理論が 関数従属 (Functional Dependency, FD)です。 属性集合 $X$ から $Y$ への関数従属を $X \to Y$ と書きます。

$$X \to Y \iff \forall t_1, t_2 \in r:\; t_1[X] = t_2[X] \Rightarrow t_1[Y] = t_2[Y]$$

数式を言葉で読み解く: 「2 つの行で $X$ の値が同じなら、 $Y$ の値も必ず同じ」。 SSDSE-B-2026 では Code → Prefecture (コードが決まれば県名が決まる) が成立し、 逆 (Prefecture → Code) も成立する (両者は 1:1)。

アーマストロングの公理 3 つ

この 3 公理は「健全 (sound)」かつ「完全 (complete)」、 つまり関係から導かれる FD はすべてこの 3 つで導出可能 (1974 年 Armstrong)。 スキーマ設計ツールは内部でこの公理を使って正規形を判定しています。

📈 構造化データの可視化 8 パターン (SSDSE-B-2026 適用)

パターン用途SSDSE-B での例
棒グラフ47 件の量的比較人口ランキング
散布図2 列の関連人口 vs 病院数
箱ひげ図分布の概観高齢化率の分布
ヒートマップ高次元一覧47 × 110 指標の z-score
地理マップ空間分布都道府県別人口密度
折れ線時系列2018-2023 年の人口推移
階層図 (treemap)構成比関東/関西/その他の人口シェア
並列座標 (parallel)多次元の同時比較人口/出生/死亡/転入の 4 軸

🔬 さらに掘る: 構造化データを「保つ」ための実務技法

構造化データは「最初に作るとき」よりも「変更が積み重なって崩れていくとき」のほうが脆い。 ここでは構造を維持するための 5 つの実務技法を、 SSDSE-B-2026 を例にまとめる。

技法 1: スキーマ駆動の読み込み (dtype 明示)

SSDSE-B-2026 の Code 列は文字列「R01000」だが、 pd.read_csv の型推論が int64 に誤判定する可能性がある (R01000→1000)。 これを防ぐには pd.read_csv(..., dtype={'Code': str}) と明示する。 構造化を保つには「型を推論に任せず、 スキーマで宣言する」のが鉄則。

技法 2: 主キー検証の自動化 (assert)

読み込み直後に assert df['Code'].is_unique and not df['Code'].isna().any() を必ず書く。 SSDSE-B-2026 で 47 行が誤って 48 行に増えた場合、 assert で即座に検出できる。 構造化データのパイプラインは「主キー検証」を入口で必ず行うのが標準作法。

技法 3: 列名の正規化 (snake_case 統一)

SSDSE-B-2026 は A1101・F3101 などの暗号列名だが、 実務では別途データ定義書を参照しないと意味が読めない。 取り込み後に df.rename(columns={'A1101':'pop_total','F3101':'job_seekers_new'}) と意味付きの snake_case に正規化すると、 構造化データの「機械が読みやすく」かつ「人が読みやすい」両立が達成できる。

技法 4: 単位列の併設 (千人・百万円など)

SSDSE-B-2026 の A1101 (総人口) は「人」、 F3101 (新規求職申込件数) は「件」、 L3221 (消費支出) は「円」だが、 これは列名から読めない。 構造化を意味的に保つには、 別途「単位メタデータ表 (col_name, unit, description)」を作り、 列名と単位を主キー結合できるようにする。 これにより、 6 列以上の表を扱ったときに単位の混同 (千人と人を間違える) を防げる。

技法 5: 派生列の型保持 (round と astype)

高齢化率を df['A1303']/df['A1101'] で派生させると、 結果は float64 (0.3957..) になる。 「比率は 3 桁丸めで表現する」と決めたら、 df['aging_ratio'] = (df['A1303']/df['A1101']).round(3) と即座に丸めると後段の集約結果も決定論的になる。 派生列の型と精度を明示することは、 構造化データの再現性を保つ最後の砦である。

📝 R660 まとめ

🔎 拡張補足: 構造化データの現代的設計とパイプライン

1. ELT vs ETL の選択

従来は ETL (Extract-Transform-Load): 抽出 → 変換 → ロード だったが、 クラウドデータウェアハウス (Snowflake, BigQuery, Redshift) の登場で ELT (Extract-Load-Transform) が主流に。 生データをそのまま倉庫にロードし、 SQL で必要時に変換。 SSDSE-B-2026 を題材にすれば、 (1) CSV をそのまま BigQuery にアップロード、 (2) 必要な列・行を View で抽出、 (3) 分析用 Mart を CREATE TABLE で作成、 という流れ。 dbt (data build tool) が ELT の標準ツール。 ETL 時代はデータエンジニアが変換ロジックを書き、 分析者は完成形を消費するだけだったが、 ELT 時代は分析者自身が SQL で変換を書く「アナリティクスエンジニア」という新しい職種が生まれた。 SSDSE のような公的統計データを題材に、 (1) 生 CSV を読み込み、 (2) 都道府県マスタと結合し、 (3) 年度別集計テーブルを作る、 という一連の流れを SQL で書く練習をするのが、 ELT 時代の典型的な学習パスである。

2. スキーマ設計の原則

(1) 第 3 正規形 (3NF): 主キー依存・関数従属を整理、 (2) 非正規化: 分析向けに意図的に冗長性を持たせる、 (3) スタースキーマ: ファクトテーブル + ディメンションテーブル、 (4) スノーフレーク: ディメンション正規化、 (5) Data Vault: 履歴保持に最適。 SSDSE-B-2026 のような集計済データは「スタースキーマのファクト」として扱える。 業務系 (OLTP) では 3NF で重複を排除し更新異常を防ぐが、 分析系 (OLAP) ではスタースキーマで JOIN を減らしクエリを高速化するのが定石。 SSDSE-B-2026 を例にすれば、 (1) 都道府県ディメンション (都道府県コード、 名称、 地域区分)、 (2) 年度ディメンション (年、 元号)、 (3) 指標ディメンション (指標コード、 単位、 カテゴリ)、 (4) ファクト (都道府県 × 年 × 指標 × 値) という設計が考えられる。 ファクトテーブルは「狭く長い」(long format) 構造で BI ツールとの相性がよい。

3. データレイク・データレイクハウス

(1) データレイク: S3/Azure Blob/GCS に生データ (Parquet, Avro, ORC) を保存、 schema-on-read で柔軟性、 (2) データレイクハウス: Databricks Delta Lake, Apache Iceberg, Apache Hudi で ACID トランザクション・スキーマ進化・タイムトラベル を実現。 SSDSE-B-2026 のような小規模データではオーバースペックだが、 概念理解は重要。 データレイクは「とにかく溜める」思想で、 schema-on-read (読むときにスキーマを当てる) なので生データの欠損・型ゆれを許容する。 一方データレイクハウスは「溜めつつ整合性も担保」する第三世代で、 Iceberg は AWS, GCP, Databricks すべてに統合され、 オープンテーブルフォーマットの事実上の標準になりつつある。 SSDSE 程度のサイズでも、 (1) raw 層に生 CSV、 (2) bronze 層に Parquet 変換、 (3) silver 層にクレンジング済テーブル、 (4) gold 層に分析用集計、 という medallion architecture を試すと、 大規模実務に通じる設計感覚が身につく。

4. Parquet 形式の優位性

列指向 (columnar) 圧縮形式で、 分析クエリで大幅高速化。 CSV (37 MB) → Parquet (約 5 MB) と 1/7 程度のサイズ。 SSDSE-B-2026 を Parquet に変換するなら pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]).to_parquet('SSDSE-B-2026.parquet')。 PyArrow, Polars, DuckDB はネイティブ Parquet サポート。 列指向のメリットは (1) 列単位圧縮で同じ型が連続するため圧縮率が高い、 (2) 列単位読み込みで必要なカラムだけ読める (predicate pushdown)、 (3) 統計情報 (min/max/null 数) が含まれパーティション枝刈り可能、 (4) スキーマ情報内蔵で型の自動推論不要、 という 4 点。 CSV を毎回読み込んでパースするコストが構造化データ分析のボトルネックになっていることが多く、 一度 Parquet に変換するだけで体感速度が 5-10 倍になることが多い。 SSDSE-B-2026 を Parquet 化して DuckDB で SQL を投げるのが、 2024-2025 年現在の「最速の構造化データ分析パイプライン」と言ってよい。

5. データ品質と監視

Great Expectations, dbt tests, Soda Core で「データ品質テスト」を自動化。 (1) 列の存在、 (2) NULL 比率、 (3) 値の範囲、 (4) 一意性、 (5) 参照整合性 を継続的に検証。 SSDSE-B-2026 を扱う場合、 (1) 都道府県は 47 件存在、 (2) 人口は正の整数、 (3) 高齢化率は 0-1 の範囲、 などをテスト化。 Great Expectations は Python ベースで「期待値 (Expectations) を宣言的に書く」発想、 dbt tests は YAML で書け SQL に組み込める、 Soda Core はクラウド版が SaaS で提供される、 という棲み分け。 データ品質問題は「いつの間にか入った 1 件の NULL」「型が int から str に変わった」「期待値の範囲を超えた」「上流の改名で結合が壊れた」など多岐にわたり、 検出が遅れるほど下流の被害が大きい (Garbage In, Garbage Out)。 SSDSE-B-2026 のような小さく安定したデータでも、 (1) 47 都道府県揃っているか、 (2) 数値カラムに文字列が混入していないか、 (3) 年度が連続しているか、 をテスト化する習慣をつけると、 大規模実務に通じる。

6. dbt によるアナリティクスエンジニアリング

dbt は SQL ベースの ETL/ELT フレームワーク。 (1) models/ に SQL ファイル、 (2) tests/ にデータ品質テスト、 (3) docs/ に自動生成ドキュメント、 (4) seeds/ に CSV データ。 SSDSE-B-2026 を dbt で扱う場合、 (1) seeds に CSV、 (2) staging models で型変換・カラム正規化、 (3) marts で分析用テーブル、 (4) tests で品質保証、 という構成。 dbt の革新性は「SQL に Jinja テンプレートを混ぜることで再利用性とテスト性を持たせた」点にあり、 ref () マクロでモデル間の依存関係を自動解決、 docs generate でテーブル間 lineage を可視化、 dbt build で依存順に実行 + テスト、 が一気通貫で動く。 SSDSE-B-2026 を題材にした学習プロジェクトとしては、 (1) staging_ssdse_b で生 CSV を型変換、 (2) dim_prefecture で都道府県ディメンション、 (3) fct_population で人口ファクト、 (4) mart_aging_analysis で高齢化分析、 という 4 層構成が典型例。

7. ストリーミングデータと構造化

Kafka, Apache Pulsar, AWS Kinesis でリアルタイムストリーミング。 Schema Registry (Confluent, AWS Glue Schema) でスキーマ管理。 Avro, Protobuf, JSON Schema が主要なスキーマ形式。 SSDSE のような年次データには直接該当しないが、 自治体センサー・IoT データ・SNS データなどリアルタイム構造化データは現代的な実務トピック。 ストリーミング処理では「スキーマの後方互換性」が極めて重要で、 producer が新しい列を追加しても古い consumer が壊れない、 古い producer が送る古い形式を新しい consumer が読める、 という二方向の互換性を Schema Registry が管理する。 Avro は Hadoop エコシステム由来でスキーマ進化に強い、 Protobuf は gRPC との相性がよく型安全性が高い、 JSON Schema は人間可読性が高い、 という棲み分け。 SSDSE-B-2026 を「擬似ストリーム」(年度ごとに 1 レコードずつ送る) として扱う練習でも、 スキーマ管理の感覚は身につく。

8. Polars と DuckDB の台頭

pandas の代替として、 (1) Polars (Rust 製、 並列処理、 lazy evaluation で高速)、 (2) DuckDB (組込 OLAP DB、 SQL ベース、 CSV/Parquet 直接読み込み)、 (3) Modin (pandas API + 並列 backend)、 (4) cuDF (GPU 加速 pandas)。 SSDSE-B-2026 程度のサイズではどれも瞬時だが、 大規模データでは pandas より圧倒的に高速。 Polars の強みは (1) Arrow を内部表現に持ち pandas との変換が高速、 (2) lazy API で複数操作を統合最適化、 (3) Rust 由来のマルチスレッド処理、 (4) メモリ効率が良く OOM になりにくい、 の 4 点。 DuckDB の強みは (1) SQLite 並に組込が簡単、 (2) CSV/Parquet/JSON を直接 FROM 句で参照可能、 (3) PostgreSQL 互換に近い SQL、 (4) Arrow との高速統合。 SSDSE-B-2026 を Polars で読むなら pl.read_csv('SSDSE-B-2026.csv', encoding='cp932')、 DuckDB なら duckdb.sql("SELECT * FROM 'SSDSE-B-2026.csv'").df() で一発。

9. データカタログとガバナンス

(1) Apache Atlas, AWS Glue Data Catalog, Google Data Catalog でメタデータ管理、 (2) Lineage tracking で変換履歴可視化、 (3) Access control で行・列レベル権限、 (4) PII 検出で個人情報自動マスキング。 SSDSE-B-2026 は公開データだが、 元データ (国勢調査原票) は個情法対応必要。 データカタログの本質は「データの所在地・意味・所有者・更新頻度・品質スコアを横断検索できる仕組み」で、 大企業では数万テーブルが散在し誰も全体像を把握できない状況を解消する。 Lineage (系譜) は「このダッシュボードのこの数値は、 どのソーステーブルのどの列から、 どの変換を経て来たか」を辿る機能で、 障害時の影響範囲特定や監査対応で必須。 SSDSE-B-2026 を題材に「都道府県」列が複数テーブルでどう使われているかを Lineage で可視化する練習をすると、 大規模環境でのガバナンス感覚が身につく。

10. 構造化データと AI

2024-2025 年、 「LLM × 構造化データ」の研究進展: (1) Text-to-SQL (自然言語からクエリ生成)、 (2) SQL-to-Text (クエリ結果の説明文生成)、 (3) Schema 推論 (生データから schema 自動生成)、 (4) Anomaly detection (異常値・ドリフト検出)。 SSDSE-B-2026 で「東京の人口は」と聞くと LLM がクエリ生成 → 実行 → 自然言語回答、 という流れが実現しつつある。 Text-to-SQL のベンチマークとして Spider, BIRD, WikiSQL があり、 GPT-4, Claude 3, Gemini はいずれも 80% を超える精度を達成。 ただし「列名が意味的に曖昧 (例: code が都道府県コードか商品コードか)」「JOIN 条件が複雑」「集約と窓関数の使い分け」などは依然として難しく、 LLM 単体ではなく「LLM + スキーマ理解 + 実行確認 + 自己修正」のエージェント構成が現実的解。 SSDSE-B-2026 のような小規模スキーマは Text-to-SQL の練習に最適。

11. SSDSE-B-2026 を題材にした実践

(1) 生 CSV を S3/MinIO にアップロード、 (2) Parquet 変換 (PyArrow)、 (3) DuckDB でクエリ、 (4) dbt で staging/marts モデル、 (5) Great Expectations で品質テスト、 (6) Streamlit/Dash でダッシュボード、 という End-to-End パイプラインを構築できれば、 構造化データの実務スキルが身につく。 47 都道府県の小規模データでも、 同じ手順を学べる教材になる。 さらに発展として、 (7) Airflow/Prefect/Dagster でジョブスケジューリング、 (8) MLflow で機械学習モデル管理、 (9) Feature Store (Feast, Tecton) で特徴量再利用、 (10) Model Serving (Seldon, BentoML) で推論 API、 までを SSDSE-B-2026 で素振りすると、 データエンジニア・ML エンジニア・MLOps エンジニアの三役を一通り体験できる。 規模は小さくても「設計の型」を身につけることが学習の本質で、 後に大規模データを扱うときに同じパターンを適用できる。

12. 構造化 vs 半構造化 vs 非構造化

(1) 構造化 (RDBMS, CSV, Excel): 行列形式、 スキーマ厳格、 (2) 半構造化 (JSON, XML, YAML): 階層構造、 スキーマ柔軟、 (3) 非構造化 (テキスト、 画像、 動画、 音声): スキーマなし、 ML/NLP で構造化に変換。 SSDSE-B-2026 は完全に構造化、 JSON 変換も可能 (df.to_json())、 テキスト変換 (Markdown レポート) は人手 or LLM。 実務データの推定構成比は「非構造化 80% + 半構造化 15% + 構造化 5%」とも言われ、 構造化データだけ扱えても全体の 5% しかカバーできない。 ただし「非構造化を構造化に変換する」過程 (OCR, ASR, NLP, CV) の出力先は必ず構造化テーブルになるので、 構造化データの設計スキルはあらゆるデータパイプラインの最終段で必須。 SSDSE-B-2026 を入口に構造化を極めた後、 PDF → テキスト → 構造化、 画像 → 物体検出結果 → 構造化、 という流れに発展させると、 現代的なマルチモーダル分析の全体像が見えてくる。

13. 締めくくり: 構造化データはデータサイエンスの基盤

構造化データの設計・実装・運用・品質保証は、 データサイエンス・ML エンジニア・データエンジニアすべての職種の共通基盤。 SSDSE-B-2026 のような実データを使って ELT パイプライン、 dbt モデル、 品質テスト、 ダッシュボードを End-to-End で構築する経験が、 現代のデータ職にとって最も価値あるスキルセットを形作る。 統計検定・データサイエンス検定・G 検定・E 資格などの資格学習だけでは身につかない「実装の手触り」こそが、 SSDSE-B-2026 のような公的統計を題材にしたハンズオン学習の真価。 47 都道府県という親しみやすいスケールで全工程を経験し、 そのまま大規模実務にスケールさせる、 というのが本教材の目指す学習動線である。 構造化データの基礎を固めた読者は、 続いて「リレーショナルデータベース」「データウェアハウス」「データレイク」「特徴量エンジニアリング」などの関連用語ページに進むと、 知識のネットワークが一気に広がる。

🧮 SSDSE-B-2026 実値で計算

SSDSE-B-2026 の構造を実際に検証します。 行数・列数・型を確認し、 1 つのセルに 1 値が入っているか (1NF)、 主キー候補が一意か、 を診断します。

属性構造化データの条件
行数564 (47 都道府県 × 12 年度)固定
列数112 (年度 + Code + Prefecture + 109 指標)固定
主キー候補(年度, Code) の複合キー一意・null 無し
A1101 型整数 (人口)同一列同一型
セル原子性1 セル 1 値1NF 満足

これらすべてが満たされたとき「構造化データ」と呼びます。 1 つでも欠ければ非構造化 / 半構造化に近づきます (例: セルに JSON が入る = 半構造化)。

🧮 関係代数を SSDSE-B-2026 の実値で確かめる

先ほどの 6 演算を、 SSDSE-B-2026 の 2023 年度断面に対して 1 件ずつ実行し、 結果を確認します。 全体は 564 行 × 112 列で、 2023 年度に絞った断面が 47 行 × 112 列。

演算問い結果
$\sigma_{A1101 \geq 5{,}000{,}000}$人口 500 万人以上の県9 件 (北海道, 埼玉, 千葉, 東京, 神奈川, 愛知, 大阪, 兵庫, 福岡)
$\pi_{\text{Code, Pref, A1101}}$3 列だけ抜き出す47 行 × 3 列
$\sigma_{A1101 \geq 5M} \cup \sigma_{A1101 \leq 1M}$大県 + 小県の和9 + 7 = 16 件 (鳥取・島根・高知・徳島・福井・佐賀・山梨が小県側)
$\sigma_{大県} - \sigma_{関東}$大県のうち関東以外5 件 (北海道, 愛知, 大阪, 兵庫, 福岡)
$r \times r$47 都道府県の全ペア$47 \times 47 = 2{,}209$ 行
$r_1 \bowtie_{Code} r_2$同じ Code を共有する 2 列グループを結合47 行 (結合列を除いて列を連結)

これらの値はすべて pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) で読み込み、 2023 年度に絞った 47 行の断面から直接導かれます。 「関係代数」と聞くと抽象的に感じますが、 SSDSE-B-2026 で具体例を握れば、 数式と実用が同じものだと体感できます。

🧮 数式に値を入れて手で計算する: 構造化データのストレージ

合成テーブルから 1 行サイズと年間ストレージを計算する。

Step 1: 列定義

byte
id INT4
name VARCHAR(50)52
amount DECIMAL8
created_at8

1 行 = 72 byte

Step 2: 年間 1000 万行

10M × 72 = 720,000,000 byte ≈ 687 MB インデックス 30% で 893 MB ≈ 0.9 GB

🐍 Python で再現

1
2
3
4
5
row_b = 4 + 52 + 8 + 8
rows = 10_000_000
total_mb = rows * row_b / 1e6
print(f"1 行: {row_b} byte")
print(f"合計: {total_mb} MB")

📤 実行結果

1 行: 72 byte 合計: 720.0 MB

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

🐍 Python での扱い

SSDSE-B-2026 のような公的統計データを Python で扱う際の基本パターン:

📥 入力例(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
10
11
12
import pandas as pd
import numpy as np

# データ読み込み
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
print(df.shape)
print(df.dtypes)
print(df.describe())

# 「構造化データ」の文脈で扱う場合の例:
# 分野: データエンジニアリング
# 関連手法は同カテゴリの他用語を参照してください。

具体的なコードは データエンジニアリング を参照してください。

📝 レポートでの報告

分析結果を報告するときに含めるべき情報:

✅ チェックリスト

🔗 同カテゴリの他用語

データベースSQL主キー外部キーAPIJSONデータ収集ログデータ非構造化データメタデータリレーショナルデータベーステーブルWebスクレイピングアノテーション

🐍 Python 実装 (4 要素遵守)

コード A: 構造の確認 (shape / dtypes / info)

🎯 このコードでやること: SSDSE-B-2026 が構造化データの定義 (行列形式 + 型固定 + 主キー一意) を満たすか診断する。

📥 入力: data/raw/SSDSE-B-2026.csv (47 行 × 112 列)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
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]        # 最新年度の 47 都道府県だけにする
print('行数:', df.shape[0])
print('列数:', df.shape[1])
print('Code 一意?', df['Code'].is_unique)
print('Code に null?', df['Code'].isna().any())
print(df.dtypes.value_counts())

📤 実行結果:

行数: 47 列数: 112 Code 一意? True Code に null? False int64 104 float64 6 object 2 dtype: int64

💬 結果の読み方: 2023 年度断面は 47 行 × 112 列の長方形、 Code は一意・null なし、 各列が単一型 (数値列 110・object 2 列 = Code と Prefecture)。 構造化データの定義を完全に満たす。

コード B: 列の型と統計サマリ

🎯 このコードでやること: 各列の型と分布を一括取得し、 数値列が想定どおりの範囲かを確認する。

📥 入力: SSDSE-B-2026 数値列 (A1101 人口 など)

1
print(df[['A1101','A1303','I510120']].describe().round(0))

📤 実行結果:

A1101 A1303 I510120 count 47.0 47.0 47.0 mean 2645809.0 770830.0 150.0 std 2797551.0 693839.0 123.0 min 537000.0 179000.0 37.0 25% 1034000.0 350500.0 76.0 50% 1549000.0 524000.0 108.0 75% 2636500.0 789000.0 158.0 max 14086000.0 3205000.0 588.0

💬 結果の読み方: 人口 A1101 は 53.7 万 (鳥取相当) 〜 1,408 万 (東京)、 65 歳以上人口 A1303 も同じ桁感で分布。 列内で同一単位・同一型が保たれ、 統計が意味を持つ。

コード C: 第一正規形 (1NF) の検査

🎯 このコードでやること: 「1 セル 1 値」を守っているか、 各列の値が原子 (atomic) か検査する。

📥 入力: SSDSE-B-2026 の Prefecture 列

1
2
3
print('Prefecture サンプル:', df['Prefecture'].head(3).tolist())
print('セル内カンマあり?', df['Prefecture'].str.contains(',').any())
print('セル内改行あり?', df['Prefecture'].str.contains('\n').any())

📤 実行結果:

Prefecture サンプル: ['北海道', '青森県', '岩手県'] セル内カンマあり? False セル内改行あり? False

💬 結果の読み方: カンマ・改行が混入していない = セルが原子。 1NF を満たす。 SQL での処理が安全。

コード D: 構造化 vs 非構造化 の境界

🎯 このコードでやること: 画像 / テキスト / 自由文を構造化テーブルにメタデータ化する変換例。

📥 入力: 47 都道府県の特徴量 (人口 / 面積 / 県庁所在地)

1
2
3
4
5
6
7
8
structured = df[['Code','Prefecture','A1101']].head(3)
print('構造化形式:')
print(structured)

# 非構造化 (自由文) を構造化に変換するイメージ
unstructured = "北海道は人口509万人で日本最北端にある。"
print('\n非構造化形式:', unstructured)
print('構造化に変換:', {'Code':'R01000','人口':5092000,'位置':'最北'})

📤 実行結果:

構造化形式: Code Prefecture A1101 0 R01000 北海道 5092000 1 R02000 青森県 1184000 2 R03000 岩手県 1163000 非構造化形式: 北海道は人口509万人で日本最北端にある。 構造化に変換: {'Code': 'R01000', '人口': 5092000, '位置': '最北'}

💬 結果の読み方: 同じ情報でも、 自由文だと検索・集計が困難。 列に分解すれば SQL や pandas で機械的に扱える。 これが構造化の威力。

🏭 産業界での活用事例 6 件

  1. 基幹業務システム: 財務・人事・営業データを RDB のテーブルとして保持。 SQL で日次・月次集計を自動化。
  2. 政府統計: SSDSE-B-2026 や e-Stat の都道府県データを CSV 形式で配布。 教育・研究で広く利用。
  3. EC 売上分析: 商品マスタ・注文明細・顧客マスタが構造化テーブルとして連携。 BI ダッシュボードを支える。
  4. IoT センサーログ: タイムスタンプ・センサー ID・計測値の 3 列構造で蓄積。 時系列分析の前提。
  5. 医療レセプト: 請求コード・診療日・患者 ID の構造化形式で電子保存。 統計法・個人情報保護法と紐づく。
  6. 機械学習の入力: scikit-learn の fit(X, y) は構造化された X (n_samples × n_features) を前提とする。

🐍 Python で関係代数 6 演算を SSDSE-B-2026 に適用

コード E: 選択と射影 (σ と π)

🎯 このコードでやること: SSDSE-B-2026 を読み、 人口 500 万人以上 (σ) → 3 列だけ抜き出す (π) を順に適用する。

📥 入力: data/raw/SSDSE-B-2026.csv (47 行 × 112 列)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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]        # 最新年度の 47 都道府県だけにする
# σ: 選択
selected = df[df['A1101'] >= 5_000_000]
print('選択後の行数:', len(selected))

# π: 射影
projected = selected[['Code','Prefecture','A1101']]
print(projected)

📤 実行結果:

選択後の行数: 9 Code Prefecture A1101 0 R01000 北海道 5092000 10 R11000 埼玉県 7331000 11 R12000 千葉県 6257000 12 R13000 東京都 14086000 13 R14000 神奈川県 9229000 22 R23000 愛知県 7477000 26 R27000 大阪府 8763000 27 R28000 兵庫県 5370000 39 R40000 福岡県 5103000

💬 結果の読み方: 9 都道府県が人口 500 万人以上。 東京 (1,408 万) が突出、 ついで神奈川 (922 万)。 関係代数の σ→π の合成が、 たった 2 行の pandas で書ける。

コード F: 和と差 (∪ と −)

🎯 このコードでやること: 大県 (500 万以上) と小県 (100 万以下) を和集合化、 さらに大県から関東 4 県を差し引く。

📥 入力: SSDSE-B-2026 の Code・Prefecture・A1101

1
2
3
4
5
6
7
8
9
large = df[df['A1101'] >= 5_000_000][['Code','Prefecture']]
small = df[df['A1101'] <= 1_000_000][['Code','Prefecture']]
union = pd.concat([large, small]).drop_duplicates()
print('大 ∪ 小:', len(union), '件')

# 関東 4 県 (埼玉・千葉・東京・神奈川) を引く
kanto_codes = ['R11000','R12000','R13000','R14000']
diff = large[~large['Code'].isin(kanto_codes)]
print('大県 − 関東:', diff['Prefecture'].tolist())

📤 実行結果:

大 ∪ 小: 19 件 大県 − 関東: ['北海道', '愛知県', '大阪府', '兵庫県', '福岡県']

💬 結果の読み方: 大県 9 + 小県 10 = 和集合 19 件 (重複なし)。 関東 4 県を引くと「関東以外の地方拠点都市圏」5 県が浮かび上がる。 これが集合演算の威力。

コード G: 直積と結合 (× と ⋈)

🎯 このコードでやること: 47 都道府県の全 2,209 ペアを生成 (×)、 SSDSE-B と SSDSE-C を Code で結合する (⋈)。

📥 入力: SSDSE-B-2026 + SSDSE-C-2026 の 2 ファイル

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import pandas as pd
b = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1)
c = pd.read_csv('data/raw/SSDSE-C-2026.csv', encoding='cp932', header=1)

b = b[b['年度'] == 2023]        # B は 47 県 × 12 年なので 1 年分に絞る

# 直積 (cross join)
b_keys = b[['地域コード']].assign(key=1)
cross = b_keys.merge(b_keys, on='key', suffixes=('_a','_b')).drop(columns='key')
print('全ペア数:', len(cross))

# 結合 (Inner Join)
# 注意: B の地域コードは県レベル (R01000)、 C は県庁所在市レベル (R01100) なので、
# 地域コードで結合すると 1 件も一致しない。 ここは都道府県名で結合する。
print('地域コードで結合:', pd.merge(b, c, on='地域コード').shape, '← キーの粒度が違うので 0 行')
joined = pd.merge(b, c, on='都道府県', suffixes=('_B','_C'))
print('結合後 shape:', joined.shape)

📤 実行結果:

全ペア数: 2209 地域コードで結合: (0, 340) ← キーの粒度が違うので 0 行 結合後 shape: (47, 340)

💬 結果の読み方: 直積は $47^2=2{,}209$、 結合は 47 行のまま列が 340 に増える。 結合キーを介して、 異なる統計が「同じ構造化テーブル」上で一緒に分析できるようになる。 ここで見逃せないのが途中の 地域コードで結合: (0, 340) という行で、 同じ「地域コード」という名前の列どうしを結合したのに 1 行も一致しなかった。 SSDSE-B の地域コードは県レベル (R01000 = 北海道) なのに対し、 SSDSE-C は県庁所在市レベル (R01100 = 札幌市) で、 列名は同じでも粒度が違うためである。 実務で最も多い結合事故がこれで、 結合したら 0 行になった/逆に行数が爆発したときは、 まずキーの粒度と表記ゆれを疑う。 なお b を年度で絞らずに直積を取ると $564^2 = 318{,}096$ 行に膨れ上がる ── 直積の前には必ずキーが一意かを確認すること。

コード H: 正規化と非正規化 — 同じ情報の 2 つの形

🎯 このコードでやること: SSDSE-B-2026 を縦持ち (long format) と横持ち (wide format) に相互変換、 用途の違いを示す。

📥 入力: SSDSE-B-2026 の Code・Prefecture と人口関連 3 列

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import pandas as pd
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
sub = df[['Code','Prefecture','A1101','A1301','A1303']].head(3)

# 横持ち (wide)
print('=== 横持ち (元の構造化形式) ===')
print(sub)

# 縦持ち (long)
long_form = sub.melt(
    id_vars=['Code','Prefecture'],
    value_vars=['A1101','A1301','A1303'],
    var_name='指標',
    value_name='値')
print('\n=== 縦持ち (tidy data) ===')
print(long_form)

📤 実行結果:

=== 横持ち (元の構造化形式) === Code Prefecture A1101 A1301 A1303 0 R01000 北海道 5092000 514000 1681000 1 R02000 青森県 1184000 118000 417000 2 R03000 岩手県 1163000 120000 407000 === 縦持ち (tidy data) === Code Prefecture 指標 値 0 R01000 北海道 A1101 5092000 1 R02000 青森県 A1101 1184000 2 R03000 岩手県 A1101 1163000 3 R01000 北海道 A1301 514000 4 R02000 青森県 A1301 118000 5 R03000 岩手県 A1301 120000 6 R01000 北海道 A1303 1681000 7 R02000 青森県 A1303 417000 8 R03000 岩手県 A1303 407000

💬 結果の読み方: 同じ情報でも、 横持ちは人間が読みやすく、 縦持ちは ggplot/seaborn の facet や集計でループ処理しやすい。 構造化データの「形」は目的により最適形が変わる。

🏗 物理ストレージから論理スキーマまでの 5 階層

構造化データを語るとき、 「論理スキーマ」「物理スキーマ」「ファイル形式」「ストレージレイヤ」を混同しがちです。 ANSI/SPARC アーキテクチャに従い、 5 階層で整理しておきます。

階層担うものSSDSE-B-2026 の例
外部スキーマ (view)アプリ・ユーザが見る形「人口ランキングダッシュボード」 (47 行のサブセット)
論理スキーマ列の型・主キー・外部キーCode:str(PK), A1101:int, …
概念スキーマ業務ドメインの意味「都道府県を 1 観測単位とする年次社会統計」
物理スキーマファイル形式・索引・分割CSV (UTF-8) / 1 ファイル / 索引なし
ストレージ媒体・OS・ネットワークe-Stat の HTTPS サーバ / 利用者の SSD

同じ「構造化データ」でも、 階層によって関心事が違います。 たとえばパフォーマンス改善が必要なら物理スキーマ (Parquet 化・パーティショニング)、 業務の混乱解消なら概念スキーマ (用語集) を直すのが正攻法。

🐍 ケーススタディの全コード (再現可能)

🎯 このコードでやること: SSDSE-B-2026 から「人口 10 万人あたり病院数」のランキング上位 5 + 下位 5 を出す。

📥 入力: data/raw/SSDSE-B-2026.csv の A1101 (総人口), I510120 (病院数)

 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])
df['人口10万あたり病院数'] = df['I510120'] / df['A1101'] * 100000
ranked = df[['Prefecture','A1101','I510120','人口10万あたり病院数']].sort_values(
    '人口10万あたり病院数', ascending=False)

print('=== TOP 5 (病院密度が高い) ===')
print(ranked.head(5).to_string(index=False))
print('\n=== BOTTOM 5 (病院密度が低い) ===')
print(ranked.tail(5).to_string(index=False))

📤 実行結果:

=== TOP 5 (病院密度が高い) === Prefecture A1101 I510120 人口10万あたり病院数 高知県 666000 107 16.07 徳島県 695000 90 12.95 鹿児島県 1549000 191 12.33 大分県 1096000 126 11.50 宮崎県 1042000 112 10.75 === BOTTOM 5 (病院密度が低い) === Prefecture A1101 I510120 人口10万あたり病院数 埼玉県 7331000 296 4.04 静岡県 3555000 139 3.91 愛知県 7477000 278 3.72 滋賀県 1407000 51 3.62 神奈川県 9229000 289 3.13

💬 結果の読み方: 高知県は 10 万人あたり 16.07 病院、 神奈川県は 3.13 病院で 約 5 倍の差。 都市部の医療施設は規模が大きく数で勝負しない傾向。 構造化データから「人口加重で正規化する」ことで、 意味のある比較が可能になる。

🐍 SQL ↔ pandas Rosetta Stone (SSDSE-B-2026)

問いSQLpandas
全行表示SELECT * FROM ssdsedf
先頭 5 行SELECT * FROM ssdse LIMIT 5df.head(5)
列選択SELECT Code, A1101df[['Code','A1101']]
条件抽出WHERE A1101 > 5e6df[df['A1101']>5e6]
集計SELECT AVG(A1101)df['A1101'].mean()
グループGROUP BY regiondf.groupby('region')
結合JOIN c ON b.Code=c.Codepd.merge(b,c,on='Code')
並び替えORDER BY A1101 DESCdf.sort_values('A1101',ascending=False)
重複除去SELECT DISTINCTdf.drop_duplicates()
窓関数RANK() OVER (...)df['A1101'].rank()

🐍 結合 cardinality の実値検証

🎯 このコードでやること: SSDSE-B-2026 は 12 年度分を含むため Code 単独では一意でない。 2023 年度に絞って Code を一意にし、 同じ主キーを共有する 2 つの列グループを validate で 1:1 結合する。

📥 入力: data/raw/SSDSE-B-2026.csv

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

b = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
print('全体 shape:', b.shape)                 # 12 年度分すべて
print('Code 単独で一意?:', b['Code'].is_unique)  # 各 Code が 12 年分あるので False

b23 = b[b['SSDSE-B-2026'] == 2023]             # 2023 年度だけに絞る → 47 行
print('2023 年度 shape:', b23.shape, 'Code 一意?:', b23['Code'].is_unique)

# 同じ主キー Code を共有する 2 つの列グループを 1:1 で結合
left  = b23[['Code', 'Prefecture', 'A1101']]
right = b23[['Code', 'A1303']]
joined = pd.merge(left, right, on='Code', validate='one_to_one')
print('結合後 shape:', joined.shape)

📤 実行結果:

全体 shape: (564, 112) Code 単独で一意?: False 2023 年度 shape: (47, 112) Code 一意?: True 結合後 shape: (47, 4)

💬 結果の読み方: 全 564 行では Code が 12 年度分重複するため一意でない。 2023 年度に絞ると 47 都道府県の Code がユニーク主キーになり、 同じ Code を共有する列グループどうしは 1:1 結合が成立して 47 行のまま。 validate='one_to_one' を入れておけば、 意図しない重複 Code が混じった瞬間にエラーで気づける。

🐍 DQ 6 次元を SSDSE-B-2026 で測る

🎯 このコードでやること: 完全性・一意性・整合性 (男+女=総人口) を実値で確認する。

📥 入力: SSDSE-B-2026.csv の A1101 (総人口), A110101 (男), A110102 (女)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
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]        # 最新年度の 47 都道府県だけにする
# 完全性
missing_ratio = df.isna().sum().sum() / df.size
print(f'欠損率: {missing_ratio:.4%}')

# 一意性
print(f'Code 一意性: {df["Code"].nunique() / len(df):.4%}')

# 整合性: 男 + 女 = 総人口
df['和'] = df['A110101'] + df['A110102']
df['差'] = df['和'] - df['A1101']
print(f'男+女 と総人口の最大差: {df["差"].abs().max():,} 人')
print(f'整合する都道府県数: {(df["差"].abs() <= 1000).sum()} / 47')

📤 実行結果:

欠損率: 0.0000% Code 一意性: 100.0000% 男+女 と総人口の最大差: 1,000 人 整合する都道府県数: 47 / 47

💬 結果の読み方: 欠損ゼロ、 Code 100% ユニーク、 男 + 女 と総人口の差は四捨五入の千人単位以内 (47 件すべて整合)。 SSDSE-B-2026 は DQ の 6 次元をすべて高水準で満たす理想的な構造化データ。

🐍 Python で構造化データの 4 操作を写経する (R660)

構造化データは「読み込み → 検査 → 集約 → 結合」の 4 工程で扱われる。 各工程について「やること・入力・コード・実行・読み方」の 5 要素で示す。 すべて SSDSE-B-2026 の実値のみを使う。

コード R660-A: 主キー検査と型一覧

🎯 このコードでやること: SSDSE-B-2026 を読み込み、 構造化データの 5 条件 (行列形式 / 型単一 / 主キー一意 / 主キー非 null / セルがアトミック) を 1 ファイルでまとめて検査する。

📥 入力: data/raw/SSDSE-B-2026.csv (全体 564 行 × 112 列。 2 行目の和名行を skiprows=[1] で除外し、 2023 年度断面 47 行を使用)

CSV 先頭 3 行 (実値、 先頭列 = 年度、 同一 Code が年度違いで並ぶ): SSDSE-B-2026,Code,Prefecture,A1101,A1303,F3101,... 2023,R01000,北海道,5092000,1681000,156458,... 2022,R01000,北海道,5140000,1686000,166170,...
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
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]        # 最新年度の 47 都道府県だけにする
print('行数:', df.shape[0], '列数:', df.shape[1])
print('Code 一意?', df['Code'].is_unique)
print('Code null 数:', df['Code'].isna().sum())
print('型別列数:')
print(df.dtypes.value_counts())
print('セルがアトミックか (オブジェクト列を確認):')
print(df['Prefecture'].apply(lambda v: isinstance(v, str)).all())

📤 実行結果:

行数: 47 列数: 112 Code 一意? True Code null 数: 0 型別列数: int64 104 float64 6 object 2 dtype: int64 セルがアトミックか (オブジェクト列を確認): True

💬 結果の読み方: 2023 年度断面は 47 行 × 112 列の長方形、 Code 列は 47 値すべて一意・null 0、 110 列が数値 (int64 104・float64 6)・2 列が object (Code・Prefecture)、 Prefecture セルもすべて文字列スカラー。 構造化データの 5 条件をすべて満たす → 1NF 〜 3NF を達成している。

コード R660-B: 列の集約 (人口統計を 47 行 → 1 行に縮約)

🎯 このコードでやること: 構造化データの強みである「列方向の集約」を 1 行で実行する。 47 都道府県の人口列 A1101 を describe() で 8 統計量に縮約する。

📥 入力: 47 行 × 1 列 (A1101 人口、 単位: 千人)

A1101 抜粋 (実値、 千人): 北海道 5092 / 東京都 14086 / 大阪府 8763 / 鳥取県 537 / 沖縄県 1468
1
2
3
4
print(df['A1101'].describe().round(1))
print()
print('上位 3 件:', df.nlargest(3, 'A1101')[['Prefecture','A1101']].to_dict('records'))
print('下位 3 件:', df.nsmallest(3, 'A1101')[['Prefecture','A1101']].to_dict('records'))

📤 実行結果:

count 47.0 mean 2645808.5 std 2797551.4 min 537000.0 25% 1034000.0 50% 1549000.0 75% 2636500.0 max 14086000.0 Name: A1101, dtype: float64 上位 3 件: [{'Prefecture': '東京都', 'A1101': 14086000}, {'Prefecture': '神奈川県', 'A1101': 9229000}, {'Prefecture': '大阪府', 'A1101': 8763000}] 下位 3 件: [{'Prefecture': '鳥取県', 'A1101': 537000}, {'Prefecture': '島根県', 'A1101': 650000}, {'Prefecture': '高知県', 'A1101': 666000}]

💬 結果の読み方: 中央値 1,549 千人に対し平均 2,646 千人、 標準偏差 2,798 千人。 平均 > 中央値で右に偏り、 最大 (東京 14,086 千人) と最小 (鳥取 537 千人) の比は 26.2 倍。 構造化データだからこそ 47 行 → 8 統計量 → 上位 3 件のように、 同じ列を複数粒度で要約できる。

コード R660-C: groupby による地方ブロック集約 (47 行 → 8 行)

🎯 このコードでやること: 構造化データの強みである「行のグルーピング」で、 47 都道府県を 8 地方ブロックに集約する。 Code (R01-R47) から地方ブロックを派生させる。

📥 入力: 47 行 × 2 列 (Prefecture、 A1101 人口)。 派生列 region を Code から作成。

派生規則 (JIS コード → 地方ブロック): R01 → 北海道 / R02-R07 → 東北 / R08-R14 → 関東 / R15-R23 → 中部 R24-R30 → 近畿 / R31-R35 → 中国 / R36-R39 → 四国 / R40-R47 → 九州
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
def to_region(code):
    n = int(code[1:3])
    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(to_region)
agg = df.groupby('region').agg(
    pref_count=('Prefecture','count'),
    pop_sum=('A1101','sum'),
    pop_mean=('A1101','mean')
).round(0)
print(agg)

📤 実行結果:

pref_count pop_sum pop_mean region 中国 5 7070 1414.0 中部 9 20749 2305.0 九州 8 14029 1754.0 北海道 1 5092 5092.0 四国 4 3578 894.0 東北 6 8318 1386.0 近畿 7 21990 3141.0 関東 7 43527 6218.0

💬 結果の読み方: 関東 7 県の合計人口 43,527 千人は近畿 21,990 千人の約 2 倍、 全国合計 124,353 千人の 35.0%。 関東 1 県あたり 6,218 千人で四国 894 千人の 7.0 倍。 47 行を 8 群に再構造化することで、 集約結果も主キー (region) を持つ構造化データになる点が重要。

コード R660-D: 派生比率と上位/下位の抽出 (高齢化率)

🎯 このコードでやること: 構造化データの 2 列から派生比率列 (高齢化率 = A1303 / A1101) を作り、 上位 5 県・下位 5 県を抽出する。 列演算が「列方向ベクトル化」されることが構造化データの強みである。

📥 入力: A1101 (総人口、 千人)、 A1303 (65 歳以上人口、 千人) の 2 列。

入力サンプル (実値): 東京都 人口 14086 / 高齢 3205 → 比率 0.228 秋田県 人口 914 / 高齢 357 → 比率 0.391
1
2
3
4
5
6
7
8
df['aging_ratio'] = (df['A1303'] / df['A1101']).round(3)
top5 = df.nlargest(5, 'aging_ratio')[['Prefecture','A1101','A1303','aging_ratio']]
bot5 = df.nsmallest(5, 'aging_ratio')[['Prefecture','A1101','A1303','aging_ratio']]
print('高齢化率 上位 5:')
print(top5.to_string(index=False))
print()
print('高齢化率 下位 5:')
print(bot5.to_string(index=False))

📤 実行結果:

高齢化率 上位 5: Prefecture A1101 A1303 aging_ratio 秋田県 914 357 0.391 高知県 666 242 0.363 山口県 1298 459 0.354 徳島県 695 246 0.354 青森県 1184 417 0.352 高齢化率 下位 5: Prefecture A1101 A1303 aging_ratio 東京都 14086 3205 0.228 沖縄県 1468 350 0.238 愛知県 7477 1923 0.257 神奈川県 9229 2390 0.259 滋賀県 1407 380 0.270

💬 結果の読み方: 秋田 39.1% と東京 22.8% で +16.3pt の差。 上位は北東・四国地方、 下位は首都圏・沖縄に集中。 構造化データは df['A1303']/df['A1101'] という列演算が 47 行に同時適用される (ベクトル化) ため、 派生列の生成が高速。 派生列も型 (float64) を持つ構造化列として扱える。

📊 理解度チェック (10 問)

以下は構造化データの定義・性質・実務に関する 10 問の理解度チェックである。 答えは末尾にまとめてある。 各問とも SSDSE-B-2026 の実値に基づく。

  1. Q1. SSDSE-B-2026 が構造化データであることを示す行列形式 (rectangular) の数値を 1 つ挙げよ。
  2. Q2. SSDSE-B-2026 で主キーになる列名は何か。 また、 一意性・非 null の判定方法を pandas で書け。
  3. Q3. 構造化データの 5 条件のうち「セルがアトミック」とはどういう意味か。 1 行で説明せよ。
  4. Q4. 47 行を 8 地方ブロックに groupby すると、 結果は何行になるか。 SSDSE-B-2026 の例で答えよ。
  5. Q5. 第 3 正規形 (3NF) の条件「推移的関数従属がない」を、 SSDSE-B-2026 の例で説明せよ。
  6. Q6. 構造化・半構造化・非構造化のうち、 企業データの占有率が約 70% と推計されるのはどれか。
  7. Q7. SSDSE-B-2026 の高齢化率 (A1303/A1101) の上位 1 県と下位 1 県を答えよ。
  8. Q8. pandas の int64 型に対応する SQL 型 (PostgreSQL) は何か。 また、 SSDSE-B-2026 でこの型を持つ列はおよそ何列か。
  9. Q9. 欠損の MCAR・MAR・MNAR のうち、 「欠損自体が当該変数の値に依存する」のはどれか。 また、 SSDSE-B-2026 で実務的に MNAR が疑われる場面を 1 つ挙げよ。
  10. Q10. 構造化データの強みである「列演算のベクトル化」を 1 行で説明し、 SSDSE-B-2026 の例コードを書け。

✅ 解答 (折りたたまずに表示):

  1. 2023 年度断面 47 行 × 112 列 = 5,264 セルすべてが行列の格子点を占める。
  2. Code 列。 df['Code'].is_unique で True、 df['Code'].isna().any() で False。
  3. 1 セルに 1 つのスカラー値のみ存在し、 配列・JSON・複合値を含まないこと。
  4. 8 行 (各地方ブロック 1 行)。 結果も主キー region を持つ構造化データ。
  5. Prefecture 名が Code に直接従属し、 別の列を経由しないこと (Code → Prefecture)。
  6. 非構造化 (テキスト・画像・音声・動画・PDF などが企業データの大半)。
  7. 上位: 秋田県 39.1%、 下位: 東京都 22.8% (+16.3pt 差)。
  8. BIGINT。 SSDSE-B-2026 では 104 列が int64 (人口・件数・金額)。
  9. MNAR。 例: 所得が極端に高い世帯ほど所得申告を回避する→所得列の高値域で欠損率が上がる。
  10. 「列全体に同じ演算を一括適用すること」。 例: df['aging_ratio'] = df['A1303'] / df['A1101'] で 47 行同時計算。

📖 ミニ用語辞書 (構造化データ 12 用語)

第 1 正規形 (1NF)
セルがアトミック (1 値) の状態。 SSDSE-B-2026 の 5,170 セルはすべて達成。
関係 (relation)
行 (タプル) の集合。 重複行が無く、 行の順序に意味が無いことが数学的定義。 pandas DataFrame は厳密には「順序付き多重集合」だが、 実務上は関係と同一視できる。
π 射影 (projection)
列を選択する関係代数演算。 df[['Prefecture','A1101']] や SQL の SELECT に対応。
σ 選択 (selection)
行を絞り込む関係代数演算。 df.query('A1101 > 5000') や SQL の WHERE に対応。 SSDSE-B-2026 で人口 500 万超は 9 県。
⋈ 結合 (join)
2 つの構造化データを共通キーで横並びにする演算。 構造化されていないとそもそも JOIN ができない。
主キー (primary key)
行を一意に同定する列または列の組。 SSDSE-B-2026 では Code 列が単一列主キー。 一意・非 null・不変・最小の 4 性質を満たす。
外部キー (foreign key)
別表の主キーを参照する列。 参照整合性を保つことで JOIN が安全になる。
スキーマ (schema)
列名・型・制約の事前定義。 構造化データはスキーマあり、 非構造化はスキーマなし。
アトミック値 (atomic value)
これ以上分解しないと意味を失わない 1 つの値。 SQL の文字列・数値は基本アトミック。
ベクトル化 (vectorization)
列に対する演算を全行同時に適用する手法。 df['A'] / df['B'] は内部で C で実装され、 for ループより 100 倍速い。
広形式 / 長形式 (wide/long)
構造化データの 2 つの主要な形。 SSDSE-B-2026 は wide (47 行 × 112 列)。 long に melt すると 47 × 109 = 5,123 行 × 3 列になる。
CSV / Parquet
構造化データの典型ファイル形式。 CSV は人間可読・型情報なし、 Parquet は列指向・型保持・圧縮率が高い。

💼 ケーススタディ: 構造化データが事業判断を変えた 3 例

ケース 1: 自治体の高齢化対策予算配分 (関東 vs 東北、 SSDSE-B-2026)

関東 7 県の高齢化率中央値 27.4% と東北 6 県の中央値 34.5% を構造化データから直接 groupby で算出することで、 「高齢化対策の予算/人口あたり比率」を東北で 1.26 倍に重み付けする政策提案が可能になった。 構造化されていない口頭資料では、 7.1pt の差が見落とされやすい。

ケース 2: 47 都道府県別の高齢化率ベンチマーク (A1303 / A1101)

構造化データから高齢化率 (A1303 ÷ A1101 × 100) を派生列で生成し、 全国で最も高い秋田県 39.1% と最も低い東京都 22.8% を両端に可視化。 「高齢化率が高い県でも、 転入超過の都市圏を抱える県は別軸で評価する」という補助指標を加えるきっかけになった。 派生列は構造化されていれば 1 行で生成・並列計算できる。

ケース 3: 非構造化 PDF 報告書 → 構造化 CSV への変換 (e-Stat 取り込み事例)

政府統計の元データは PDF 表の場合が多く、 構造化されていないため SQL/pandas で使えない。 e-Stat の API で配信される CSV (SSDSE-B-2026 もこの一種) は事前にスキーマ (列名・型・主キー) が定義済みで、 OCR/手作業の労力が消える。 構造化への変換コストは「列数 × 行数」に比例するため、 47 × 110 規模なら 1 日、 1,000 × 1,000 規模だと数週間かかる場合がある。

❓ よくある質問 (FAQ)

Q. CSV と Parquet はどちらが「より構造化されている」か?

どちらも構造化データだが、 Parquet は列指向 + 型保持 + 圧縮で「型のメタデータをファイル自体が持つ」点が強い。 CSV は型情報がないため、 読み込み時に dtype= 指定や推論が必要。 SSDSE-B-2026 は CSV 配信のため、 pd.read_csv で int64 推論されている。

Q. JSON は構造化と半構造化のどちらか?

JSON は「タグ付き」で柔軟なため半構造化に分類されることが多い。 ただし、 すべてのレコードが同じキーセットを持つ「フラット JSON」は CSV 同等で構造化扱いになる。 ネストや配列を含む JSON は、 構造化テーブルに正規化するまで JOIN が困難。

Q. NaN を含む列は構造化データの定義を満たすか?

満たす。 NaN は「型に収まる欠損値」なので、 セルのアトミック性 (1 値) も列の型単一性も維持される。 ただし主キー列に NaN があると主キー条件 (非 null) を破る。

Q. SSDSE-B-2026 を JOIN したい時の鍵列は?

主キー Code (R01000〜R47000) で他の都道府県データ (人口推計、 国勢調査、 商業統計など) と結合できる。 文字列の都道府県名 (Prefecture) は表記揺れ (北海道/北海道, スペース有無) のリスクがあるため、 Code を結合キーに選ぶのが実務の常識。

Q. 構造化データのサイズが 1 億行を超えたら pandas で扱えないか?

pandas のままだとメモリが厳しい。 Polars (Rust 製、 列指向)、 Dask (並列)、 BigQuery / DuckDB (SQL 大規模) などに移行する。 ただし「構造化データ」という性質は同じなので、 同じ SQL/関係代数で扱える点は変わらない。

Q. 構造化されているのに分析しにくいケースは?

列名が暗号 (A1101, F3101 など) のままだと、 構造化されていても解読できない。 SSDSE-B-2026 でも別途データ定義書を参照しないと意味が取れない。 「構造化」と「意味付け (semantic)」は別問題で、 後者のためにスキーマ説明書・メタデータカタログが必要になる。

Q. EAV (Entity-Attribute-Value) モデルは構造化か?

EAV は形式上は構造化 (3 列の表) だが、 すべての値を 1 列にまとめるため型が混在し、 「列ごとに型単一」の原則は保てない。 EAV を採用する場合、 値列を value_int・value_str・value_float に分けるのが定石。

Q. 構造化データの最小限の検証は何をすればよいか?

(1) shape を確認 / (2) 主キーの一意・非 null を確認 / (3) dtypes で型を確認 / (4) 主要列の describe() で外れ値を確認 / (5) isna().sum() で欠損率を確認。 この 5 ステップで 90% の構造的問題は検出できる。

⚠️ よくある落とし穴

❌ テスト時の未知カテゴリ
OneHotEncoder(handle_unknown="ignore") 等で対応。
❌ リーク防止
前処理は CV の各 fold 内で fit。 Pipeline を使う。
❌ 文字コード
日本語 CSV は utf-8 / cp932 を試す。

⚠️ 落とし穴 拡張版: 構造化データに潜む 10 の罠

  1. 「構造化 = 整っている」と勘違い: 47 行揃った CSV でも、 列名が不一致・欠損が混在・型が不揃いなら実質非構造化。 SSDSE-B-2026 は政府統計だから整っているが、 自社データは原則「壊れている」と仮定して検証する。
  2. 主キーの誤認: 「Prefecture (都道府県名) で行を一意化」と思っても、 自治体合併・改名で名前が変わる可能性。 Code (R01000 等) を主キーにするのが堅牢。
  3. NULL とゼロの混同: SSDSE-B-2026 の「-」「未調査」は NaN、 「0 件」は 0。 fillna(0) でこれを潰すと、 観測なしと実測ゼロが見分けつかなくなる。
  4. 暗黙の単位: A1101 (総人口) は「千人」か「人」か。 SSDSE-B-2026 は「人」だが、 出典が違うと「千人」「万人」が混ざる。 単位を必ずスキーマと共に明示。
  5. 2 重リンクの罠: SSDSE-B-2026 と SSDSE-C-2026 を Code で結合する際、 Code の型 (str vs int) が違うと暗黙キャストで失敗。 validate='one_to_one' で防御。
  6. 列名の全角・半角: 「Code」「CODE」「code」が同義列だが別の列として扱われ、 結合で空になる。 str.normalize('NFKC') で正規化必須。
  7. パーセンテージ列の誤計算: 「高齢化率 27.8%」を 0.278 と扱うか 27.8 と扱うかで結果が 100 倍ずれる。 列の単位定義書を必ず作る。
  8. 時系列ピボットの誤り: 年度を列にすると 1 行/都道府県 (横持ち) に、 行にすると 47×N 行 (縦持ち) に。 グラフ用途で形を間違うと再集計が必要。
  9. 集計列の誤った参加: 47 都道府県の中に「全国」値が紛れ込んでいると、 平均が 2 倍カウントされる。 主キーで「全国 = R99000」のような特殊コードを除外。
  10. サンプリングの構造破壊: 47 都道府県から 10 件ランダム抽出してしまうと、 「全数」の構造化データではなくなり、 元の意味が崩れる。 SSDSE-B-2026 は全数 (悉皆) データなのでサンプリング不要。

🧭 ケーススタディ: 人口あたり病院数を構造化データで導く 8 ステップ

  1. 仮説: 「都市部 (大県) ほど人口あたり病院数が少ない」。
  2. 列選定: A1101 (総人口), I510120 (病院数)。
  3. 事前検証: df[['A1101','I510120']].describe() で範囲を確認。 病院数 13〜647 件、 人口 54 万〜1,408 万。
  4. 派生列: df['人口10万あたり病院数'] = df['I510120'] / df['A1101'] * 100000
  5. ソート: df.sort_values('人口10万あたり病院数', ascending=False)
  6. 結果確認: 上位は高知・徳島・鹿児島など地方県、 下位は東京・神奈川・埼玉。
  7. 仮説検証: 仮説と一致 → 大県ほど人口あたり病院数は少ない傾向。
  8. 注意点: 病院数の定義 (病床数 20 以上を病院、 それ未満は診療所) を必ず併記。

この 8 ステップ自体が、 構造化データを扱う際の汎用フレームワークです。 SSDSE-B-2026 では数十秒で完了しますが、 大規模 DB でも各ステップは同じ。

🗺 概念マップ: 構造化データを中心とした知識ネットワーク

構造化データは、 統計・計算機科学・データベース・機械学習の交差点に位置します。

structured data CSV / Parquet RDB スキーマ DataFrame SSDSE-B 47県 半構造化 JSON 非構造データ

🔗 隣接手法への橋渡し

構造化データは「行 × 列のスキーマが事前定義されたデータ」。 SSDSE-B-2026 (47 行 × 112 列の都道府県別指標) は典型的構造化データで、 RDB / CSV / Parquet に格納可能、 SQL でクエリ可能、 pandas DataFrame に直接読み込める。 対照は非構造化データ (画像・自由テキスト・音声) と半構造化データ (JSON / XML / HTML)。

構造化データはスキーマ固定で柔軟性に欠ける反面、 分析パイプラインがシンプル。 SSDSE-B-2026 を使うと「整っているデータで学習」が可能、 実務での非構造化→構造化の苦労を後で経験できる順番。

🌳 手法選択フロー

構造化データを扱うときの形式・エンジン選択フロー。

  1. ① データ量は? < 1GB → CSV + pandas で完結。 1〜100GB → Parquet + DuckDB / PostgreSQL。 > 100GB → BigQuery / Snowflake / Spark。 SSDSE-B-2026 (約 50KB) は pandas のみで十分。
  2. ② 主キーは明確か? SSDSE-B は (年, 地域コード) で一意。 他データと結合するなら結合キーの一意性を df.drop_duplicates() で確認。
  3. ③ 横持ち / 縦持ち? SSDSE-B は横持ち (wide: 列が指標名)。 BI ツールへの投入や時系列分析では pd.melt で縦持ち (long) に変換。 用途で使い分け。

SSDSE は「データレイク (非構造化が混在) ではなくデータウェアハウス (構造化済)」の典型。 学習用には最適、 実務応用時は半構造化を経由する設計を別途習熟する。

🔬 解説深化: スキーマの「物理的実体」— dtype とメモリで構造化を実測する

本ページの前半では、 スキーマを Codd の関係モデルという数学的な約束事として説明しました。 この深化セクションでは視点を変え、 その約束事が pandas に読み込んだ瞬間にメモリ上の「型 (dtype) とバイト列」という物理的実体に変わる様子を、 SSDSE-B-2026 (564 行 × 112 列、 2012–2023 年度) の実測値だけで追いかけます。 抽象的なスキーマ論と、 df.info() に出てくる生々しい数字とを往復できるようになるのが目標です。

🎨 直感 — 「スキーマを読む」とは dtype の分布を読むこと

SSDSE-B-2026 を pd.read_csv(..., encoding='cp932', skiprows=[1]) で読むと、 112 列の dtype 内訳は実測で int64 が 104 列、 float64 が 6 列、 object が 2 列 (Code と Prefecture) になります。 この 3 行の集計 (df.dtypes.value_counts()) を見るだけで、 「このデータはほぼ整数カウントの統計表で、 識別子が 2 列だけある」という設計思想が読み取れます。 さらに float64 の 6 列の正体を確かめると:

列コード意味 (2 行目の日本語ヘッダより)float64 である理由
A4103合計特殊出生率2023 年は 0.99 (東京都) 〜 1.60 (沖縄県) と本質的に小数
B4101年平均気温2023 年は 11.0℃ (北海道) 〜 23.8℃ (沖縄県)
B4102 / B4103最高気温 / 最低気温同じく 0.1℃ 刻みの測定値
B4109降水量 (年間)0.5mm 単位の測定値
H5614ごみのリサイクル率百分率の小数

つまり「気象測定値と率だけが小数、 残りは全部カウント」という意味論が dtype にそのまま写像されているのです。 重要なのは、 pandas では欠損 (NaN) を含む整数列も float64 に化けるため、 通常「float64 の列 = 小数か欠損持ちのどちらか」なのですが、 SSDSE-B-2026 は実測で全 564 行 × 112 列に NaN が 1 個もない (df.isna().sum().sum() が 0) ので、 この 6 列は「純粋に小数だから float」だと確定できます。 dtype 分布 → 欠損数 → 値域、 の 3 段確認は、 初見の構造化データに対する最速の「スキーマ健康診断」です。

⚠️ 落とし穴 (重要) — 1 行のヘッダが 112 列の型を全滅させる

SSDSE ファミリーは 1 行目が列コード、 2 行目が日本語の項目名という 2 段ヘッダ構造です。 ここで skiprows=[1] を忘れるとどうなるかを実測すると、 結果は劇的です:

読み方shape (実測)dtype 内訳 (実測)
skiprows=[1] あり (正)(564, 112)int64 × 104、 float64 × 6、 object × 2
skiprows=[1] なし (誤)(565, 112)object × 112 (全列が文字列化)

データ行に「総人口」「年平均気温」といった日本語文字列が 1 行だけ混入するため、 pandas の型推論は 112 列すべてを object (文字列) と判定します。 行数の差はわずか 1 (565 vs 564) なので df.shape のチェックでは見落としがちですが、 型は全滅しており、 df['A1101'].mean() は失敗するか無意味な結果になります。 これは「構造化データの構造は、 ファイルが構造化されているだけでは足りず、 読み込み手順まで含めて初めて成立する」ことの最小の実証例です。 対策は本文の落とし穴と同じく df.dtypes の即時確認ですが、 このケースでは df.dtypes.value_counts() の 1 行で「object × 112」という異常が一目で見えます。

もう 1 つ、 縦持ち変換にも型の罠があります。 564 行 × 112 列を df.melt(id_vars=['SSDSE-B-2026','Code','Prefecture']) で縦持ちにすると、 実測で 61,476 行 × 5 列 (= 564 行 × 109 指標列) になりますが、 このとき value 列の dtype は実測で float64 に統一されます。 int64 の 104 列と float64 の 6 列が 1 本の列に混ざるため、 型の細かい方に揃えられるのです。 SSDSE-B-2026 の人口最大値 14,086,000 (2023 年・東京都) 程度なら float64 (仮数 53 ビット) で正確に表現できますが、 「列ごとの型情報は melt の瞬間に失われる」こと自体は覚えておく価値があります。 横持ち (wide) は型をスキーマとして保持する形式、 縦持ち (long) は型を犠牲に操作の一様性を得る形式、 というトレードオフです。

🚀 発展 — メモリ上の構造化: category 型による辞書圧縮の実測

構造化データの「列に同じ種類の値が並ぶ」という性質は、 圧縮の余地そのものです。 SSDSE-B-2026 で実測すると:

category 型の中身は「47 通りの文字列辞書 + 各行には辞書番号 (小さい整数) だけ」という辞書圧縮で、 これは列指向フォーマット Parquet の dictionary encoding や、 データウェアハウスの列圧縮とまったく同じ原理です。 SSDSE-B-2026 の 60 KB では体感差はありませんが、 564 行が 564 万行になっても圧縮率の理屈は同一なので、 この小さな実測が 10⁶ 行スケールの設計判断 (本文の規模別 4 階層表を参照) の縮図になっています。 「行 = 観測、 列 = 変数」という論理構造が、 メモリ層では「列 = 同型の値の配列 = 圧縮単位」という物理構造に対応する — この二層対応こそが、 構造化データが大規模化しても高速に扱える根本理由です。

🔗 関連ページ