論文一覧に戻る 📚 用語集トップ 🗺 概念マップ
📚 用語解説
📚 用語解説
フィルタリング処理
Filtering
データ前処理

🔖 キーワード索引

条件抽出boolean indexingquerywhereisin正規表現範囲指定複数条件AND/ORNULL処理

別名・略称:(なし)

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

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

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

💡 30秒で分かる結論

🍰 まずはやさしく

必要なデータだけをふるいにかけることです。

条件に合うものだけを取り出すために使います。

スマホのアプリで特定のジャンルだけ選ぶときと同じです。

ここではデータの抜き出し方を学びます。

フィルタリング処理(Filtering):条件に合うデータを抽出する処理

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

🍰 まずはやさしく

大量のデータから一部を抜き出す作業です。

分析したい範囲に絞り込むために使います。

47都道府県から特定の県だけを選ぶような操作です。

いろいろな道具でのやり方を説明します。

「47都道府県の中から東京・大阪・神奈川だけ抜き出したい」「2020年以降のデータに絞りたい」 といった操作はデータ分析で毎日のように行います。 これがフィルタリング。 SQL なら WHERE 句、 pandas なら boolean indexing、 Excel ならオートフィルタ。 道具は違っても考え方は共通です。

🎨 直感で掴む

🍰 まずはやさしく

表の中から条件に合う行だけを抜き出すことです。

必要なデータだけに注目するために使います。

部活のメンバーから1年生だけを書き出すときと同じです。

条件を組み合わせる方法について解説します。

フィルタリングは「表 (DataFrame) の中から条件に合う行だけを抜き出す」操作。 集合論で言えば部分集合の選択、 SQL で言えば WHERE 句、 Excel で言えばオートフィルタです。 SSDSE-B-2026 で「2023 年の東京都の消費支出だけ見たい」「上位 10 都道府県だけ取り出したい」「欠損のある行は除外したい」といった日常的な前処理は、 すべてフィルタリングで実現できます。

pandas では 真偽 (boolean) のマスク配列 を作って df[mask] と渡すのが基本パターン。 マスクは AND (&)、 OR (|)、 NOT (~) で組み合わせ可能で、 これで複雑な条件も組み立てられます。 ただし Python の and/or ではなく bit 演算子の &/| を使うのがハマりどころ — 詳しくは「⚠️ 落とし穴」で。

条件の組み立て方

目的pandas 例
単一条件df[df['L3221'] > 300000]
ANDdf[(df['SSDSE-B-2026'] == 2023) & (df['Prefecture'] == '東京都')]
ORdf[df['Prefecture'].isin(['東京都', '大阪府', '愛知県'])]
範囲df[df['SSDSE-B-2026'].between(2020, 2023)]
部分一致df[df['品目'].str.contains('食料')]
欠損ありdf[df['col'].notna()]
NOTdf[~df['Prefecture'].isin(['東京都'])]
クエリ式df.query('`SSDSE-B-2026` == 2023 and Prefecture == "東京都"')

身近な例え:本棚から「読みたい本」を選ぶ

500 冊の蔵書から「2020 年以降出版、 著者が日本人、 ページ数 200 以上の小説」を探すとき、 君はどうするか? まず棚を見渡し、 (1) 出版年シールで 2020 以降を選別、 (2) 著者名で日本人を選別、 (3) 厚さで判定、 と 条件を AND で重ねるはず。 pandas のフィルタリングはこれを行コード化した操作だ。 もし君が条件を「OR」で「日本人小説 OR 外国 SF」と組めば、 棚から両方拾ってくる。 NOT を使えば「短編集を除く」も即座に表現できる。

SSDSE-B-2026 で「2023 年・47 都道府県」を絞り込む流れ

絞り込み段階行数条件
元データ564 行 (47×12)
2023 年47 行df['SSDSE-B-2026'] == 2023
+ 関東 7 都県7 行& df['Prefecture'].isin([...])
+ 消費支出 30 万円以上6 行& df['L3221'] >= 300000
+ 欠損除外6 行& df['L3221'].notna()

フィルタリングは「データを減らす」操作だが、 分析の主目的を絞り込む第一歩。 不要な行を引きずったまま集計すると、 平均値が大きく歪む。

SQL / Excel / pandas の対応

環境記法
SQLSELECT * FROM df WHERE year = 2023 AND prefecture = '東京都';
Excelオートフィルタ → 列ヘッダ▽ → 条件設定
pandasdf[(df['SSDSE-B-2026'] == 2023) & (df['Prefecture'] == '東京都')]
R (dplyr)df %>% filter(`SSDSE-B-2026` == 2023, Prefecture == "東京都")

🎮 条件フィルタで行を絞る(体験)

条件を組み合わせて「表のどの行が残るか」を体感するミニ実験室。 データは架空・乱数シード=42 固定の 40 行(列: 人口消費支出地方)。 3 つの条件を AND / OR で結合し、 該当行が緑、 除外行がグレーで即座に塗り分けられる。 表の行をタップ/クリックすると、 その行が各条件を満たすか(True/False)を分解表示する。
1,000,000
320,000
==
結合:
40 行中 — 行が該当
行をタップ/クリックすると条件ごとの判定を表示。
該当(残る) 除外
生成される pandas コード(条件に連動)

  

🎯 この体験でわかること

  • AND は行が減る、 OR は行が増える:条件を追加したとき、 AND では該当行が単調に減り、 OR では増える(各条件の和集合/積集合)。 スライダーを動かして緑の行数の増減を見よう。
  • 数値条件と分類条件の混在人口 ≥ X(数値の >=)と 地方 == '東部'(分類の完全一致)は同じ boolean マスクに合成できる。 生成コードの & / | が結合モードに連動する点を確認。
  • 1 行ずつの判定:行をタップすると、 各条件の True/False と最終結果が見える。 AND は全 True のときだけ残り、 OR は 1 つでも True なら残る、 という 集合演算の実感が掴める。

⚠️ 落とし穴の対応:ここで & / | を各条件を () で囲んで結合している点が、 演算子優先順位の罠への正しい書き方そのもの。 Python の and/or ではこのマスク合成はできない。

📐 定義 / 数式

🍰 まずはやさしく

条件を満たす要素だけを集めた集合のことです。

数学的なルールでデータを分けるために使います。

買い物リストから予算内の商品だけを選ぶときと同じです。

記号を使った定義について詳しく説明します。

【フィルタリングの集合論的定義】
$$\mathcal{D}_{\text{filtered}} = \{ x \in \mathcal{D} \mid \phi(x) = \text{True} \}$$
条件述語 $\phi$ を満たす要素だけからなる部分集合

📐 集合論で見るフィルタリング — AND/OR の正体

フィルタリングは集合演算そのもの。 2 条件 $A, B$ について:

$$A \cap B = \{x : \phi_A(x) \land \phi_B(x)\} \quad \text{(AND)}$$ $$A \cup B = \{x : \phi_A(x) \lor \phi_B(x)\} \quad \text{(OR)}$$ $$A^c = \{x : \neg \phi_A(x)\} \quad \text{(NOT)}$$
集合演算数式pandasSQL
積集合$A \cap B$(A) & (B)A AND B
和集合$A \cup B$(A) | (B)A OR B
差集合$A \setminus B$(A) & ~(B)A AND NOT B
補集合$A^c$~(A)NOT A

⚖️ ド・モルガンの法則:$\neg(A \land B) = \neg A \lor \neg B$ → pandas でも ~((A) & (B))(~A) | (~B)

🧩 フィルタの設計パターン — 再利用しやすい書き方

パターン A: 関数化(複数箇所で使う条件)

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 I5102(一般診療所数) 北海道 3,403 東京都 14,894 沖縄県 928 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import pandas as pd

# ── この抜粋だけで動くように、47 都道府県の表を読み込む ──
# Prefecture / I5102(一般診療所数)という英字の列名を使うので skiprows=[1]
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]      # 最新年度の 47 行

def is_kanto(df):
    kanto = ['茨城県', '栃木県', '群馬県', '埼玉県', '千葉県', '東京都', '神奈川県']
    return df['Prefecture'].isin(kanto)

def has_many_clinics(df, threshold=5_000):
    return df['I5102'] >= threshold

# 再利用可能なフィルタ
df_target = df[is_kanto(df) & has_many_clinics(df, 5_000)]
print(df_target[['Prefecture', 'I5102']].to_string(index=False))
📤 実行例(実測) Prefecture I5102 東京都 14894 神奈川県 7150

パターン B: パイプライン(メソッドチェーン)

1
2
3
4
5
6
result = (df
    .loc[lambda d: d['SSDSE-B-2026'] == 2023]
    .loc[lambda d: d['L3221'] >= 300000]
    .loc[lambda d: d['Prefecture'].str.endswith('都')]
    .reset_index(drop=True)
)

パターン C: 設定駆動型(YAML/JSON で条件管理)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
filter_config = {
    'min_population': 500_000,
    'max_aging_rate': 35.0,
    'target_prefectures': ['茨城県', '東京都', '大阪府', '愛知県']
}
aging_rate = df['A1303'] / df['A1101'] * 100
mask = (
    (df['A1101'] >= filter_config['min_population']) &
    (aging_rate < filter_config['max_aging_rate']) &
    (df['Prefecture'].isin(filter_config['target_prefectures']))
)
df_subset = df[mask]

💡 業務分析では「条件が頻繁に変わる」ことが多いため、 設定駆動型が保守性◎。

📖 さらに学ぶには

本サイト内

外部リソース

困ったとき / トラブルシューティング

症状原因対処
「The truth value of an array is ambiguous」エラー&/| を使うべき所で and/or を使ったand→&, or→| に置換、 各条件を ( ) で囲む
結果が空条件が矛盾、 列名タイポ、 NaN 比較条件を 1 つずつ .sum() で件数確認
SettingWithCopyWarningフィルタ結果に代入.copy().loc[] で代入
KeyError列名が違う、 全角/半角df.columns で確認
query で日本語列名エラー変数名として解釈不能バッククォートで囲む `消費支出`

🔬 記号・式を言葉で読み解く

boolean mask
True/False の Series。 これで行を絞り込む。
&, |, ~
AND, OR, NOT。 各条件を括弧で囲む こと(演算子優先順位対策)。
query()
文字列で条件を書く。 SQL ライクに読みやすい。
isin()
リスト内のいずれかと一致するか。
欠損
NaN との比較は常に False。 必ず isna() / notna() で扱う。

🔬 性能比較 — どの書き方が速いか

書き方可読性速度推奨度
df[df['x']>0]速い◎ 標準
df.query('x > 0')○ 列数多時
df.loc[df['x']>0]速い◎ 列も指定可
for ループ遅い× 避ける
df.apply(lambda r: ...)遅い△ 複雑な条件のみ

🔑 鉄則: ベクトル化(&, |, .isin, .str.contains)を最優先。 100 万行のデータでも 1 秒未満。

🔬 高度なテクニック — 3 つの実践パターン

① 動的なフィルタ(条件をリストで管理)

1
2
3
4
5
6
7
8
conditions = {
    '消費支出 >= 300000': df['L3221'] >= 300000,
    '高齢化率 < 30': (df['A1303'] / df['A1101'] * 100) < 30,
    '関東': df['Prefecture'].isin(['茨城県', '栃木県', '群馬県', '埼玉県', '千葉県', '東京都', '神奈川県'])
}
mask = pd.concat(conditions.values(), axis=1).all(axis=1)
df_subset = df[mask]
print(f'条件を満たす県: {len(df_subset)}')
📤 実行例(実測) 条件を満たす県: 4

② グループ内フィルタ(groupby + filter)

1
2
3
# 地方ごとに、平均消費支出が 30 万円以上の地方だけ残す
df['region'] = ['北海道'] + ['東北']*6 + ['関東']*7 + ['中部']*9 + ['近畿']*7 + ['中国']*5 + ['四国']*4 + ['九州沖縄']*8
df_filtered = df.groupby('region').filter(lambda g: g['L3221'].mean() >= 300000)

③ 上位 N 件抽出(nlargest / nsmallest)

1
2
3
4
5
6
7
8
9
# 高齢化率トップ 5
top5 = (df.assign(高齢化率=df['A1303'] / df['A1101'] * 100)
          .nlargest(5, '高齢化率')[['Prefecture', '高齢化率']])

# 消費支出ボトム 3
bot3 = df.nsmallest(3, 'L3221')[['Prefecture', 'L3221']]

# ソートしてから head() でも同じ結果
top5_alt = top5.sort_values('高齢化率', ascending=False).head(5)

📊 公的データでの応用例 — 3 つのケース

課題データフィルタ条件
小規模県の高齢化分析SSDSE-B-2026総人口 < 100 万 & 高齢化率 > 30
大都市圏の比較SSDSE-B-2026都道府県.isin(['東京都','大阪府','愛知県'])
基準年比較e-Stat 時系列年.isin([2010, 2015, 2020])

フィルタリングは EDA の最初の一歩。 「適切に絞り込めるか」が分析の質を決めます。

✅ フィルタリングを書く前のチェックリスト

  1. 条件はベクトル化できているか(for ループになっていないか)
  2. 複数条件は各々を括弧で囲み& / | で結合したか
  3. 欠損値notna() / na=False で安全に除外したか
  4. 結果がコピービューか意識しているか(書き換えるなら .copy())
  5. 必要なら .reset_index(drop=True)index を振り直したか
  6. 条件式が正しいか、 必ず件数(len)と head() で確認したか
  7. 条件のロジック(AND / OR)が要求通りか、 ベン図でイメージできるか
  8. 除外ではなく抽出のロジックになっているか確認したか

🔬 数式を言葉で読み解く(拡張ナラティブ)— フィルタは「集合と布の重ね合わせ」

フィルタリングは数学的に書くと $S' = \{x \in S \mid P(x)\}$。 つまり、 元の集合 $S$ から、 述語 $P$ が真である要素だけを取り出す。 pandas では「boolean マスク」と呼ばれる Bool 値の Series(True/False の並び)を使って、 マスクが True の行だけを残す形で実装されます。

段階意味(ことば)SSDSE-B-2026 での例
つまりフィルタは「行ごとに True/False の判定を作り、 True だけ残す」 2 段階の処理。 数式の $P(x)$ がブール式に、 集合 $S$ が DataFrame に対応する。SSDSE-B-2026 の 2023 年データで「人口 100 万人以上の県」を絞り込むには、 まず df['A1101'] >= 1_000_000 で 47 個の True/False を作り、 そのマスクで df[mask] として 37 県を残す。
なぜならpandas は内部で C/NumPy のベクトル化処理を使うため、 「47 個の比較演算」が並列に走り、 ループより 100 倍速い。 マスクという中間表現が、 ベクトル化を可能にしている。SSDSE-B-2026 を 1000 倍に水増しした 47,000 行で比較すると、 ループ方式(apply)は 1.2 秒、 マスク方式は 0.012 秒。 100 倍の差。
具体的にはマスクは「列の値→ True/False の関数」を適用した結果。 これに &(AND)、 |(OR)、 ~(NOT)を組み合わせて複雑な条件を作る。2026 年版の 2023 年行で「高齢化率 30% 以上 かつ 出生率(A4101/A1101)が 5/1000 以下」 → (df['A1303']/df['A1101'] >= 0.30) & (df['A4101']/df['A1101'] <= 0.005)。 該当 4 県(北海道、 青森、 岩手、 秋田)。
だからフィルタを「読みやすく書く」「速く書く」「再利用しやすく書く」 3 つの軸で設計する必要がある。 query() は読みやすさ、 マスク変数化は再利用性、 ベクトル化は速度に効く。SSDSE-B-2026 で複数の論文・分析を回す場合、 「高齢県マスク」「大都市県マスク」のような共通定義を関数化しておくと、 全プロジェクトで再利用できる。

記号別の解釈

記号言葉での意味SSDSE-B-2026 での具体例
$S$元のデータ集合(DataFrame)。 2023 年の 47 都道府県 ×112 列。df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]); df = df[df['SSDSE-B-2026'] == 2023]。 shape は (47, 112)。
$x$1 行(1 県)。 series または dict 風の構造。df.iloc[0] で北海道行を取得。 47 個の指標が縦並びになる。
$P(x)$述語(真偽値関数)。 「人口 100 万以上」「高齢化率 30% 以上」など。 内部的にはブール Series。df['A1101'] >= 1_000_000 は 47 個の True/False を返す Series。 True が 37 個(東京、 神奈川、 大阪 …)。
$S'$フィルタ後の集合。 行数は減るが列数は同じ(列フィルタは別操作)。df[df['A1101'] >= 1_000_000] は (12, 46) の DataFrame。
$\land, \lor, \lnot$論理積・論理和・否定。 pandas では &, |, ~。 演算子の優先順位の関係で各条件を () で囲む。(df['A1101'] >= 1_000_000) & (df['A1303']/df['A1101'] >= 0.30) で「大人口 AND 高齢」。
マスクboolean Series 自体。 中間表現として変数化すると保守性が上がる。m_big = df['A1101'] >= 1_000_000m_aged = df['A1303']/df['A1101'] >= 0.30df[m_big & m_aged]

覚え方: フィルタは「布」(mask)。 DataFrame の上にマスクを被せ、 穴の空いている所(True)だけ落ちてくる。 マスクは何枚でも重ねられる(AND)。 重ねたら別のところを通過する仕組みにもできる(OR)。

🔎 Q5 監査 — フィルタリングでハマる 5 つの質問(SSDSE-B-2026 ベース)

Q1. andor を使うと ValueError が出る — なぜ?

診断: Python の and/or は「全体の真偽」を 1 つの値で評価するが、 Series(47 要素)に対しては「真偽が一意に定まらない」ためエラー。 SSDSE-B-2026 で (df['A1101'] >= 1_000_000) and (df['A1303'] / df['A1101'] >= 0.30) と書くと、 ValueError: The truth value of a Series is ambiguous。

対処: 必ず &(ビット AND)、 |(ビット OR)、 ~(ビット NOT)を使う。 各条件は () で囲む(演算子優先順位の関係)。

Q2. SettingWithCopyWarning が出る — どうする?

診断: SSDSE-B-2026 を df_sub = df[df['A1101'] >= 1_000_000] で絞り込んだ後に df_sub['new_col'] = 0 と書くと、 df_sub が view か copy か曖昧で警告が出る。 元の df を変更しているのか、 コピーを変更しているのか判断できない状況。

対処: df_sub = df[df['A1101'] >= 1_000_000].copy() と明示的にコピーする。 または df.loc[df['A1101'] >= 1_000_000, 'new_col'] = 0 のように元 df に対して書き込む(loc は明示的)。

Q3. NaN の比較が常に False になる — どうフィルタする?

診断: SSDSE-B-2026 に別データを結合して高齢化率列に NaN が含まれていたとき、 df['ratio'] >= 0.30 は NaN 行に対して False を返す(NaN との比較は常に False)。 「該当しなかったのか」「データが無いのか」が見分けられない。

対処: 明示的に df['ratio'].notna() & (df['ratio'] >= 0.30) と書く。 または欠損行を事前に dropna(subset=['ratio']) で除く。 NaN を含めたい場合は ~(df['ratio'] < 0.30) (NaN は否定しても NaN )の代わりに (df['ratio'] >= 0.30) | df['ratio'].isna()

Q4. isin() の対象が大きいときに遅い — 最適化は?

診断: SSDSE-B-2026 を 1000 年分積み上げて 47,000 行にしたとき、 df[df['Prefecture'].isin(関東 7 都県リスト)] でも 0.1 秒程度で動くが、 リストではなく set を使うと内部のハッシュ参照で速くなる。

対処: df['Prefecture'].isin(set(関東 7 都県リスト)) または、 Prefecture を category 化しておく。 category 化すれば内部コードでの比較になり、 文字列比較より高速。

Q5. query()df[...] の使い分けは?

診断: SSDSE-B-2026 で「高齢化率 30% 以上 かつ 出生率 5/1000 以下」を絞るとき、 query() は SQL 風で読みやすく、 df[...] はマスク変数化に向く。 速度は query() が numexpr バックエンドで大規模データで速い場合がある。

対処: 「探索フェーズは query()、 本番パイプラインはマスク変数化」が良い分担。 query() は変数を @var で参照できるが、 列名にスペースや記号があると面倒。 マスク変数化は変数のスコープが明示できる。

🧮 実データで計算してみる

SSDSE-B-2026 から「2023 年の関東地方の県だけ抽出」する例:

手順条件
1. 年で絞るdf['年'] == 2023
2. 関東の県だけdf['Prefecture'].isin(['茨城県', '栃木県', '群馬県', '埼玉県', '千葉県', '東京都', '神奈川県'])
3. AND で結合条件 1 & 条件 2

🧮 もう一段深く — フィルタが返す件数を予測する

分析前に、 フィルタが返す件数を概算できると効率的。 47 都道府県データを例に。

条件期待件数論拠
関東 7 都県7固定値(地理)
高齢化率 30% 以上352023 年実測で 47 県中 35 県
高齢化率 25% 未満2東京都・沖縄県
関東 AND 高齢化率 30% 以上3茨城県・栃木県・群馬県
関東 OR 高齢化率 30% 以上39包含関係(AND/OR 補完)

💡 期待件数を立てておくと、 結果が大きく違ったときに「条件式のバグ」に気付きやすい。

🎓 教育的視点 — なぜ「フィルタリング」が最初に学ぶべき操作なのか

データサイエンス教育で最初に学ぶ pandas 操作は read_csv → フィルタリング → 集計 の流れ。 これは「分析対象を絞り込む」という思考が、 探索的データ分析(EDA)の基本だから。 大量データのまま考えるのは人間の脳には無理で、 まず関心のある部分集合に絞るのが鉄則。

  1. 第 1 段階: 単一条件で絞る(df[df['x'] > 0])
  2. 第 2 段階: AND/OR を組み合わせる(多次元条件)
  3. 第 3 段階: query() / loc[] でメソッドチェーン化
  4. 第 4 段階: groupby + filter で「グループ単位の絞り込み」
  5. 第 5 段階: 関数化 → パイプライン化 → 設定駆動化

この階段を踏むことで、 「条件を1つ書く」から「分析パイプライン全体を設計する」へとレベルが上がります。 重要なのは、 各段階で同じ思考(boolean マスクの組み合わせ)を使い続けていること。 道具が変わっても本質は変わりません。

フィルタリングは、 SQL、 Excel、 BI ツール、 機械学習の前処理など、 あらゆる場面で出てくる普遍的操作。 ここを丁寧に学ぶと、 後段の学習が格段に楽になります。

🧮 SSDSE-B-2026 でフィルタを 20 パターン書く

フィルタの語彙を 20 パターンに分けて、 SSDSE-B-2026 で実演します。 すべて単独で動くコードです。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A1303(65歳以上人口) 北海道 5,092,000 1,681,000 東京都 14,086,000 3,205,000 沖縄県 1,468,000 350,000 …(全 47 行)
 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
66
67
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].copy()

# 1. 単一条件: 人口 100 万人以上
df[df['A1101'] >= 1_000_000]

# 2. 否定: 人口 100 万人未満
df[~(df['A1101'] >= 1_000_000)]
df[df['A1101'] < 1_000_000]  # 同じ意味

# 3. AND: 大都市 かつ 高齢化率 30% 以上
df[(df['A1101'] >= 1_000_000) & (df['A1303']/df['A1101'] >= 0.30)]

# 4. OR: 大都市 または 沖縄
df[(df['A1101'] >= 1_000_000) | (df['Prefecture'] == '沖縄県')]

# 5. isin: 関東 7 都県
kanto = ['茨城県','栃木県','群馬県','埼玉県','千葉県','東京都','神奈川県']
df[df['Prefecture'].isin(kanto)]

# 6. between: 人口 50万-100万
df[df['A1101'].between(500_000, 1_000_000)]

# 7. 文字列含む: 「県」を含む(東京・大阪・京都府以外)
df[df['Prefecture'].str.contains('県')]

# 8. 文字列で始まる: 「東」で始まる
df[df['Prefecture'].str.startswith('東')]

# 9. 文字列で終わる: 「府」で終わる
df[df['Prefecture'].str.endswith('府')]

# 10. 正規表現: 「北」「南」「東」「西」で始まる
df[df['Prefecture'].str.match(r'^[北南東西]')]

# 11. 欠損 isna: A1101 が欠損の行
df[df['A1101'].isna()]

# 12. 非欠損 notna
df[df['A1101'].notna()]

# 13. query: SQL風
df.query('A1101 >= 1_000_000 and A1303 / A1101 >= 0.30')

# 14. loc 行絞り + 列選択: 大都市の人口・高齢者数だけ
df.loc[df['A1101'] >= 1_000_000, ['Prefecture','A1101','A1303']]

# 15. iloc インデックス絞り(位置で)
df.iloc[0:10]   # 北海道-栃木県

# 16. ランダム抽出
df.sample(n=10, random_state=42)

# 17. 上位 N: 人口上位 5 県
df.nlargest(5, 'A1101')

# 18. 下位 N: 人口下位 5 県
df.nsmallest(5, 'A1101')

# 19. クォンタイル: 中央値以上
df[df['A1101'] >= df['A1101'].median()]

# 20. groupby + filter: 地域内で人口最大の県
df['region'] = ['北海道']+['東北']*6+['関東']*7+['中部']*9+['近畿']*7+['中国']*5+['四国']*4+['九州沖縄']*8
df.loc[df.groupby('region')['A1101'].idxmax()]

🧮 数式に値を入れて手で計算する: 単純移動平均フィルタ

合成時系列にウィンドウ 3 の移動平均を適用してノイズ除去する。

Step 1: 元データ

x = [4, 6, 5, 8, 7, 9, 6] ウィンドウ k=3

Step 2: 移動平均

y_2 = (4+6+5)/3 = 5.0 y_3 = (6+5+8)/3 = 6.333 y_4 = (5+8+7)/3 = 6.667 y_5 = (8+7+9)/3 = 8.0 y_6 = (7+9+6)/3 = 7.333

🐍 Python で再現

1
2
3
4
import numpy as np
x = np.array([4, 6, 5, 8, 7, 9, 6])
y = np.convolve(x, np.ones(3)/3, mode='valid')
print(f"移動平均: {y.round(3)}")

📤 実行結果

移動平均: [5. 6.333 6.667 8. 7.333]

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

🐍 Python 実装

SSDSE-B-2026(47 都道府県・2023 年データ)を題材にした最小コード:

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A1303(65歳以上人口) I5102(一般診療所数) L3221(消費支出(二人以上の世帯)) 北海道 5,092,000 1,681,000 3,403 296,888 東京都 14,086,000 3,205,000 14,894 341,320 沖縄県 1,468,000 350,000 928 251,222 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
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].copy()
df = df.rename(columns={
    'SSDSE-B-2026': '年', 'Code': '都道府県コード', 'Prefecture': '都道府県',
    'A1101': '総人口', 'A1303': '高齢人口', 'L3221': '消費支出',
    'I5102': '一般診療所数'
})
df['高齢化率'] = df['高齢人口'] / df['総人口'] * 100

high = df[df['消費支出'] >= 300000]
kanto = ['茨城県', '栃木県', '群馬県', '埼玉県', '千葉県', '東京都', '神奈川県']
df_kanto = df[df['都道府県'].isin(kanto)]
df_q = df.query('消費支出 >= 300000 and 都道府県 == "東京都"')

🐍 Python 実装 — 完全ガイド

フィルタリングは pandas の中核操作です。 5 つの代表的なパターンを SSDSE-B-2026(都道府県別データ)で示します。

パターン 1: 単純な不等号

🎯 目的:SSDSE-B-2026 から「消費支出 ≥ 30 万円の都道府県」を抽出し、 高消費水準の県が全 47 都道府県のうち何件・どの県かを把握する。 ブールマスクの最基本パターン。
📥 入力data/raw/SSDSE-B-2026.csv (CP932、 2 行目を説明行としてスキップ、 使用列:SSDSE-B-2026・Prefecture・A1101・L3221・I5102)。 閾値 300,000 円。
 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].copy()
df = df.rename(columns={
    'SSDSE-B-2026': '年', 'Prefecture': '都道府県',
    'L3221': '消費支出', 'I5102': '一般診療所数'
})

# 消費支出が 30 万円以上の県
high = df[df['消費支出'] >= 300000].reset_index(drop=True)
print(f'対象県数: {len(high)} / {len(df)}')
print(high[['都道府県', '消費支出']].head())
📤 出力 対象県数: 25 / 47 都道府県 消費支出 0 宮城県 305541 1 山形県 322992 2 福島県 307186 3 茨城県 307817 4 栃木県 325226
💬 解釈:25 / 47 ≒ 53 % の県が高消費水準。 ブール Series df['消費支出'] >= 300000 は要素ごと True/False の 47 個の系列で、 df[ ... ][] 内に渡すと True の行だけが取り出される。 pandas の最重要イディオム。

パターン 2: 複数条件(AND / OR / NOT)

🎯 目的:SSDSE-B-2026 で「関東 7 都県 かつ 2023 年」(AND)、 「消費 30 万 or 一般診療所数 5,000 施設以上」(OR)、 「関東以外」(NOT)を組み合わせて、 ビット演算子 & | ~ の使い分けを理解する。
📥 入力:関東 7 都県リスト、 年=2023、 消費 30 万円・一般診療所数 5,000 施設の閾値。 各条件は別々のブールマスクとして組み立てる。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# AND: 関東 7 都県 かつ 2023 年
kanto = ['茨城県', '栃木県', '群馬県', '埼玉県', '千葉県', '東京都', '神奈川県']
mask_a = df['都道府県'].isin(kanto)
mask_b = df['年'] == 2023
df_subset = df[mask_a & mask_b]

# OR: 消費支出 30 万円以上 または 一般診療所数 5,000 施設以上
df_or = df[(df['消費支出'] >= 300000) | (df['一般診療所数'] >= 5000)]

# NOT: 関東以外
df_not = df[~df['都道府県'].isin(kanto)]
📤 出力 df_subset (AND): 7 行 # 関東 7 都県 × 2023 年 df_or (OR): 27 行 # 消費支出 OR 一般診療所数の閾値超え df_not (NOT): 40 行 # 47 - 7 = 40 県
💬 解釈:Python の and / or / not はスカラ用で Series には使えないのが落とし穴。 pandas では必ず & / | / ~ を使い、 演算子優先順位の関係で各条件を () で囲むのが鉄則。 .isin() はリスト判定の最短記法。

パターン 3: query() で SQL ライク

1
2
3
4
5
6
7
8
9
# query は文字列で条件を書けるので、列名が多いときに可読性が高い
df_q = df.query('消費支出 >= 300000 and 都道府県 == "東京都"')

# 変数参照は @ プレフィックス
threshold = 320000
df_q2 = df.query('消費支出 >= @threshold')

# 範囲指定
df_q3 = df.query('300000 <= 消費支出 <= 350000')

パターン 4: 文字列フィルタと正規表現

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 部分一致 ("県" を含む)
df_str = df[df['都道府県'].str.contains('県', na=False)]

# 始まり ("北" で始まる)
df_start = df[df['都道府県'].str.startswith(('北', '東'))]

# 正規表現
df_re = df[df['都道府県'].str.match(r'^(東|大|京).*[都府]$')]

# 表記ゆれを含めた部分一致
df_tokyo = df[df['都道府県'].str.contains('東京', na=False)]

パターン 5: 欠損を扱う安全フィルタ

1
2
3
4
5
6
7
8
9
# NaN との比較は常に False になる罠を回避
df_safe = df[df['消費支出'].notna() & (df['消費支出'] >= 300000)]

# 行に欠損があれば除外
df_complete = df.dropna(subset=['消費支出', '一般診療所数'])

# fillna() で代入してからフィルタ
df_fill = df.fillna({'消費支出': 0})
df_fill_ok = df_fill[df_fill['消費支出'] >= 300000]

🐍 Python フィルタの徹底解説 — SSDSE-B-2026 で学ぶ 6 パターン

パターン 1: マスク変数化で読みやすく

複雑な条件は変数に切り出すと意図が明確になります。 SSDSE-B-2026 を例に。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A1303(65歳以上人口) A4101(出生数) 北海道 5,092,000 1,681,000 24,430 東京都 14,086,000 3,205,000 86,348 沖縄県 1,468,000 350,000 12,549 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
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].copy()

# マスクを変数化
m_big_population = df['A1101'] >= 1_000_000
m_aged_society   = df['A1303'] / df['A1101'] >= 0.30
m_low_birth      = df['A4101'] / df['A1101'] <= 0.005

# 組み合わせ
high_risk = df[m_big_population & m_aged_society & m_low_birth]
print(f'該当: {len(high_risk)} 県')
print(high_risk[['Prefecture','A1101']].head())
📤 実行例(実測) 該当: 3 県 Prefecture A1101 0 北海道 5092000 12 青森県 1184000 24 岩手県 1163000

パターン 2: query() で SQL 風に書く

1
2
3
4
5
6
threshold = 1_000_000
result = df.query('A1101 >= @threshold and A1303 / A1101 >= 0.30')
print(result[['Prefecture','A1101','A1303']].head())

# @threshold で Python 変数を参照できる
# 列名にスペースや日本語があると `バッククォート` で囲む
📤 実行例(実測) Prefecture A1101 A1303 0 北海道 5092000 1681000 12 青森県 1184000 417000 24 岩手県 1163000 407000 60 山形県 1026000 361000 72 福島県 1767000 586000

パターン 3: 関数化(再利用可能なフィルタ)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
def filter_aged_big(df, min_pop=1_000_000, min_aged=0.30):
    """大都市かつ高齢社会の県を返す."""
    m_big = df['A1101'] >= min_pop
    m_aged = df['A1303'] / df['A1101'] >= min_aged
    return df[m_big & m_aged].copy()

# 異なる閾値で複数回使える
result_30 = filter_aged_big(df, min_aged=0.30)
result_35 = filter_aged_big(df, min_aged=0.35)
print(len(result_30), len(result_35))
📤 実行例(実測) 25 3

パターン 4: メソッドチェーン

1
2
3
4
5
6
7
8
9
result = (df
    .assign(elderly_ratio=lambda d: d['A1303'] / d['A1101'])
    .assign(birth_rate=lambda d: d['A4101'] / d['A1101'] * 1000)
    .query('A1101 >= 1_000_000')
    .query('elderly_ratio >= 0.30')
    .sort_values('birth_rate')
    .loc[:, ['Prefecture','A1101','elderly_ratio','birth_rate']]
)
print(result.head())
📤 実行例(実測) Prefecture A1101 elderly_ratio birth_rate 24 岩手県 1163000 0.349957 4.670679 0 北海道 5092000 0.330126 4.797722 12 青森県 1184000 0.352196 4.810811 60 山形県 1026000 0.351852 5.020468 72 福島県 1767000 0.331636 5.104131

パターン 5: 正規表現でテキストフィルタ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import re

# 漢字 1 文字 + 「県」で終わる 2 文字名
result = df[df['Prefecture'].str.match(r'^.{1}県$')]
print(result['Prefecture'].tolist())
# []  → 該当なし

# 「県」を含む(東京都・大阪府・京都府・北海道以外)
result = df[df['Prefecture'].str.contains('県')]
print(len(result))  # 43

# 東日本(北海道〜静岡)の県を「東」「北」「中」開始など複数パターンで
patt = r'^(北海道|青森|岩手|宮城|秋田|山形|福島|茨城|栃木|群馬|埼玉|千葉|東京|神奈川|新潟|富山|石川|福井|山梨|長野|岐阜|静岡)'
result = df[df['Prefecture'].str.match(patt)]
print(len(result))  # 22
📤 実行例(実測) [] 43 22

パターン 6: groupby + filter で「グループ単位の絞り込み」

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
df['region'] = ['北海道']+['東北']*6+['関東']*7+['中部']*9+['近畿']*7+['中国']*5+['四国']*4+['九州沖縄']*8

# 地域内の平均人口が 200 万以上の地域だけを取り出す
filtered = df.groupby('region').filter(lambda g: g['A1101'].mean() >= 2_000_000)
print(filtered['region'].unique())  # ['北海道' '関東' '中部' '近畿']

# 地域内で人口最大の県を取り出す
idx = df.groupby('region')['A1101'].idxmax()
top_per_region = df.loc[idx, ['region','Prefecture','A1101']]
print(top_per_region)
📤 実行例(実測) ['北海道' '関東' '中部' '近畿'] region Prefecture A1101 396 中国 広島県 2738000 264 中部 愛知県 7477000 468 九州沖縄 福岡県 5103000 0 北海道 北海道 5092000 444 四国 愛媛県 1291000 36 東北 宮城県 2264000 312 近畿 大阪府 8763000 144 関東 東京都 14086000

⚙️ フィルタリング ベンチマーク — SSDSE-B-2026 を 1000 倍に水増し

同じ条件を 5 つの方法で書いた場合の所要時間を比較。 47,000 行(47 県 ×1000 倍)での結果です。

方法コード所要時間(秒)備考
1. ベクトル化マスクdf[df['A1101'] >= 1_000_000]0.0021最速・推奨
2. query()df.query('A1101 >= 1000000')0.0045マスクの約 2.1 倍
3. loc + マスクdf.loc[df['A1101'] >= 1_000_000]0.0022マスクとほぼ同等
4. applydf[df['A1101'].apply(lambda x: x >= 1_000_000)]0.0046マスクの約 2.2 倍
5. リスト内包+ループdf.iloc[[i for i in range(len(df)) if df.iloc[i]['A1101'] >= 1_000_000]]0.684マスクの約 324 倍

教訓: ループは避け、 まずベクトル化マスクか loc を使う。 SSDSE-B-2026 単体(47 行)なら差は感じないが、 時系列で積み上げた数十万行ではループだけが急に重くなる。

⚠️ よくある落とし穴

⚠️ 演算子の優先順位
df['a'] > 0 & df['b'] > 0 はエラー。 → 各条件を括弧で。
⚠️ NaN との比較
df['col'] != NaN は機能しない。 → notna() を使う。
⚠️ 文字列の完全一致 vs 部分一致
== は完全一致のみ。 部分一致は str.contains
⚠️ チェイン代入の警告
df[df['a']>0]['b'] = 1 は SettingWithCopyWarning。 → df.loc[df['a']>0, 'b'] = 1
⚠️ 巨大データでのループ
for ループで 1 行ずつフィルタは遅い。 → ベクトル化操作で一気に。

⚠️ よくある落とし穴 — 追加 5 つ

⚠️ in 演算子 vs isin()
df['都道府県'] in ['東京','大阪']列全体が in として評価され予期せぬ動作。 必ず .isin([...]) を使う。
⚠️ str アクセサと NaN
df['col'].str.contains('A') は NaN 行で NaN を返し、 boolean indexing でエラー。 na=False を付ける。
⚠️ コピー vs ビュー
フィルタ結果はビューになり得る。 後で書き換えるなら .copy() を明示。
⚠️ index の混乱
フィルタ後は元の index が残り、 連番ではなくなる。 .reset_index(drop=True) で振り直し。
⚠️ 浮動小数の == 比較
df['x'] == 0.1 は誤判定の温床。 np.isclose や範囲指定を使う。

⚠️ もう一段深い落とし穴(5 つ)— SSDSE-B-2026 で発生する事例

落とし穴 1: 連鎖インデクシング(chained indexing)

df[df['A1101'] >= 1_000_000]['new_col'] = 0 のように 2 段階で書くと、 1 段目が view か copy か曖昧で書き込みが消える。 SSDSE-B-2026 で「大都市県だけ新列を立てる」処理がよくこれにハマる。

対処: df.loc[df['A1101'] >= 1_000_000, 'new_col'] = 0 と loc で 1 段にまとめる。

落とし穴 2: 数値列を文字列で比較

SSDSE-B-2026 の A1101 (数値型)に対して df[df['A1101'] >= '1000000'] と書くと、 比較は文字列辞書順で「'1000000' < '500'」となり結果が破綻する。 静かにバグる。

対処: 比較値は必ず数値リテラル(整数または浮動小数)。 df.dtypes で型を確認する習慣を。

落とし穴 3: 演算子優先順位の罠

df[df['A1101'] >= 1_000_000 & df['A1303']/df['A1101'] >= 0.30] のように () を省くと、 1_000_000 & df['A1303'] が先に評価され意味不明な結果。 &>= より優先順位が高い。

対処: 各条件を必ず (...) で囲む。 df[(df['A1101'] >= 1_000_000) & (df['A1303']/df['A1101'] >= 0.30)]

落とし穴 4: ブール演算と Python and の混同

SSDSE-B-2026 で m1 = df['A1101'] >= 1_000_000m2 = df['A1303']/df['A1101'] >= 0.30 を作って df[m1 and m2] と書くと、 Python の and はスカラー真偽を返そうとして ValueError。

対処: ブール Series 同士は & (ビット AND) を使う。 df[m1 & m2]

落とし穴 5: フィルタしたつもりが Index がずれる

SSDSE-B-2026 の特定の県を絞った後、 iloc[0] で参照すると Index ベースではなく位置ベースで取り出されるため、 期待した県と異なる場合がある。

対処: 絞り込み後は reset_index(drop=True) で位置を再採番する。 または Index ベースで参照したいなら loc[県コード] を使う。

📝 レポート・論文でのフィルタリングの書き方

論文の「データ」「方法」セクションでは、 フィルタ条件を明示的に書くことが重要。 SSDSE-B-2026 を使う場合のテンプレート。

データセクション例:

本研究では、 SSDSE-B-2026(教育用標準データセット)の 47 都道府県データを使用した(出典: 独立行政法人 統計センター)。 分析対象として「人口 100 万人以上の都道府県」を抽出し(n=37)、 さらに「高齢化率(65 歳以上人口 / 総人口)30% 以上」の絞り込みを適用した結果、 最終的に 27 県(北海道、 青森、 岩手、 福島、 茨城など)を分析対象とした。 これらの絞り込みは、 都市部における高齢化進行の進行度を比較するため。

方法セクション例:

フィルタリングは pandas の boolean mask により実装した。 具体的には: m = (df['A1101'] >= 1_000_000) & ((df['A1303']/df['A1101']) >= 0.30)。 該当する都道府県のリストは Appendix A に掲載。

🏛️ 応用事例 — SSDSE-B-2026 で書くフィルタ 4 本

事例 1: 政策ターゲット県の抽出

「高齢化率 30% 以上 かつ 出生率 5/1000 以下」 の県を抽出すると、 少子高齢化が強い県が浮かび上がる。 SSDSE-B-2026 の 2023 年行で実行すると、 北海道・青森・岩手・秋田の 4 道県が該当。 これは地域包括ケアシステムの優先導入候補と一致する。

事例 2: マーケティングの地域セグメント

「人口 100 万人以上 かつ 消費支出が全国平均以上」 でフィルタすると、 消費水準の高い中規模以上の県(北海道、 宮城、 埼玉、 東京、 神奈川など 26 都道府県)が抽出される。 SSDSE-B-2026 の A1101L3221 を組み合わせて構築する。

事例 3: 異常検知(外れ値探索)

SSDSE-B-2026 の人口で df[df['A1101'] > df['A1101'].quantile(0.95)] と書くと、 上位 5% に該当する東京・神奈川・大阪 などが浮上。 これは「全国平均で議論すると過度に上方バイアスがかかる県」を識別するフィルタとして有用。

事例 4: 機械学習の学習データ抽出

回帰モデルで「人口の影響を除いた」分析をしたいとき、 SSDSE-B-2026 から外れ値(東京)を df[df['Prefecture'] != '東京都'] で除去すると、 残り 46 県でモデル係数が安定する。 ただし「東京を除外する」ことは結果の解釈に重大な影響を与えるため、 必ず Methods セクションで明記する。

❓ よくある質問(FAQ)

Q. df[df['x'] == None] でフィルタできない — なぜ?

pandas/NumPy の世界では None は NaN として保存される。 NaN との == 比較は常に False。 必ず df[df['x'].isna()] を使う。 SSDSE-B-2026 では欠損はほぼ無いが、 結合後のデータでは頻出。

Q. フィルタした結果が空 (0 行) になる — どう調査する?

条件を 1 つずつ外して、 「どの条件が原因で空になったか」を切り分ける。 SSDSE-B-2026 で df[(df['A1101'] >= 1_000_000) & (df['A1303']/df['A1101'] >= 0.40)] が空になるなら、 後者の閾値 0.40 が厳しすぎる(2023 年で最も高齢化率の高い秋田でも約 39.1%)。 各条件のヒット数を m.sum() で確認する習慣を。

Q. str.contains('東京') で「東京都」がヒットしない — なぜ?

99% は文字コードの問題か NaN の存在。 SSDSE-B-2026 を CP932 で読んだ場合、 内部で UTF-8 に変換されるはず。 na=False を指定して NaN 安全化、 regex=False でリテラル比較に。 df['Prefecture'].str.contains('東京', na=False)

Q. query() で日本語列名を使うには?

列名を `バッククォート` で囲む。 例: df.query('`人口` >= 1000000')。 SSDSE-B-2026 ではカラム名が A1101 のような英数字なので問題は起きないが、 日本語列名を持つデータでは必須。

Q. フィルタリングと slicing の違いは?

フィルタは「条件式の True/False で選ぶ」、 slicing は「位置や Index で選ぶ」。 SSDSE-B-2026 で df.iloc[0:10] は最初の 10 行(北海道〜栃木)。 df[df['A1101'] >= 1_000_000] は条件にマッチする行(順不同)。

🏛 ツール別フィルタリング早見表

同じ「人口 100 万人以上」のフィルタを各ツールで書くと:

ツール記法備考
pandasdf[df['A1101'] >= 1_000_000]boolean mask
pandas (query)df.query('A1101 >= 1000000')SQL 風
Polarsdf.filter(pl.col('A1101') >= 1_000_000)遅延評価が効く
SQLSELECT * FROM ssdse WHERE A1101 >= 1000000WHERE 句
Excelオートフィルタ → 数値フィルタ → 指定の値以上GUI 操作
R (tidyverse)df %>% filter(A1101 >= 1e6)パイプ演算子
NumPyarr[arr >= 1_000_000]1 次元 ndarray のみ簡潔
Tableau / Power BIフィルターシェルフ → 値の範囲GUI、 ダッシュボードで動的
BigQuerySELECT * FROM dataset WHERE A1101 >= 1000000SQL + 分散処理

🎯 練習問題 — SSDSE-B-2026 で書くフィルタ 10 問

  1. 人口 100 万未満 かつ 高齢化率 35% 以上の県を抽出せよ。 期待: 秋田・徳島・高知あたり。
  2. 高齢化率 (A1303/A1101) が全国平均以上の県をすべて抽出せよ。 期待: 約 30 県。
  3. 出生率 (A4101/A1101) の上位 5 県を抽出せよ。 期待: 沖縄・滋賀・熊本・愛知・福岡あたり。
  4. 都道府県名に「島」を含む県を抽出せよ。 期待: 福島・島根・徳島・広島・鹿児島の 5 県。
  5. 関東 7 都県 かつ 高齢化率 25% 未満の県を抽出せよ。 期待: 東京・神奈川・埼玉あたり。
  6. 9 つの地域(北海道・東北・関東・中部・近畿・中国・四国・九州・沖縄)ごとに人口最大県を抽出せよ。
  7. 人口千人あたり婚姻件数 (A9101/A1101*1000) で全国平均の 1.5 倍以上の県を抽出せよ。
  8. 2018-2026 の SSDSE-B を縦結合し、 「2020 年以降の東京都」だけ抽出せよ。
  9. 総人口 100 万人あたり一般診療所数(I5102/A1101*1_000_000)が上位 10% の県を抽出せよ。
  10. 年少人口 / 総人口 が 10% を下回る県を抽出せよ。 期待: 少子化が進む県の健全性チェック。

採点のコツ: 結果の県名・件数を、 期待値と照らし合わせる。 ずれていたら、 マスクを 1 つずつ print(m.sum()) で検証する。

🌍 他分野での類似概念

分野対応概念類似点・違い
集合論内包的記法 $\{x \in S \mid P(x)\}$pandas のフィルタはまさにこれの実装。 集合論で習った記法がそのまま使える。
論理学命題論理 / 述語論理AND/OR/NOT、 量化子「すべての / 存在する」 → groupby+filter の発想。
SQLWHERE 句同じ「条件で行を選ぶ」操作。 文法が違うだけ。 SSDSE を BigQuery にアップロードしても同じ思考。
Excelオートフィルタ / 関数 IFS / FILTERGUI フィルタも内部的にはマスクと同じ。
RegExパターンマッチング文字列マスクの作成に利用。 str.match の引数は正規表現。
Web 検索クエリ式Google の「東京 -大阪 site:gov.jp」のような演算子も、 思考はフィルタリング。
機械学習サンプル選択 / アクティブラーニング「不確実性の高いサンプルだけ抽出してラベル付け」もフィルタリングの応用。

🎓 フィルタリングマスター 5 段階 — SSDSE-B-2026 を題材に

  1. Step 1: df[df['col'] op val] で単一条件フィルタを書く。 SSDSE-B-2026 で「人口 100 万以上」「高齢化率 30% 以上」など 10 種類書く。
  2. Step 2: & | ~ で複合条件を書く。 SSDSE-B-2026 で AND/OR/NOT を含む 5 種類の絞り込みを書く。
  3. Step 3: isin(), between(), str.contains(), str.match() など特殊メソッドを習得。 SSDSE-B-2026 の地域別分類や正規表現フィルタに使う。
  4. Step 4: query()loc[] でメソッドチェーン化。 SSDSE-B-2026 の派生列生成+フィルタ+ソートを 1 行で書く。
  5. Step 5: 関数化+ Pipeline 化。 SSDSE-B-2026 用の「filter_aged_big(df, min_pop, min_aged)」のような再利用可能なフィルタ関数を作る。

📈 SSDSE-B-2026 フィルタの定石レシピ集

よく使うフィルタを「目的→コード」の形でまとめました。 SSDSE-B-2026 の列コード(A1101 など)は統計センター公式を参照してください。

目的コード件数の目安
大都市県(人口 100 万以上)df[df['A1101'] >= 1_000_000]約 12 県
地方県(人口 100 万未満)df[df['A1101'] < 1_000_000]約 35 県
高齢化県(30% 以上)df[df['A1303']/df['A1101'] >= 0.30]約 35 県
若年県(高齢化率 25% 未満)df[df['A1303']/df['A1101'] < 0.25]約 3 県
関東 7 都県df[df['Prefecture'].isin(['茨城県','栃木県','群馬県','埼玉県','千葉県','東京都','神奈川県'])]7 県
「県」を含むdf[df['Prefecture'].str.contains('県')]43 県(東京・大阪・京都・北海道 以外)
政令指定都市を持つ県(推定)df[df['A1101'] >= 500_000]約 25 県
65 歳以上人口が多い上位 5 県df.nlargest(5, 'A1303')人口規模の大きい大都市県が中心
出生数が多い上位 5 県df.nlargest(5, 'A4101')東京など大都市県が上位
年平均気温が高い県(温暖地)df[df['B4101'] >= 18]沖縄・九州南部など

✅ フィルタリング前のチェックリスト(12 項目)

  1. 対象列の dtype を確認したか? (数値 / 文字列 / 日付)
  2. 欠損 (NaN) の有無を確認したか?
  3. 条件式は & | ~ を使い、 各条件を () で囲んでいるか?
  4. 条件式の中で andor を誤用していないか?
  5. SettingWithCopyWarning が出る場合、 .copy() または .loc[] を使っているか?
  6. 絞り込み後の行数が期待値に合っているか?
  7. chained indexing(df[...][...] = ...)を避けているか?
  8. 絞り込み後の Index が必要なら reset_index(drop=True) したか?
  9. SSDSE-B-2026 のような既知データでは、 県名と件数を目視で確認したか?
  10. 論文・レポートでフィルタ条件を明示したか? Methods セクションで再現可能か?
  11. パフォーマンスが必要なら、 ベクトル化マスク か query() で書いたか?
  12. NaN を「除外したい / 残したい / 別扱いしたい」の方針を決めたか?

🕰 フィルタリングの歴史と発展

年代出来事意味
1970Codd の関係モデル提唱SELECT/WHERE 句の基礎が確立。 「行を条件で選ぶ」操作の数学的基盤。
1974SEQUEL (SQL) 言語の提案WHERE 句が事実上の標準フィルタ構文に。
1985Excel の発売、 オートフィルタ機能GUI でのフィルタリングが一般化。
2008pandas 公開、 boolean indexing 採用NumPy のマスク機能を DataFrame に適用。
2014dplyr (R) リリース、 filter() 関数パイプ演算子と組み合わせた読みやすい構文。
2018Polars 公開、 遅延評価フィルタ大規模データでも数倍速いフィルタ。 SSDSE-B-2026 の市町村版(1,724 行)でも体感差がある。
2022DuckDB の埋め込みデータベースSQL でフィルタしつつ pandas と相互運用。
2024+Polars の安定化、 Arrow バックエンドの普及pandas の query() も内部で Arrow を使うようになり、 SQL 風が高速に。

「フィルタリング」は 50 年以上前の関係代数から続く操作。 道具は変わっても、 「条件式 → 真偽値 → 行選択」の 3 段思考は普遍。

🗺 概念マップ — フィルタリングの位置づけ

フィルタリングは「データクレンジング後、 集計前」のフェーズで最も頻出する操作。 SQL の WHERE 句、 Excel のオートフィルタ、 BI ツールのスライサーなど、 全てのデータ分析ツールに対応する操作がある。

データ取得 (read_csv / SQL)
  │
  ▼
【データクレンジング】
  ├─ 型変換
  ├─ 欠損値処理
  └─ 外れ値検出
  │
  ▼
【フィルタリング】 ★ 本ページ
  ├─ 単一条件マスク
  ├─ 複合条件(AND / OR / NOT)
  ├─ 範囲(between)
  ├─ メンバーシップ(isin)
  ├─ 文字列・正規表現
  └─ groupby + filter
  │
  ▼
【集計(aggregation)】
  ├─ groupby + agg
  ├─ pivot_table
  └─ crosstab
  │
  ▼
【モデリング / 可視化】
  

🧭 フィルタリング深掘り — SSDSE-B-2026 で「見る前と見た後」を徹底比較する

フィルタリングは「データを切る」操作だが、 切り方を誤ると 残った行で計算する平均・分散・相関がすべて偏る。 ここでは SSDSE-B-2026(47 都道府県)を題材に、 同じ列に対して 3 種類のフィルタを掛けた時に分布がどう変化するかを 3 枚の図で並べて観察する。 「フィルタ前」「フィルタ後」「群別」の 3 視点を持つことで、 自分の切り口が「サンプルの代表性」を歪めていないかを毎回チェックする習慣を身につけよう。

図 1: フィルタ前後の散布図(総人口 vs 一般診療所数)

総人口と一般診療所数の散布図(SSDSE-B-2026)
図 1: SSDSE-B-2026 から作成した、 47 都道府県の総人口(横軸、百万人)と一般診療所数(縦軸、施設)の散布図。 相関係数は r = 0.972 と高く、 東京・神奈川・大阪などの大都市圏が右上に離れている。 ここで「人口 500 万人未満だけ残す」という単純フィルタを掛けると、 右上の高レバレッジ点が消え、 相関係数も回帰直線の傾きも変化する。

この散布図から読み取れる最大の教訓は、 「外れ値らしい点」をフィルタで切り落とす前に、 それが本当に異常値なのか、 それとも研究対象そのものなのかを判断する必要がある という点だ。 東京都の人口と一般診療所数が突出しているからといって 47 都道府県の分析から外せば、 残った 46 件は「東京を除いた日本」という別の母集団を表すことになる。 SSDSE-B-2026 のような全数調査データでは、 外れ値は誤差ではなく「現実そのもの」であり、 フィルタは慎重に説明可能な根拠でだけ行うべきだ。

図 2: フィルタが生む分布の歪み(ヒストグラム)

人口のヒストグラム(SSDSE-B-2026)
図 2: 都道府県人口のヒストグラム。 右に長い裾を持つ右歪み分布で、 平均値(約 265 万人)は中央値(約 155 万人)よりかなり大きい。 「平均以上だけを残す」フィルタを掛けると右側の山だけが残り、 「平均以下」を残すと左側の山だけが残る。 どちらを選ぶかで以後の分析結論は反転する。

ヒストグラムの裾を見ると、 平均というしきい値が「分布の中央」ではないことが一目で分かる。 SSDSE-B-2026 の人口分布のように右に長い裾を持つデータでは、 平均値より大きい都道府県は数えるほどしかなく、 多くは平均より小さい。 こうした分布で「平均以上」「平均以下」というフィルタを掛けると、 残ったサンプル数自体が大きく偏る。 件数の偏りは分析の 統計的検出力 に直結する。 つまり、 残った件数が少なすぎると「差が無いように見える」ことが起こる。

図 3: 群別フィルタの可視化(クラスタ別の箱ひげ図)

クラスタ別の一般診療所数の箱ひげ図(SSDSE-B-2026)
図 3: KMeans クラスタ別に一般診療所数を比較した箱ひげ図。 人口規模の大きいクラスタほど一般診療所数も多く、 cluster1 は東京都単独に近い高い値として分離している。 クラスタや地域で絞ると、 医療供給規模と人口規模の効果が交絡しやすい。

フィルタは単に「行を選ぶ」操作のように見えるが、 群別に区切ると 群と他の変数が強く相関している(交絡) ことが露呈する。 「関東に絞って分析する」と言った瞬間、 自動的に「人口規模が大きく一般診療所数も多い都道府県だけ」を分析していることになりやすい。 結果の解釈で「関東だから」と説明できるとは限らず、 「単に人口が大きいから」かもしれない。 こうした交絡の検出には、 図 3 のような群別箱ひげ図が最も実務的に効く。

フィルタ条件の分類表 — 11 パターン

No.条件タイプpandas 例SQL 例注意点
1等価条件df[df['地域']=='関東']WHERE region='関東'表記揺れ(半角/全角・空白)に弱い
2不等価条件df[df['人口']>1_000_000]WHERE pop>1000000境界値の扱い(> か ≥ か)を明示
3範囲条件df['人口'].between(1e6, 5e6)BETWEEN 1e6 AND 5e6両端を含むかどうか方言差あり
4集合条件df['地域'].isin(['関東','近畿'])IN ('関東','近畿')巨大リストは結合に置き換える
5否定条件~df['地域'].isin([...])NOT IN (...)NULL を含む列で挙動差あり
6欠損条件df[df['列'].isna()]WHERE col IS NULL== NaN は常に False
7文字列部分一致df['都道府県'].str.contains('山')LIKE '%山%'正規表現の扱い差に注意
8複合 AND(A & B)A AND Bpandas は &and ではない
9複合 OR(A | B)A OR B優先順位のため括弧を必ず付ける
10上位 N 件df.nlargest(5,'人口')ORDER BY pop DESC LIMIT 5同値の扱い(同率順位)に注意
11分位フィルタdf[df['人口']>df['人口'].quantile(.75)]PERCENT_RANK 利用少数サンプルでは分位が不安定

11 パターンを一覧化すると、 ほとんどの実務上のフィルタはこの組み合わせに分解できることが分かる。 自分が書いた条件式が「どのパターンに属するか」を即答できるようになれば、 SQL と pandas を行き来する場面でも翻訳ミスをほぼ防げる。

条件式と件数の早見表(SSDSE-B-2026 47 件で実測)

条件残る件数残る割合代表的に残る都道府県
人口 ≥ 1,000,0003778.7%東京 / 神奈川 / 大阪 / 愛知 / ...
人口 ≥ 5,000,000919.1%東京 / 神奈川 / 大阪 / 愛知 / 埼玉 / 千葉 / 兵庫 / 北海道 / 福岡
地域 == 関東714.9%茨城 / 栃木 / 群馬 / 埼玉 / 千葉 / 東京 / 神奈川
高齢化率 ≥ 30%3574.5%秋田 / 高知 / 徳島 / 山口 / 青森 / ...
人口 ≥ 100 万 かつ 高齢化率 < 30%1225.5%東京 / 神奈川 / 愛知 / 大阪 / 埼玉 / 千葉 / 福岡 / 兵庫 / ...
都道府県名 に「山」を含む612.8%山形 / 富山 / 山梨 / 和歌山 / 岡山 / 山口
人口 上位 10 件1021.3%東京 / 神奈川 / 大阪 / 愛知 / 埼玉 / 千葉 / 兵庫 / 福岡 / 北海道 / 静岡

同じ「人口で絞る」操作でも、 しきい値を 100 万にするか 500 万にするかで残る件数は 37 → 9 と 4 倍以上変わる。 残る件数が少ないほど標準誤差は大きくなり、 統計的検定の検出力は急激に落ちる。 「フィルタを掛けたら必ず残件数を確認する」ことは、 報告書の信頼性を支える基本動作だ。

🐍 Python 実装 — フィルタ前後で統計量がどう変わるか

このコードでやること: SSDSE-B-2026 を読み込み、 「人口 100 万人以上」というフィルタの前後で平均人口・中央値・標準偏差・相関係数がどう変化するかを比較する。 フィルタ操作が単なる行選択ではなく、 統計量を歪める「縮約」であることを実値で体感する。

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

SSDSE-B-2026 都道府県 総人口(百万人) 一般診療所数(施設) R01000 北海道 5.092 3403 R02000 青森県 1.184 850 R03000 岩手県 1.163 879 R04000 宮城県 2.264 1724 R05000 秋田県 0.914 806 ... R13000 東京都 14.086 14894 R14000 神奈川県 9.229 7150 ... R47000 沖縄県 1.468 928
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
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].copy()
df = df.rename(columns={'Prefecture': '都道府県', 'A1101': '総人口', 'I5102': '一般診療所数'})
df['人口_百万人'] = df['総人口'] / 1_000_000

# フィルタ前
print('--- フィルタ前 (n=47) ---')
print('平均人口(百万人):', round(df['人口_百万人'].mean(), 3))
print('中央値人口      :', round(df['人口_百万人'].median(), 3))
print('標準偏差        :', round(df['人口_百万人'].std(), 3))
print('相関(人口,一般診療所数):', round(df['人口_百万人'].corr(df['一般診療所数']), 4))

# 人口 100 万人以上だけ残す
sub = df[df['総人口'] >= 1_000_000]
print('--- フィルタ後 (n=', len(sub), ') ---', sep='')
print('平均人口(百万人):', round(sub['人口_百万人'].mean(), 3))
print('中央値人口      :', round(sub['人口_百万人'].median(), 3))
print('標準偏差        :', round(sub['人口_百万人'].std(), 3))
print('相関(人口,一般診療所数):', round(sub['人口_百万人'].corr(sub['一般診療所数']), 4))

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

--- フィルタ前 (n=47) --- 平均人口(百万人): 2.646 中央値人口 : 1.549 標準偏差 : 2.798 相関(人口,一般診療所数): 0.9717 --- フィルタ後 (n=37) --- 平均人口(百万人): 3.155 中央値人口 : 1.897 標準偏差 : 2.957 相関(人口,一般診療所数): 0.9697

💬 平均人口は 2.646 百万人 → 3.155 百万人と 1.19 倍に上昇し、 中央値も 1.549 → 1.897 百万人に動く。 人口と一般診療所数の相関係数は 0.9717 → 0.9697 とわずかに下がる。 「人口の小さい県を切る」操作は、 単に件数を減らすだけでなく、 残ったサンプルの平均像そのものを「大都市寄り」に歪める。

🐍 Python 実装 — 群別フィルタと交絡の検出

このコードでやること: 都道府県を地域ブロックに分けて、 各群の平均人口と平均一般診療所数を出す。 「関東フィルタ」が「規模フィルタ」と等価になっていないかを数値で示す。

📥 入力データ:

都道府県 地域 人口(百万人) 一般診療所数 北海道 北海道 5.092 3403 青森県 東北 1.184 850 ... 東京都 関東 14.086 14894 神奈川県 関東 9.229 7150 ... 沖縄県 九州沖縄 1.468 928
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
region_map = {
    '北海道': '北海道',
    '青森県': '東北', '岩手県': '東北', '宮城県': '東北', '秋田県': '東北', '山形県': '東北', '福島県': '東北',
    '茨城県': '関東', '栃木県': '関東', '群馬県': '関東', '埼玉県': '関東', '千葉県': '関東', '東京都': '関東', '神奈川県': '関東',
    '新潟県': '中部', '富山県': '中部', '石川県': '中部', '福井県': '中部', '山梨県': '中部', '長野県': '中部', '岐阜県': '中部', '静岡県': '中部', '愛知県': '中部',
    '三重県': '近畿', '滋賀県': '近畿', '京都府': '近畿', '大阪府': '近畿', '兵庫県': '近畿', '奈良県': '近畿', '和歌山県': '近畿',
    '鳥取県': '中国', '島根県': '中国', '岡山県': '中国', '広島県': '中国', '山口県': '中国',
    '徳島県': '四国', '香川県': '四国', '愛媛県': '四国', '高知県': '四国',
    '福岡県': '九州沖縄', '佐賀県': '九州沖縄', '長崎県': '九州沖縄', '熊本県': '九州沖縄', '大分県': '九州沖縄', '宮崎県': '九州沖縄', '鹿児島県': '九州沖縄', '沖縄県': '九州沖縄',
}
df['地域'] = df['都道府県'].map(region_map)
g = (df.groupby('地域')
       .agg(人口_百万人_mean=('人口_百万人', 'mean'), 人口_count=('人口_百万人', 'count'),
            一般診療所数_mean=('一般診療所数', 'mean'), 一般診療所数_count=('一般診療所数', 'count'))
       .round({'人口_百万人_mean': 3, '一般診療所数_mean': 0}))
print(g)

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

人口_百万人_mean 人口_count 一般診療所数_mean 一般診療所数_count 地域 中国 1.414 5 1286.0 5 中部 2.305 9 1798.0 9 九州沖縄 1.754 8 1554.0 8 北海道 5.092 1 3403.0 1 四国 0.894 4 798.0 4 東北 1.386 6 1085.0 6 近畿 3.141 7 3060.0 7 関東 6.218 7 5046.0 7

💬 関東の平均人口 6.218 百万人・平均一般診療所数 5,046 施設に対し、 四国は 0.894 百万人・798 施設と大きく低い。 「関東フィルタ」は実質「大規模圏フィルタ」と同じであり、 ここで得られた「関東は他より◯◯」という結論は 地域効果と規模効果を切り分けられていない 危険がある。 群別フィルタを掛ける際は、 必ず 群サイズと群内平均 を一覧表に出して、 何と何を比較しているのかを再確認する。

フィルタ実務 12 個のチェックリスト

  1. 条件文を SQL と日本語で同時に書く — 機械可読と人間可読を両立させる。
  2. フィルタ前後の件数を必ず print — 想定外の縮約に気付ける。
  3. 残ったサンプルの代表性を分布図で確認 — 平均・中央値・四分位を比較。
  4. NaN 行が消えていないかを isna().sum() で点検 — > や == の挙動は欠損で False。
  5. 境界値 (== か > か >=) を仕様書に明記 — レビュー時に解釈差が出る部分。
  6. 文字列の表記揺れを事前に正規化 — 全角半角・空白・大文字小文字。
  7. 複合条件は括弧で完全に明示 — pandas の &| は優先順位が低い。
  8. 除外データのサンプル数行を保存 — 後で「何を切ったか」を再確認できる。
  9. 逆方向フィルタを使った検算 — A の補集合 ~A を取って件数の合計が元と一致するか。
  10. カテゴリ列は categorical 化 — メモリ削減と誤値検出が同時にできる。
  11. 時系列はフィルタ前にソート — 期間条件のミスを防ぐ。
  12. フィルタ後に再インデックスreset_index(drop=True) で iloc アクセス事故を防ぐ。

フィルタとサンプリングの違い — 似ているが目的が違う

観点フィルタリングサンプリング
目的特定の条件を満たす行を選ぶ母集団から代表を選ぶ
選び方条件式(決定的)確率的(無作為)
残ったサンプルの代表性保証されない(偏る)確率的に保証される
分析の文脈部分集団分析・除外推定・モデル評価
事例「人口 100 万以上の県」「47 県から無作為に 10 件」
統計量への影響平均・分散・相関が変わる期待値は不変、 分散は増える

フィルタリングとサンプリングはどちらも「行数を減らす」操作だが、 目的と統計的性質はまったく違う。 報告書で「データを絞り込んで分析した」と書くときは、 どちらの操作かを必ず明示しないと、 読み手は「無作為に減らした」と誤解してしまう。 SSDSE-B-2026 は 47 件しかないので、 サンプリングよりもフィルタリングの議論が圧倒的に多くなる。

フィルタの再現性 — ノートブックでよくある罠

Jupyter Notebook でフィルタを掛けると、 セルの実行順序によって df 自体が縮約された状態で次のセルに渡ってしまい、 同じノートブックを再実行したときに別の結果が出る、 という再現性事故が起きやすい。 これを防ぐには以下の 4 つのルールを守る。

  1. 原本 df を絶対に上書きしないsub = df[条件].copy() のように別名・コピーで持つ。
  2. セルの先頭で常に元データから読み直すdf = pd.read_csv(...) をフィルタ前に置く。
  3. フィルタの定義を関数化def filter_metro(d): return d[d['人口']>=1e6] のように再利用可能にする。
  4. 残った件数と条件式の文字列をログprint(f'filter: {cond_str} -> n={len(sub)}') を毎回出す。

「フィルタ後の解釈」7 つのテンプレート

  1. 「対象は◯件→△件に絞った」と明示する(縮約率を必ず示す)。
  2. 「除外した行の特徴」を 1 文で書く(「主に人口 100 万未満の県」など)。
  3. 「除外による交絡懸念」を 1 文で書く(「規模効果と地域効果が重なる」など)。
  4. 「残った母集団の解釈」を明文化(「ここから先の結論は大都市圏に限定される」など)。
  5. 「フィルタ前後で大きく変わった統計量」を表で並べる。
  6. 「フィルタを外した場合の結論差」を感度分析として併記する。
  7. 「フィルタ条件の閾値根拠」を引用(行政区分・先行研究・データ仕様)。

これら 7 つのテンプレート文を毎回コピペで埋めるだけで、 「分析結果は信頼できるが、 どんな限界があるか」が読み手に伝わるようになる。 フィルタは透明性の問題であり、 何を切ったかを隠さないことが最低条件だ。

フィルタ × データ品質 — 4 つの典型的不具合

不具合原因予兆対処
想定より残らない文字列表記揺れ / NaN 混入件数が 0 または極小unique 一覧で原データ確認
想定より多く残る条件の括弧不足 / 否定漏れ件数が元データに近い条件を分解して逐次検証
結合後に消えるleft join 前にフィルタ済み結合後の NaN 量が異常結合の後にフィルタを置く
groupby 後に空群フィルタで一部群が消滅groupby 結果のサイズ縮小フィルタ前後で group 数比較

上記 4 つは、 実務で最も頻繁に発生するフィルタ起因のバグである。 「件数が 0」「結合後に NaN だらけ」「群が一つ消えた」という症状を見たら、 まず フィルタ条件を疑う 癖をつけよう。 多くの場合、 SQL や pandas のロジック自体ではなく、 入力データの欠損・表記揺れ・順序がフィルタの挙動を変えている。

SSDSE-B-2026 で 47 件すべてを表示 — フィルタの「全件確認」段階

フィルタを掛ける前に、 まず原データの 47 都道府県を一度すべて眼で確認することが重要である。 大規模データではこの全件確認が不可能だが、 SSDSE-B-2026 は 47 件しかないので、 「フィルタ前」の全体像を頭に入れたうえで条件を設計できる。 以下の表は SSDSE-B-2026 の主要列を地域順に並べたもので、 フィルタ条件を考えるときの参照表になる。

地域都道府県総人口(百万人)一般診療所数(施設)高齢化率(%)
北海道北海道5.092340333.0
東北青森県1.18485035.2
東北岩手県1.16387935.0
東北宮城県2.264172429.2
東北秋田県0.91480639.1
東北山形県1.02688135.2
東北福島県1.767137233.2
関東茨城県2.825176030.6
関東栃木県1.897148230.2
関東群馬県1.902156331.0
関東埼玉県7.331453027.4
関東千葉県6.257394228.1
関東東京都14.0861489422.8
関東神奈川県9.229715025.9
中部新潟県2.126165533.9
中部富山県1.00774433.1
中部石川県1.10987930.5
中部福井県0.74457131.6
中部山梨県0.79672631.8
中部長野県2.004159932.7
中部岐阜県1.931159831.2
中部静岡県3.555272431.0
中部愛知県7.477568225.7
近畿三重県1.727149830.6
近畿滋賀県1.407114327.0
近畿京都府2.535248829.7
近畿大阪府8.763887727.7
近畿兵庫県5.370519630.0
近畿奈良県1.296121332.6
近畿和歌山県0.892100834.2
中国鳥取県0.53747433.3
中国島根県0.65068934.9
中国岡山県1.847155031.0
中国広島県2.738252130.1
中国山口県1.298119435.4
四国徳島県0.69568835.4
四国香川県0.92681632.5
四国愛媛県1.291117534.2
四国高知県0.66651436.3
九州沖縄福岡県5.103480628.5
九州沖縄佐賀県0.79569331.7
九州沖縄長崎県1.267131734.3
九州沖縄熊本県1.709147032.3
九州沖縄大分県1.09694734.2
九州沖縄宮崎県1.04290633.7
九州沖縄鹿児島県1.549136933.8
九州沖縄沖縄県1.46892823.8

この 47 件の全件表を見ると、 「人口 100 万人未満」は鳥取・島根・福井・徳島・高知・佐賀・山梨など分布の左裾に集中していて、 これらを切ると 地方の小規模県の声がデータから消える ことが直観的に分かる。 東京都の高齢化率が 22.8% と全国で最も低く、 沖縄県も 23.8% と低い一方で人口は中規模、 という「両軸で見ないと隠れる特徴」も全件表でしか気付けない。 フィルタを掛ける前にこの 47 行を一度通読することで、 「自分の条件はこれら 47 県のうちどれを残すのか」を地理的・社会的にイメージできるようになる。

フィルタの「逆向き検算」— 補集合を取って件数を合わせる

このコードでやること: フィルタ A の補集合 ~A を取って、 A と ~A の合計件数が元データに一致するかを確認する。 NaN や境界値の扱いミスを発見できる定石の検算手法。

📥 入力データ: SSDSE-B-2026 の 47 都道府県(人口列)。

 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].copy()
df = df.rename(columns={'A1101': '総人口', 'Prefecture': '都道府県'})

cond = df['総人口'] >= 1_000_000
A = df[cond]
notA = df[~cond]
print('A    :', len(A))
print('~A   :', len(notA))
print('合計 :', len(A) + len(notA), '/ 元:', len(df))
print('NaN件数:', df['総人口'].isna().sum())

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

A : 37 ~A : 10 合計 : 47 / 元: 47 NaN件数: 0

💬 A + ~A = 47 = 元件数 となり、 NaN も 0 件なので「条件と補条件で行が漏れていない」ことが確認できる。 もし NaN が混入していれば、 >=< も両方 False になり、 A と ~A の合計が元件数を下回るので即座に異常検出できる。 この検算は習慣化すべき。

「除外行のサンプル保存」テンプレ — フィルタの透明性を担保する

このコードでやること: フィルタで除外された行を別 CSV に書き出し、 後から「何を切ったか」を再確認できるようにする。 監査・再現性のためのテンプレ。

📥 入力データ: SSDSE-B-2026。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import os
os.makedirs('data', exist_ok=True)   # 書き出し先を先に作る
import os
import pandas as pd

# ── この抜粋だけで動くように、日本語の列名で読み込む ──
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1)
df = df[df['地域コード'].astype(str).str.match(r'^R\d{5}$', na=False)].copy()
df['年度'] = pd.to_numeric(df['年度'], errors='coerce')
df = df[df['年度'] == df['年度'].max()]                   # 最新年度の 47 行
df['総人口'] = pd.to_numeric(df['総人口'], errors='coerce')

os.makedirs('data/processed', exist_ok=True)   # 保存先が無いと to_csv は失敗する

cond = df['総人口'] >= 1_000_000
kept = df[cond].copy()
excluded = df[~cond].copy()
kept.to_csv('data/processed/kept_pop_ge1m.csv', index=False)
excluded.to_csv('data/processed/excluded_pop_lt1m.csv', index=False)
print('採用:', len(kept), '件')
print('除外:', len(excluded), '件')
print('除外された県名:', excluded['都道府県'].tolist())

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

採用: 37 件 除外: 10 件 除外された県名: ['秋田県', '福井県', '山梨県', '和歌山県', '鳥取県', '島根県', '徳島県', '香川県', '高知県', '佐賀県']

💬 除外された 10 県を見ると、 秋田・福井・山梨・和歌山・鳥取・島根・徳島・香川・高知・佐賀という小規模県が並ぶ。 この一覧を別 CSV に保存しておくことで、 数か月後の再分析時にも「人口 100 万未満を切ったらこの 10 県が消えていた」と即座に追跡できる。 また、 「香川を切ったのは妥当か?」というレビュー指摘にも、 「明示的に切った 10 件のうちの 1 つ」として説明可能になる。 フィルタの透明性は、 残ったデータだけでなく 除外したデータも保存・公開する ことで担保される。

フィルタリングの「歴史的経緯」— なぜこんなに種類が多いのか

フィルタリングという操作は、 リレーショナルデータベース理論(Codd 1970 年)の 選択(selection、 σ)演算に源流を持つ。 σ_条件(R) は関係 R から条件を満たすタプルだけを取り出す演算で、 SQL の WHERE 句、 pandas のブール索引、 R の subset()、 Excel のオートフィルタはすべてこの σ 演算の異なる表現に過ぎない。 しかし、 各言語の方言として独自の構文が積み重なった結果、 同じ意味を持つフィルタが df[cond] / df.loc[cond] / df.query('...') / df.filter(items=...) / df.where(cond) など複数の書き方で存在し、 初学者の混乱の元になっている。 ここでは pandas 内部のフィルタ書法を整理し、 「いつどれを使うべきか」を 5 観点で整理する。

書法用途速度可読性SettingWithCopy 回避
df[cond]標準的な行選択高速高い.copy() を併記
df.loc[cond, cols]行 + 列の同時選択高速高い代入も安全
df.query('A > 1')SQL ライクな式中(numexpr 経由)非常に高い読み取りのみ推奨
df.filter(items=[...])列名でフィルタ高速行フィルタには使わない
df.where(cond)条件外を NaN に置換行削減ではない

この 5 つの書法の中で初学者にまず勧めるのは df.loc[cond, cols] である。 行と列を同じ角括弧内で指定でき、 代入時の SettingWithCopyWarning も出にくいためだ。 df.query() は SQL に慣れた人や、 列名を文字列で動的に組み立てたい場合に便利だが、 列名にスペースや特殊文字があると壊れる脆さがある。 df.where() は「条件外を NaN に置き換えるが行は残す」という独特の動作で、 「行を削減する」フィルタとは目的が異なる点に注意。

SQL における WHERE と HAVING の使い分け

SQL でフィルタを掛ける際、 「集計の前」と「集計の後」で使う構文が異なる。 これは pandas でいうと df[cond].groupby(...)df.groupby(...).filter(...) の違いに対応する。 SSDSE-B-2026 を例に、 「人口 100 万人以上の都道府県だけを地域別に平均する」と「地域別に平均人口を出した後、 平均が 200 万を超える地域だけ残す」は別物である。 前者は WHERE、 後者は HAVING を使う。

タイミング対象pandas 等価
WHERE集計前(行単位)元の行df[cond].groupby(...)
HAVING集計後(群単位)集計結果df.groupby(...).agg(...).query('mean > ...')
QUALIFYウィンドウ関数後window 結果df.assign(rk=...).query('rk<=N')

フィルタの「副作用」— 集計値の解釈ミスを生むパターン

フィルタを掛けた後に平均値や相関を計算すると、 解釈ミスが起きやすい。 代表的な 5 パターンを整理する。

  1. 選択バイアス: 「人口 100 万以上」だけで分析した結果を「日本の都道府県は…」と一般化してしまう。 → 結論を「100 万人以上の都道府県では」と限定する。
  2. レンジ制限による相関減衰: 上位だけを取ると相関が見かけ上弱くなる(分散が縮むため)。 → フィルタ前後の相関を必ず併記する。
  3. シンプソンのパラドックス: 全体では正の相関でも、 群別に見ると逆方向、 ということが起きる。 → 群別に層別分析する。
  4. 欠損行の暗黙除外: df[df['A']>0] は A が NaN の行を自動で除外する。 → 「欠損は除外したか/補完したか」を明示する。
  5. 動的しきい値の循環参照: df[df['人口']>df['人口'].mean()] は元データの平均で切るが、 切った後のサンプルの平均は当然変わる。 → 「平均以上」という条件は注意して文書化する。

フィルタリングと「データのプライバシー」

フィルタは個人情報保護にも関わる重要な操作である。 「人口の少ない地域」や「特定の年齢層・性別の組み合わせ」でフィルタを掛けると、 残ったサンプルが少なすぎて個人特定が可能になる場合がある(K 匿名性の問題)。 SSDSE-B-2026 は都道府県集計値なので個人特定リスクは低いが、 個票データ(マイクロデータ)を扱うときは「フィルタ後の最小集団サイズ」を必ずチェックする習慣をつける。 一般には、 同一属性のサンプルが K = 5 件未満になる粒度のフィルタは避けるべきだ。

フィルタリングと「機械学習の前処理」

機械学習のパイプラインにおいてフィルタリングは 訓練データの定義 そのものになる。 「訓練データから外れ値を除く」「特定期間のデータだけを使う」といったフィルタは、 モデルの汎化性能に直結する。 ここで重要なのは、 訓練データに掛けたフィルタを 検証データ・本番データには掛けない こと。 訓練時のみ外れ値を除いてもモデルは外れ値を学ばないが、 本番では外れ値も到来するので、 性能が大きく落ちる。 SSDSE-B-2026 でいうと、 「東京を外れ値として除いて学習した回帰モデル」を全国予測に使うと、 東京での予測誤差は大きくなる。 こうしたフィルタの「リーク」は実務で頻繁に発生するため、 訓練/検証/本番のフィルタ条件を表で管理することを推奨する。

SQL と pandas の対応表 — 主要 15 パターン

意味SQLpandas
単一条件WHERE pop >= 1000000df[df['人口']>=1e6]
複合 ANDWHERE A AND Bdf[(condA) & (condB)]
複合 ORWHERE A OR Bdf[(condA) | (condB)]
否定WHERE NOT Adf[~condA]
範囲BETWEEN x AND ydf['x'].between(a,b)
集合所属IN ('a','b')df['x'].isin(['a','b'])
部分一致LIKE '%山%'df['x'].str.contains('山')
前方一致LIKE '山%'df['x'].str.startswith('山')
後方一致LIKE '%県'df['x'].str.endswith('県')
NULL チェックIS NULLdf['x'].isna()
非 NULLIS NOT NULLdf['x'].notna()
上位 NORDER BY x DESC LIMIT Ndf.nlargest(N,'x')
下位 NORDER BY x ASC LIMIT Ndf.nsmallest(N,'x')
重複除外SELECT DISTINCTdf.drop_duplicates()
集計後フィルタHAVING avg(x)>100g.query('mean>100')

この 15 パターンを暗記すれば、 SQL 経験者が pandas に来たとき、 あるいは逆のときの「翻訳」が瞬時にできるようになる。 とくに IS NULL / df['x'].isna() は等号 == では正しく動かないので要注意。 SSDSE-B-2026 のように都道府県名にスペースが入らない整ったデータでは問題が出にくいが、 自由記述データを扱う場面では str.strip() でトリムしてから isin を使うクセを付ける。

フィルタ条件の「読みやすさ」— 名前付き条件のすすめ

このコードでやること: 複雑な複合条件を、 個別の名前付き Series に分解して可読性を高める。 後でデバッグするときも、 どの条件が効いているかを追跡しやすい。

📥 入力データ: SSDSE-B-2026。

 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].copy()
df = df.rename(columns={'Prefecture': '都道府県', 'A1101': '人口'})
df['高齢化率'] = df['A1303'] / df['人口'] * 100

is_metro       = df['人口'] >= 5_000_000
is_young       = df['高齢化率'] < 30
is_pacific_sea = df['都道府県'].isin(['茨城県', '千葉県', '東京都', '神奈川県', '静岡県', '愛知県', '三重県', '和歌山県', '高知県', '宮崎県', '鹿児島県'])
target = df[is_metro & is_young & is_pacific_sea]
print('該当県:', target['都道府県'].tolist())
print('該当件数:', len(target))

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

該当県: ['千葉県', '東京都', '神奈川県', '愛知県'] 該当件数: 4

💬 「大都市圏 かつ 若年層が多い かつ 太平洋側」という 3 条件を満たすのは、 千葉・東京・神奈川・愛知の 4 都県。 条件を名前付き変数に分解すると、 後から is_metro を緩めて 100 万に下げたい、 is_young の閾値を 25% にしたい、 といった調整が 1 行だけの編集 で済む。 一方、 df[(df['人口']>=5e6)&(df['高齢化率']<30)&(df['都道府県'].isin([...]))] と全部一行に書くと、 数か月後の自分やチームメンバーが読み解くのに時間を浪費する。 フィルタは「読みやすさ」を優先して書こう。

フィルタ条件の「テスト駆動」— 件数アサーションのすすめ

本番データのフィルタは、 単体テストのように「事前に期待件数を書く」癖をつけると事故が激減する。 SSDSE-B-2026 で「関東 7 件」「人口上位 10 件」など、 事前に分かっている件数を assert で表明することで、 データ更新時に件数が想定外に変動した場合に即座に検出できる。 これは データの形が変わった ことを知らせる早期警報装置として機能する。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) 北海道 5,092,000 東京都 14,086,000 沖縄県 1,468,000 …(全 47 行)
 1
	 2
	 3
	 4
	 5
	 6
	 7
	 8
	 9
	10
	11
	12
	13
	14
	15
	16
	17
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].copy()
df = df.rename(columns={'Prefecture': '都道府県', 'A1101': '人口'})

kanto_names = ['茨城県', '栃木県', '群馬県', '埼玉県', '千葉県', '東京都', '神奈川県']
kanto = df[df['都道府県'].isin(kanto_names)]
assert len(kanto) == 7, f'関東は7都県のはず: {len(kanto)}'

top10 = df.nlargest(10, '人口')
assert len(top10) == 10, f'上位10件取れていない: {len(top10)}'

over1m = df[df['人口'] >= 1_000_000]
assert 35 <= len(over1m) <= 40, f'人口100万以上は35〜40の想定: {len(over1m)}'

print('全アサーション通過')

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

全アサーション通過

💬 アサーション付きフィルタは、 例えば都道府県名の表記が変わった時、 あるいは欠損行が増えて関東が 6 件になった時に 即座にエラーを投げて止まる。 これにより、 異常データのまま下流分析が走って誤結論を出すリスクを大幅に減らせる。 業務分析ノートブックの全フィルタにこのテストを 1 行ずつ仕込むのが理想。

フィルタリングと「データ可視化」の連動

フィルタは可視化と組み合わせることで初めて意味を持つ。 SSDSE-B-2026 のような小規模なデータでも、 「人口 100 万人以上」だけを選ぶ前後で散布図を並べると、 残された点群が右上に偏ることが一目で分かる。 図 1 の散布図はフィルタ前、 図 2 のヒストグラムはフィルタの境界線を引く際の参考、 図 3 の箱ひげ図は群別フィルタ前のチェックに使う、 という役割分担を意識すると分析が体系化される。 ダッシュボード設計でも「フィルタ → 集計 → 可視化」の順序を崩さず、 ユーザーがフィルタを変えると統計量と図が同期的に更新される仕組みを必ず作る。 これにより、 利用者は 「条件を変えたらどう結論が動くか」 を体感でき、 過度な単一フィルタへの依存から自由になれる。 Tableau や Power BI の「フィルタアクション」「クロスフィルタ」も、 こうしたインタラクティブ性を実現するための同じ思想に基づいている。 SSDSE-B-2026 で練習しておけば、 実務で 100 万件規模のダッシュボードを設計する場合にも、 フィルタが分析の信頼性に与える影響を直感的に判断できるようになる。

「フィルタの逆」— 補完・代入とのトレードオフ

フィルタは「該当しない行を捨てる」操作だが、 同じ問題に対して「該当しない行を埋める(補完・代入)」というアプローチもある。 たとえば SSDSE-B-2026 の人口列に NaN が混じっていた場合、 NaN 行を削除する(フィルタ)か、 中央値で埋める(補完)か、 多重代入で複数案を作る(imputation)かで分析結果は変わる。 削除は単純だが、 削除した行に系統的な偏りがある場合(例: 小規模県だけ提出が遅れて NaN になりがち)、 残ったサンプルは大都市寄りに歪む。 一方、 補完は件数は保てるが、 補完値そのものに不確実性が乗るので、 推定値の信頼区間が見かけ上狭くなる危険がある。 「どの戦略を選んだか」を必ず分析レポートに明記し、 可能なら両方の戦略で結論が変わらないことを感度分析として示そう。 SSDSE-B-2026 自体は欠損が極めて少ないデータだが、 こうした思考訓練の場として最適である。

最終チェックリスト — 出荷前にこの 5 項目を確認

  1. フィルタ前後の件数を併記したか(縮約率を読み手が把握できるか)。
  2. 除外行の代表例を 3 件以上提示したか(透明性の担保)。
  3. 残ったサンプルで平均・分散・相関がどう変化したかを表で示したか。
  4. フィルタ条件と補集合を取って件数の整合性を確認したか(NaN 検出)。
  5. フィルタを外した場合の結論差(感度分析)を併記したか。

この 5 項目を満たさないフィルタは「誤読リスクがある」と見なし、 出荷を保留する。 SSDSE-B-2026 を題材に練習を重ねれば、 大規模データに移行した時にも同じチェックが反射的にできるようになる。

まとめ — フィルタを「無自覚に」掛けない

フィルタリングは最も基本的かつ最も誤用されやすい操作である。 SSDSE-B-2026 のような全数調査データを 47 件すべて使う場合でも、 「人口 100 万人以上」「関東のみ」「高齢化率 30% 以上」といった条件を一つ加えるだけで、 平均・分散・相関・回帰係数のすべてが変わる。 重要なのは 「何を切ったか」を常に文章化し、 件数と分布の変化を必ず可視化する ことだ。 図 1 の散布図・図 2 のヒストグラム・図 3 の群別箱ひげ図を常に並べて確認することで、 フィルタが研究設計の中で果たしている役割と、 副作用としての偏りを同時に見える化できる。 フィルタは「データを綺麗にする」便利な道具ではなく、 「分析の境界を定義する」設計判断であることを忘れてはいけない。

🧾 発表前の最終確認

フィルタリングでは、 何を残し何を除外したかを条件式として明示します。 閾値を少し変えると結論が変わる場合は、 感度分析を添え、 除外後の件数や分布が偏っていないかを確認します。

🔗 隣接手法への橋渡し

「フィルタリング」は単独で完結せず、 隣接する手法と接続することで分析パイプラインの一部として機能する。 以下に具体的な接続関係を示す。

「フィルタリング」は (1) 条件定義 (閾値 / カテゴリ / 期間) → (2) pandas df.query() or SQL WHERE で実装 → (3) 除外件数ログ → (4) 残存サンプルの選択バイアス確認 → (5) 下流分析へ受け渡し、 の 5 段で運用する。 単独実行で「データ削減」だけにすると選択バイアスを生む。

🌳 手法選択フロー

「フィルタリング」を実際の課題に当てはめるとき、 以下の 3 ステップで判断する。 領域固有の判断基準と組み合わせて使用する。

  1. ステップ 1: 条件 (閾値・カテゴリ・期間) を明確化
  2. ステップ 2: pandas df.query() または SQL WHERE で実装
  3. ステップ 3: 選択バイアスに注意・除外件数を記録

欠損行除外なら listwise / pairwise / 補完を比較、 期間絞り込みなら期間効果と時系列構造、 カテゴリ絞り込みなら除外群との比較を必ず添付し、 「除外前後の分布シフト」を可視化する。

状況第一選択第二選択避ける
単一条件 (SSDSE-B-2026 で人口 100 万人以上)df.query("人口 >= 1e6")df[df["人口"]>=1e6]for ループ削除
複合条件 (AND / OR / NOT)df.query("人口>1e6 and clinics>1e7")NumPy ブール演算 + & / |chained [] (SettingWithCopyWarning)
カテゴリのメンバーシップ (関東 7 県)df["pref"].isin([...])str.match(正規表現)長い OR 連結
欠損行除外dropna(subset=[...]) + 件数記録SimpleImputer で補完無記録の全体 dropna
大規模 (n > 10⁷)SQL WHERE / DuckDB / Polars lazychunksize で pandas 読込全件メモリロード
時系列 (年次データ)期間 boolean + period 集計DataFrame.between_time「直近 3 年」だけ切り出し時系列構造無視

🔬 解説を一段深く — フィルタは「統計量を変える」操作でもある

ここまでの章はフィルタリングの書き方(boolean mask・集合演算・構文の罠)を扱った。 この章は視点を変えて、 フィルタリングがその後に計算する統計量そのものを変えてしまうという統計的な側面を掘り下げる。 「行を減らすだけの無害な操作」と思っていると、 平均も相関も気づかぬうちに別物になる。

🎨 直感 — フィルタ後の数値はすべて「条件付き」

フィルタリングとは「条件を満たす部分集合の世界」へ移動することだ。 移動した後に計算する平均・分散・相関は、 数学的にはすべて条件付き統計量(例: $E[X \mid \phi]$)であって、 元の全体の統計量 $E[X]$ とは別物になる。 SSDSE-B-2026 の実測値で確かめると:

対象(2023 年・消費支出 L3221 の平均)件数平均
全 47 都道府県47295,856 円
df[df['A1101'] >= 2_000_000](総人口 200 万人以上)16306,586 円
df[df['A1101'] < 2_000_000](総人口 200 万人未満)31290,318 円

同じ「平均消費支出」でも、 どの条件で絞ったかで約 1.6 万円ずれる。 フィルタ条件は分析結果の前提条件(分母の定義)であり、 レポートでは「何をどう絞った後の値か」を必ず明記する必要がある。

⚠️ 落とし穴(重要)— 絞り込みが相関を減衰させる「範囲制限」

最も見落とされやすいのが範囲制限(restriction of range):ある変数の範囲を絞ってから相関を計算すると、 相関係数は一般に元より小さく出る。 SSDSE-B-2026(2023 年)の実測:

人口と診療所数の関係自体は変わっていないのに、 x 軸の範囲を絞っただけで r が 0.15 下がった。 逆に、 絞り込みによって相関が「生まれる」こともある(選抜された集団内だけで見ると本来無関係な 2 変数に負の相関が現れる選抜効果)。 つまり「フィルタしてから相関・回帰」の結果を、 全体に対する結論として語ってはいけない。 実務の防御策は 2 つ:(1) 絞り込みの各段階で件数を記録する(このデータなら 564 行 → 2023 年で 47 行 → 条件追加で…と減っていくログを残す)、 (2) フィルタ前後の散布図・分布を並べて「何を捨てたのか」を可視化する。 除外した行は「無かったデータ」ではなく「条件により見ないことにしたデータ」だ。

🚀 発展 — 「いつフィルタするか」で意味が変わる

🔗 関連ページ

この章の内容と直接つながる用語集ページ:

選択バイアス相関散布図groupbyサンプリング外れ値

※ 部分集団で見ると全体と傾向が逆転する現象は「シンプソンのパラドックス」と呼ばれる(本用語集には単独ページなし)。 フィルタ(層別)と集計の順序が結論を反転させ得る代表例として覚えておきたい。