「type conversion」は統計データ分析の文脈で扱う重要概念のひとつ。 本ページでは「type conversion」を取り巻く中核キーワードを以下にチップで一覧化する。 各キーワードは関連する概念・手法・道具立てを含み、 文献検索や学習計画の起点になる。
これらのキーワードは「type conversion の理解 → 適用 → 検証」のプロセスを構成する。 各章で詳しく解説する。
🍰 まずはやさしく
データの種類を書き換えることです。
分析を正しく行うために使います。
スマホの入力設定を変える感覚です。
この章では重要なポイントを学びます。
データ型を変換する処理
type conversion を 30 秒で把握する重要ポイント:
🍰 まずはやさしく
データの形式を整える作業です。
計算ミスやエラーを防ぐために使います。
買い物リストの数字が文字になる例です。
なぜこの作業が必要なのかを読みます。
CSV を読み込んだら「数値の列が文字列になっていた」「日付が単なるテキストだった」という経験は誰しもあるはず。 この「型を正しい型に直す」のが型変換。 これを怠ると、 sort が辞書順になったり、 集計で意味のない結果が出たり、 機械学習モデルがエラーを吐いたりします。 つまり、 分析の全工程に影響する、 一見地味で実は最重要な前処理。
🍰 まずはやさしく
データの見た目を着せ替えることです。
計算ができる形式にするために使います。
部活の出席簿を数字で管理する例です。
具体的な変換の方法について読みます。
型変換(type conversion / type casting)は「データの型を別の型に変換する処理」。 文字列 "100" を整数 100 にしたり、 整数 5 を浮動小数点 5.0 にしたり、 数値 1.20 を文字列 "1.20" にしたり。 SSDSE-B-2026 を pandas で読み込むと頻繁に必要になる。
SSDSE-B-2026 でよくある型変換シーン:
object になる: pd.read_csv() で「1,234,567」のようなカンマ区切り数値、 「N/A」などの欠損標識があると pandas は object(文字列)として読む。 df['総人口'] = df['総人口'].str.replace(',', '').astype(int) で int に変換が必要。R01000 を 1(北海道)に変換するには df['code_num'] = df['都道府県コード'].str[1:3].astype(int)。2023 を 2023-12-31 にして時系列処理。 df['年度'] = pd.to_datetime(df['年度'], format='%Y')。地域区分列をメモリ節約のため df['地域区分'] = df['地域区分'].astype('category')。 47 行のように小さいデータでは差は出ないが、 100 万行で 10 倍速くなる。df['合計特殊出生率'] = pd.to_numeric(df['合計特殊出生率'], errors='coerce') で float に。 不正値は NaN に。暗黙の型変換(implicit)vs 明示的型変換(explicit):
5 + 3.0 → 8.0(int + float = float)、 True + 1 → 2(bool → int)。 楽だが意図せぬ挙動の温床。int() float() str() list() など関数で変換。 安全だが書く量が増える。numpy / pandas の dtype: numpy には int8, int16, int32, int64, float32, float64, bool, object, datetime64, category など。 SSDSE-B-2026 の総人口(10⁶ オーダー)には int32 で十分(範囲 ±2.1×10⁹)。 ただし計算で乗算するとオーバーフローするので int64 推奨。 float は float32(メモリ半分)vs float64(精度高)。 ML では float32 が標準。
変換失敗の典型エラー:
ValueError: invalid literal for int() with base 10: '1,200' → カンマ除去が必要ValueError: could not convert string to float: '-' → 欠損値「-」を NaN に変換が必要OverflowError: int too large to convert to int32 → int64 使用UnicodeDecodeError → エンコーディング(SSDSE-B-2026 は encoding='cp932' または 'utf-8')言語別の型変換の流派: Python は動的型付け(実行時変換)、 静的型付けの C/Java は明示的キャスト (int)x 必須、 JavaScript は緩い暗黙変換で "1" == 1 → true のような落とし穴あり、 SQL では CAST(col AS INTEGER) または ::int(PostgreSQL)。
値を入力して変換先の型を選ぶと、 変換結果・成功/失敗・注意(精度落ち / 丸め / 前ゼロ消失 / '01'→1 など)がその場で表示されます。 さらに暗黙変換の罠、 pandas の errors='coerce' の挙動も手を動かして確認できます。 変換ロジックは Python / pandas の実挙動に合わせています(JS の緩い挙動との違いも明示)。
カードをタップすると解説が開きます。 緑=Python 橙=JavaScript
サンプル列 ['1.20','3','-','N/A','5,000','42'] を数値化。 手法を選ぶと結果が変わります。
型変換は「同じ値を別のビット表現・ルールに載せ替える」操作です。 "100"(文字の並び)と 100(整数)は人間には同じでも、 計算機にとっては全く別物。 文字列のままでは足し算が連結になり、 ソートが辞書順になります。 「この列はどの型で書かれた契約か」を常に意識するのが第一歩です。
暗黙変換: '5'+3 や True+1 のように、 言語が勝手に型を合わせて意図せぬ結果を生む。 精度損失: float32 は約7桁、 1.20→1.2(末尾ゼロ消失)、 float('0.1') は2進で厳密には表せない。 前ゼロ / 桁欠け: 県コード "01100"→1100 でキーが壊れる。 NaN化: to_numeric(errors='coerce') は失敗値を黙って NaN にするので件数チェックが必須。 ロケール依存の日付: 03/04/2023 は 3月4日か4月3日か、 国によって解釈が違う。 format= の明示が鉄則です。
スキーマ検証: pandera / pydantic で「この列は int32・非負・欠損なし」を宣言し、 読込時に自動検査すると型崩れを早期に捕捉できます。 型安全: 読込時 pd.read_csv(dtype={'Code':'string','A1101':'int32'}) と明示すれば後段の暗黙昇格を防げます。 カテゴリ型: 繰り返しの多い文字列列は astype('category') でメモリと groupby 速度を改善。 ordered=True で順序も固定できます。 欠損を保持したい整数は拡張型 Int64(先頭大文字)を使います。
🍰 まずはやさしく
ある型を別の型へ移す処理です。
データの性質を正しく扱うために使います。
テストの点数を文字から数値にする例です。
型変換のきまりと定義について読みます。
データ型を変換する処理
英語名 Type Conversion。
この用語を理解・使用するときは、 次のような前提を意識してください:
型 $T_1$ の値 $x$ から型 $T_2$ の値 $y$ への写像 $f: T_1 \to T_2$ を「型変換(キャスト)」と呼ぶ:
$$y = f(x), \quad x \in T_1, \quad y \in T_2$$情報が保存される(可逆)変換と、 情報が落ちる(不可逆)変換がある。 例: int → float は可逆、 float → int は小数部が落ちる。
| dtype | 意味 | 代表例 | 注意 |
|---|---|---|---|
| int64 | 整数 | 人口、 個数 | NaN を含めない |
| float64 | 浮動小数 | 支出金額、 率 | NaN OK、 比較は誤差注意 |
| object | 文字列(または混在) | 都道府県名 | 混在に注意 |
| datetime64[ns] | 日時 | 調査日、 取引日 | タイムゾーンに注意 |
| category | カテゴリ | 性別、 階層 | メモリ節約、 順序付け可 |
| bool | 真偽値 | 該当/非該当 | NaN は object 化 |
型変換は $T_{new} = \mathrm{cast}(T_{old}, \tau)$ という形式で書けます。 ここで $T_{old}$ は元の Series の dtype 、 $T_{new}$ は変換後の dtype 、 $\tau$ は目標型(例: int64, float32, category)です。 ただし、 単なる「型を読み替える」演算ではなく、 メモリ上のビット表現自体を別形式へ「変形」する操作であることに注意してください。
| 段階 | 意味(ことば) | SSDSE-B-2026 での例 |
|---|---|---|
| つまり | 型変換は「同じ値を別のビット表現へ写す」処理。 値そのものは 近似的に 保たれるが、 表現できる範囲・精度は変わる。 | 2026 年版の人口列 A1101 が int64 (8 バイト)で保存されている場合、 astype('int32') (4 バイト)に変換するとメモリは半分になる。 ただし最大値は 2,147,483,647 までしか保持できない。 |
| なぜなら | NumPy/pandas は同一列の中で すべての要素が同じビット幅 を取る必要があるため、 型は列ごとに「最低公倍数」で決まる。 | SSDSE-B-2026 の人口列に欠損 NaN が 1 つでも混じれば、 dtype は自動的に float64 に格上げされる(NaN は IEEE-754 の浮動小数でしか表現できない)。 これが「読み込んだら数値列が float になっていた」現象の正体。 |
| 具体的には | 2026 年版 SSDSE-B(n=47×46 列)で read_csv 後に df.dtypes を確認すると、 都道府県名(Prefecture)は object、 ほとんどの統計指標は int64 または float64 に解釈される。 | 例: df['A1101'].astype('Int64') とすれば NaN を保持したまま整数型として扱える(pandas 拡張型)。 これは「平成 17 年国勢調査人口」を整数で扱いたいが欠損も保持したい場合に必須。 |
| だから | 型変換の選択は 「メモリ削減」「精度保持」「欠損保持」 の三つ巴のトレードオフを意識する必要がある。 安易に astype(int) すると NaN がエラーになる。 | SSDSE-B-2026 の 47 行 ×46 列を category 化(都道府県名)+ Int32 化(人口) で再保存すると、 ファイルサイズが 30% 程度に圧縮できる。 大規模な集計や時系列接続では数百 MB の節約になる。 |
| 記号 | 言葉での意味 | SSDSE-B-2026 における具体例 |
|---|---|---|
$T_{old}$ | 元の dtype(メモリ上のビット形式)。 NumPy 由来の int64/float64/object と、 pandas 拡張型の Int64/Float64/string/category/boolean/datetime64 がある。 | SSDSE-B-2026 を pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) で読むと、 人口列は基本 int64 、 比率列は float64 、 都道府県名は object になる。 |
$T_{new}$ | 変換後 dtype 。 値域・精度・欠損許容性を含む新しい型仕様。 | 都道府県名 Prefecture を category 化すると、 47 個の一意な文字列を整数コードで持つ「辞書 + コード」形式に変わる。 47 行程度では効果は薄いが、 全国市町村(1,724 行)級では大きい。 |
$\tau$ | 変換指定子(文字列または型オブジェクト)。 'int32'、 'Int64'(拡張)、 np.float32、 'category' など。 | 2026 年版の出生率列 A4101(人口千人あたり、 小数 1 桁)を float32 にすれば、 メモリは 4 バイト/値で済む。 ただし丸め誤差で 12.34 が 12.339999 になる。 |
$\mathrm{cast}$ | 変換関数。 astype は明示変換、 pd.to_numeric は「変換できない値を NaN に倒す」柔軟変換、 pd.to_datetime は日付専用。 | SSDSE-B-2026 の年度列が "2020年" のような文字列で入っているとき、 pd.to_numeric(df['年度'].str.replace('年',''), errors='coerce') で年だけ取り出せる。 |
mem | メモリ使用量。 df.memory_usage(deep=True) で正確に取得(object 列の実バイトを deep=True で計測)。 | SSDSE-B-2026 の Prefecture 列は object 型で 1 県あたり約 60–80 バイト × 47 = 約 3KB 。 category 化すれば 47 行 ×1 バイト = 47 バイト + 辞書数百バイト。 |
覚え方: 「型は列の契約」と覚えるとよいです。 列に値を追加するときも、 集計するときも、 まず「この列はどの dtype で書かれた契約か」を確認します。 契約に違反する値(NaN、 文字列、 範囲外)は強制的に dtype を昇格させるか、 エラーで弾かれます。
実務で発生する典型的な失敗を「質問→診断→対処」の 3 段で洗います。 すべて SSDSE-B-2026 を使った検証例を載せます。
int64 列に NaN を 1 個入れただけで dtype が float64 になるのか?診断: NumPy の int 型は IEEE-754 を使わないため、 NaN を表す bit パターンが存在しない。 1 つでも欠損が入った瞬間、 pandas は内部で float64 に昇格させて NaN を収納する。 SSDSE-B-2026 で例えば A1101(総人口)に意図せず欠損が混入していると、 全行が float になる。
対処: 欠損を保持したまま整数で扱いたいなら、 pandas 拡張型 'Int64'(先頭大文字)を使う。 df['A1101'].astype('Int64') で NaN を整数列に保持できる。 内部的には欠損マスクを別配列で持つ。
astype('int32') で人口列を圧縮したらどこまで安全か?診断: int32 の最大値は約 21.5 億($2^{31}-1$)。 SSDSE-B-2026 の都道府県別人口は最大でも東京の 1,400 万人程度(A1101)なので、 47 行ベースなら全く問題ない。 ただし市町村レベルで集計するときは合計が 1 億を超えるため、 グループ集計後の dtype が暗黙に int64 に戻る場合があるので注意。
対処: 集計前に df['A1101'].astype('int32').sum() としても、 NumPy は オーバーフローを警告しない(silent wrap)。 安全策は df['A1101'].astype('int64').sum() で集計し、 結果を必要に応じて縮小型に戻す。
object (文字列)のまま比較しているのに何となく動いてしまうのはなぜ?診断: "2020-04-01" のような ISO 形式の文字列は、 辞書順比較が偶然 日付順 と一致する。 SSDSE-B-2026 は年次データなので影響は出にくいが、 月次・日次データで "4/1/2020" のような形式を使うと、 文字列比較では "4/1/2020" < "5/1/2019" となり、 年をまたいだソートが破綻する。
対処: df['date'] = pd.to_datetime(df['date'], errors='coerce') で datetime64[ns] に変換してから比較・ソート・絞り込みを行う。 SSDSE-B-2026 の年度列は数値なので、 df['年度'].astype('int16') で十分。
category 化はいつ得するのか? SSDSE-B-2026 の Prefecture でやる意味は?診断: category 型は「ユニーク値が少ない / 行数が多い / 比較や groupby で多用する」場合にメモリと速度の双方で勝つ。 SSDSE-B-2026 単体(n=47)では効果は微小だが、 年度を縦に積んだ 10 年分(n=470)以上、 さらに性別や年齢階層を加えると効いてくる。
対処: df['Prefecture'] = df['Prefecture'].astype('category')。 さらに cat.set_categories([...], ordered=True) で表示順序を北海道→沖縄の自然順に固定すれば、 グラフ軸や groupby の結果が安定する。
to_numeric(errors='coerce') と astype(float) の使い分けは?診断: astype(float) は変換失敗時に例外を投げる。 一方 pd.to_numeric(s, errors='coerce') は失敗値を NaN に倒すため、 「汚いデータでも前進したい」 EDA フェーズに向く。 SSDSE-B-2026 のような綺麗な公式統計データなら astype で十分。 民間データや手入力 CSV では to_numeric が安全。
対処: ETL の方針として「本番では astype(例外検知)、 探索では to_numeric(前進優先)」を分ける。 失敗値の件数は pd.to_numeric(s, errors='coerce').isna().sum() - s.isna().sum() で算出して品質指標化する。
型変換における「メモリ使用量 $M$」は次の式で表せます:$M = N \times s(\text{dtype})$、 ここで $N$ は要素数、 $s(\text{dtype})$ は型ごとのバイト数。 一語ずつほどきます。 $N$:行数 × 列数の総セル数。 SSDSE-B-2026 の 47 都道府県 × 数十列 ≒ 数千セル。 たかが千件と思いがちですが、 月次データに展開すれば 47 × 12 × 41 年 = 23,124 セル、 さらに市町村レベルでは 1741 × 12 × 41 ≒ 856 万セルと一気に増えます。 $s(\text{dtype})$:型ごとのバイト数。 int8=1, int16=2, int32=4, int64=8, float32=4, float64=8, object=ポインタ 8 + 文字列実体。 object 型はポインタ表現なので、 文字列の長さに応じて実体メモリが膨らみ、 memory_usage('deep') で初めて正確な値が見えます。 ダウンキャストは、 値の取り得る範囲に応じて型を縮めることでメモリを 1/2, 1/4, 1/8 と直接減らせます。 たとえば SSDSE-B-2026 の県別人口は最大でも東京の 1400 万人なので、 int32(最大 21 億)で十分。 int16(最大 32,767)では足りない。 一方、 県別出生数は最大でも 10 万程度なので int32 で 2 倍、 int16 ならさらに 2 倍メモリ節約できます。 category 型は文字列を内部で 0,1,2,... の int に置き換え、 別の dictionary で文字列対応を保持します。 47 都道府県のように繰り返しが多い列なら、 47 件 × 平均 6 バイト ≒ 300 バイト + 整数列 47 × 1 バイト = 約 350 バイト。 一方 object のままなら 47 行 × 平均 6 文字 × 数千行に膨れます。 こうした「型は安全に最小化する」の原則を守るのが、 大規模パイプラインのコスト最適化の第一歩です。
🎯 ねらい:型変換は ETL の入口で「後工程の品質と速度を一気に決める」工程。 SSDSE-B-2026 の Code 列ゼロ落ちや人口列 int64 過多を一掃して、 後段の集計・回帰・ML を高速・安全にします。
📥 入力:CSV ファイル data/raw/SSDSE-B-2026.csv。 列は Code(地域コード R01000 等)、 Prefecture(都道府県名)、 A1101(総人口)、 A4101(出生数)など。 R 始まりの Code をそのまま数値へ切り出すと先頭ゼロが落ちるため、 文字列保持が必要。
📤 出力:dtype 最適化後の DataFrame と、 メモリ使用量の before/after 表。 Code は文字列、 Prefecture は category、 人口・出生数は int32、 出生率は float32 に揃え、 メモリは半分以下に。
💬 解釈:型変換は単なるメモリ最適化を超え、 「意味の保持」が最重要。 Code は文字列でないとゼロ落ち、 Prefecture は category でないと groupby が遅い。 「正しい型」を意識するだけで、 後工程のバグの 7 割は予防できます。
型変換は数値を裏で別表現に変えるため、 誤った変換が起きてもエラーが出ない ケースが多く、 後工程まで気付きにくい問題が多発します。 SSDSE-B-2026 を念頭に、 可視化で型変換の正当性を検証する流儀を 3 つの図でまとめます。

int64 → int32 に変換しても通常は分布は同じだが、 オーバーフロー (上限超え) が起きると右端が削れる。 また float64 → float32 では小数下位の精度落ちで微妙に丸まる。 ヒストグラムが目視で異なる場合は精度問題が起きている。
0 桁が消えて全く別の意味のキーになるのが典型例。
これら 3 図のチェックは 1 セット 5 分以内で終わるので、 すべての型変換処理に対して習慣化することを推奨します。 型変換の問題は実行時にはエラーが出ないため、 「結論が出てから気付く」 のが最も典型的な失敗パターン。 可視化チェックで前段で検知することが、 分析全体の信頼性を守ります。
| 確認段階 | 使う図 | 検知できる問題 | 主な対応 |
|---|---|---|---|
| 単変量 | ヒストグラム | 精度落ち、 オーバーフロー | より広い型に戻す |
| 二変量 | 散布図 | ゼロ落ち、 NaN 化 | 文字列保持、 別変換経路 |
| 多変量 | 相関行列 | 関係性の崩れ、 丸め | 浮動小数精度の見直し |
float64 → float32 の変換で失うものは何か? 実際に影響が大きい場面は?category 型に変換するメリットを 2 つ挙げよ。datetime64 型に変換した後、 dt.year などのメソッドが使える理由は?errors='coerce')→ すべて即答できれば、 型変換の実務で困らないレベル。 不安な項目は本ページの該当セクションを再読してください。
型変換は状況に応じて 3 種類のパターンがあります。 「メモリ最適化」、 「型整合」、 「機械学習前処理」。 ここでは SSDSE-B-2026 を念頭に、 それぞれのパターンと注意点をまとめます。
pandas の数値列はデフォルトで int64 / float64 ですが、 SSDSE-B-2026 の総人口 (最大 1400 万人) なら int32 で十分。 出生率 (0 〜 20 程度) なら float32 どころか float16 でも可。 これにより DataFrame のメモリ使用量を半分以下にできます。 大規模データでは category 型への変換も強力で、 都道府県名 (47 種類) のような繰り返しが多い文字列列は、 メモリと処理速度の両面で 10 倍以上の改善が見込めます。
複数の CSV や JSON を結合する際、 同じ意味の列が異なる型になっていることがよくあります。 SSDSE-B-2026 の県コードが片方では str、 もう片方では int だと merge が失敗します。 こうした型整合のための型変換は 結合前に必ず両方の DataFrame で型を揃える のが原則です。 日付列も str と datetime64 が混在すると同様の問題を起こすため、 結合直前で型を統一しましょう。
scikit-learn は float64 の数値配列を前提とするため、 整数列や文字列カテゴリ列を float 化する処理が必須。 OneHotEncoder や StandardScaler を ColumnTransformer で連結し、 「文字列 → ワンホット浮動小数」 「整数 → 標準化された浮動小数」 を一括で行うのが現代的パターン。 SSDSE-B-2026 で都道府県を予測対象に使うなら、 県コード列は必ずワンホット化してから学習させましょう。
| パターン | 主な目的 | 代表的な変換 | よくある失敗 |
|---|---|---|---|
| メモリ最適化 | サイズ削減・高速化 | int64 → int32, str → category | オーバーフロー |
| 型整合 | 結合可能性の確保 | str ↔ int の統一 | merge エラー |
| 前処理 | モデル学習可能化 | str → onehot float | 未知カテゴリ問題 |
これら 3 パターンを使い分けると、 「型変換は単なるおまじない」 という素朴な発想を超えて、 「型変換を分析設計の意思決定として行う」 段階に進めます。 SSDSE-B-2026 のような実データでこれを 1 周回すと、 型変換にまつわる落とし穴を体感的に学べます。
文字列から数値への変換 (pd.to_numeric) では、 変換失敗時の挙動を errors 引数で指定できます。 'raise' (デフォルト、 例外発生)、 'coerce' (失敗を NaN にする)、 'ignore' (失敗時はそのまま)。 SSDSE-B-2026 のように事前にクレンジング済みデータならデフォルトで十分ですが、 業務データの初回取り込み時は 'coerce' を使い、 後で NaN 行を抽出して原因を調べるのが定石です。 'ignore' は silent failure を招くため、 原則使わないことを推奨します。
また日付変換 (pd.to_datetime) も同じ errors 引数を持ちます。 「2026/05/30」 のような複数フォーマットが混在する CSV では format= を明示せず自動推論させると遅いので、 format='%Y/%m/%d' を明示するのが速度上の鉄則です。 これだけで日付パースが 100 倍速くなることがあります。 SSDSE-B-2026 では年単位データなので問題ありませんが、 ログデータを扱う場合に重要なテクニックです。
最後に、 型変換のドキュメント化です。 「なぜこの列をこの型に変換したか」 を docstring やコメントに残すことで、 数か月後の自分または他のメンバーが理解できる保守性が確保されます。 df['code'] = df['code'].astype('str') という 1 行のコードでも、 「県コードのゼロ落ち防止のため文字列保持」 とコメントを残すだけで、 価値が大きく変わります。 SSDSE-B-2026 で身につけたこの感覚は、 そのまま大規模データの分析現場で通用します。
pandas 1.0 以降、 Int64 (大文字始まり) という欠損対応の整数型が導入されました。 従来の int64 は欠損を扱えず、 NaN が混じると自動的に float64 に格上げされていました。 これにより整数列でも 「メモリ効率の良い整数のまま欠損を表現」 できるようになり、 SSDSE-B のような公的データでも欠損年度がある列で重宝します。 同様に boolean、 string、 Float64 も nullable 型として導入されました。
pandas 2.x からは Arrow バックエンド (dtype_backend='pyarrow') が選択でき、 メモリ効率と速度が大幅に向上します。 とくに文字列列を string[pyarrow] 型で扱うと、 大規模 CSV の処理時間が数倍速くなります。 SSDSE-B-2026 規模では恩恵が見えにくいですが、 数億行スケールでは Arrow 型なしでは処理が現実的でなくなることがあります。 将来的に標準的な選択肢になる方向なので、 今のうちに触っておく価値があります。
int32 は ±21 億までしか扱えない。 SSDSE-B の総人口 (日本全国 1.2 億) は OK だが、 累積値や金額計算では int64 が必須。float32 は 7 桁、 float16 は 3 桁の精度しかない。 経済データの円単位計算では float64 必須。category 型に変換した後、 新しい値を代入すると失敗する。 「学習時に存在しなかった県」 などで発生する典型問題。format= を明示しないと国別解釈の違いでバグる。これら 5 つの落とし穴を 1 つも踏まずに型変換を扱える人は、 実務で 「型まわりの問い合わせ係」 として重宝されます。 逆に言うと、 これらを 1 度でも踏むと数日単位のデバッグが発生するため、 事前知識として持っておく価値が極めて高いトピックです。 SSDSE-B-2026 で今すぐ手を動かして、 各落とし穴を意図的に再現する練習をしてみることをお勧めします。 「失敗を意図的に起こす経験」 が、 本番でのバグを未然に防ぐ最強の予防接種になります。
本ページで型変換の基礎・実務 3 パターン・落とし穴を押さえました。 次に学ぶべきは クレンジング処理 (型変換は前処理の一環)、 DataFrame (型のホームグラウンド)、 欠損メカニズム (nullable 型と密接に関連) です。 さらに pandas のドキュメント (pandas.api.types) には型判定・型変換のヘルパー関数が多数あり、 これらを使うと型まわりのコードが大幅に簡潔になります。
SSDSE-B-2026 を題材に、 「同じ DataFrame を 3 種類の異なる型構成で保存し、 メモリ使用量と読み込み速度を比べる」 という練習を 1 度やってみると、 型変換の感覚が一気に身につきます。 たとえば全列 object (デフォルト)、 適切な型 (int32/float32/category 混在)、 Arrow バックエンドの 3 種で比較すれば、 型変換が分析パイプライン全体に与える影響を体感的に理解できます。
pandas の型は実態として numpy の型をラップしています。 SSDSE-B-2026 を扱う際の主要な対応は次の通りです: int8/int16/int32/int64 (符号付き整数、 数字はビット幅)、 uint8/uint16/uint32/uint64 (符号なし整数)、 float16/float32/float64 (浮動小数)、 complex64/complex128 (複素数、 統計用途では稀)、 bool (真偽値、 1 バイト)、 object (Python オブジェクト、 文字列や混在型)。 これらをコード上で正しく選ぶには、 np.iinfo(np.int32) や np.finfo(np.float32) で範囲・精度を確認するのが安全です。
df.dtypes で表示される型は numpy 型 (例: int64) の場合と、 pandas 拡張型 (例: Int64、 category) の場合があります。 後者は欠損対応・カテゴリ最適化など pandas 独自の機能を持ち、 機械学習前処理の文脈でも頻出します。 とくに category 型は scikit-learn 1.4 以降でネイティブにサポートされる方向で進んでおり、 今後ますます重要になります。 SSDSE-B-2026 で都道府県列を category 型に変換しておけば、 既存パイプラインがそのまま使えて移行も楽になります。
最後に、 型変換は 「データの意味を保持しながら、 計算機にとって扱いやすい形に変える」 作業であることを忘れないでください。 メモリ最適化のためだけに行うのではなく、 「この列はどんな意味を持ち、 どの操作が必要か」 を考えてから型を選ぶことで、 分析パイプライン全体が見通しよくなります。 SSDSE-B-2026 のような実データで何度も練習し、 自分なりの型選択ガイドラインを 1 つ作り上げてください。 そのガイドラインが、 大規模データを扱う際の指針になります。
df.dtypes を確認したかfloat64 に揃ったかこのチェックリストを SSDSE-B-2026 で 1 度通せば、 型変換の落とし穴をほぼ網羅的に避けられます。 大規模データでも基本は同じで、 扱うサイズに応じて Arrow 型などを追加検討するだけです。 型変換は分析の信頼性を支える地味だが重要な工程なので、 ぜひ習慣化してください。
最後に、 型変換を 「分析設計の一部」 として扱う視点を強調します。 分析の目的が変わると、 最適な型も変わります。 たとえば SSDSE-B-2026 で 「人口の時系列推移」 を分析するなら、 年度列は datetime64 型が便利。 「都道府県別ランキング」 を出すなら、 都道府県列は category 型で順序を明示するのが便利。 「機械学習モデルで将来人口を予測」 するなら、 全列を float64 の数値配列に変換しておくのが効率的です。
このように、 「同じデータでも分析目的によって最適な型構成が異なる」 ことを意識すると、 型変換は単なる前処理ではなく、 分析設計そのものの一部として扱えるようになります。 これが上級者の感覚です。 SSDSE-B-2026 で 3 種類の分析目的を立て、 それぞれで最適な型構成を考える練習をすると、 この感覚が一気に身につきます。 ぜひ実践してみてください。
型変換にまつわる議論はここで一旦完結しますが、 実務では 「型変換のために多大な時間を使う」 段階が必ず訪れます。 たとえばレガシーシステムからの CSV を読むと、 数値が文字列、 日付が整数、 真偽値が 「Y/N」 のような文字列、 という混沌としたデータが届きます。 これを pd.read_csv の dtype= 引数や converters= 引数で一発で正しく読み込めるようになると、 分析の初動が圧倒的に速くなります。 この技を習得するための最短経路は、 SSDSE-B-2026 のような綺麗なデータをわざと汚し (列を文字列に再保存するなど)、 それを正しく読み込み直す練習を繰り返すことです。 「壊して直す」 サイクルが、 型変換スキルを高める最良の道です。
最終的に、 型変換を意識せず分析できる人と、 型変換を中心に考えながら分析する人では、 同じデータからでも引き出せる知見の質と量に大きな差が生まれます。 SSDSE-B-2026 で得たこの感覚を、 大規模データや非構造化データの場面でもそのまま適用できるよう、 ぜひ繰り返し練習し続けてください。 そして関連用語の DataFrame・クレンジング処理・欠損メカニズム も合わせて学び、 データ前処理の総合的なスキルセットを完成させましょう。 これらは相互に深く絡み合っており、 1 つだけ完璧でも実務では役に立ちません。 4 つの用語をセットで習得すると、 「データを受け取ったらまず何をすべきか」 が体系的に判断できるようになります。 この判断速度こそが、 実務で生産性の差を生む最大の要因です。 そして判断の正確さは、 SSDSE-B-2026 のような実データで何度も試行錯誤を重ねることで初めて身につきます。 教科書だけで身につくものではないため、 必ず手を動かして学習することを強く推奨します。 そして練習の成果は、 実務の現場で扱う非構造化データや業務 CSV を相手にしたときに、 体感として現れ、 自分が成長したことを確実に実感できる瞬間が訪れます。 その瞬間まで、 ぜひ粘り強く取り組んでください。 学習の継続が、 最も確実な成長の道であり、 短期的なショートカットは存在しないと割り切ることが、 上達への最初の一歩であり、 最も大切な姿勢です。
同じ「47 都道府県」のデータを違う型で保存した場合のメモリ比較:
| 列内容 | object型 | category型 | 節約率 |
|---|---|---|---|
| 47 都道府県名 (47行) | ~3.5 KB | ~0.6 KB | 83% |
| 47 都道府県名 × 10年 (470行) | ~35 KB | ~2 KB | 94% |
| int64 → int32 | 8 bytes/値 | 4 bytes/値 | 50% |
| float64 → float32 | 8 bytes/値 | 4 bytes/値 | 50% |
SSDSE-B-2026 を素直に read_csv した状態と、 型最適化後の memory_usage(deep=True) を比較します。 47 行のため絶対値は小さいですが、 列構成と最適化方針は他のデータでも転用可能です。
| 列名(SSDSE-B-2026) | 初期 dtype | 初期メモリ (bytes) | 最適化 dtype | 最適化後 (bytes) | 削減率 |
|---|---|---|---|---|---|
| Code | int64 | 376 | int16 | 94 | 75% |
| Prefecture | object | 3,290 | category | 312 | 91% |
| A1101 (総人口) | int64 | 376 | int32 | 188 | 50% |
| A1301 (15歳未満人口) | int64 | 376 | int32 | 188 | 50% |
| A1303 (65歳以上人口) | int64 | 376 | int32 | 188 | 50% |
| A4101 (出生数) | int64 | 376 | int32 | 188 | 50% |
| A4200 (死亡数) | int64 | 376 | int32 | 188 | 50% |
| A4103 (合計特殊出生率) | float64 | 376 | float32 | 188 | 50% |
| 合計(46列の概算) | — | 約 17,000 | — | 約 8,500 | 50% |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import pandas as pd # 素直に読み込む df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) print('Before:', df.memory_usage(deep=True).sum(), 'bytes') # 型最適化 df['Prefecture'] = df['Prefecture'].astype('category') df['Code'] = df['Code'].str.lstrip('R').astype('int32') # 'R01000' の R を外す(47000 まで入るので int32) # 整数列 (A で始まる人口・出生死亡系) は int32 で十分 for col in df.columns: if col.startswith('A') and df[col].dtype == 'int64': df[col] = df[col].astype('int32') if col.startswith('A') and df[col].dtype == 'float64': df[col] = df[col].astype('float32') print('After: ', df.memory_usage(deep=True).sum(), 'bytes') print(df.dtypes.value_counts()) |
この出力例:
Before: 17256 bytes
After: 8632 bytes
int32 38
float32 6
category 1
int16 1
注意: SSDSE-B-2026 は 47 行のため絶対メモリは KB 単位ですが、 時系列で 10 年分・100 年分と縦に積む or 国際比較で 200 国分に広げると、 同じ型最適化で 数 GB → 数百 MB の差になります。 型は「実行速度」にも効きます(SIMD ベクトル化が dtype の幅で変わるため、 int32 化で集計が 1.5–2 倍速くなる)。
SSDSE-B-2026 の典型カラム値 (合計特殊出生率 1.20、 人口 "1396" を文字列で読み込んだ場合、 列名 "A1101" 等) を題材に、 int / float / str の三方向変換結果と落とし穴 (切り捨て・ERR・情報損失) を計算する。
| 元 | int | float | str |
|---|---|---|---|
| 3.7 (float) | 3 (切り捨て) | 3.7 | "3.7" |
| "5" (str) | 5 | 5.0 | "5" |
| "3.14" (str) | ERR | 3.14 | "3.14" |
1 2 3 | print(int(3.7)) print(float("5")) print(int(float("3.14"))) |
💬 手計算 (Step 2) と Python 出力が完全一致。
SSDSE-B-2026 のような公的統計データを Python で扱う際の基本パターン:
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()) # 「型変換」の文脈で扱う場合の例: # 分野: データ前処理 # 関連手法は同カテゴリの他用語を参照してください。 |
具体的なコードは データエンジニアリング を参照してください。
分析結果を報告するときに含めるべき情報:
TypeError が出ない状態に整える。data/raw/SSDSE-B-2026.csv (CP932、 2 行ヘッダ=1 行目コード・2 行目日本語名)。 skiprows=[1] で 2 行目を飛ばし、 コード名(A1101 等)を列名として読む。 これに .astype() と df.astype({...}) を組み合わせる。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]) # 総人口 A1101 を int64→int32 に縮小 df['A1101'] = df['A1101'].astype('int32') # 地域コード Code を文字列に(ゼロ落ち防止) df['Code'] = df['Code'].astype(str) # 複数列を一度に df = df.astype({'A4103': 'float32', 'B4101': 'float32', 'Prefecture': 'category'}) print(df.dtypes) |
astype() は「失敗したら例外を投げる」厳しい変換。 SSDSE-B-2026 の A1101(総人口)のように数字のみなら成功するが、 列に「-」「N/A」が紛れていれば ValueError になる。 そのときはパターン 2 の to_numeric(errors='coerce') に切り替える。pd.to_numeric(errors='coerce') でそれらを NaN に置換、 downcast でメモリも 4 倍節約。1 2 3 4 5 6 7 8 9 | # errors='coerce' で変換できない値を NaN に df['A5101'] = pd.to_numeric(df['A5101'], errors='coerce') # errors='raise' (デフォルト) でエラーを上げる # errors='ignore' で元の値を保持 # downcast でメモリ節約 df['A1101'] = pd.to_numeric(df['A1101'], downcast='integer') # int8〜int64 の最小 df['A4103'] = pd.to_numeric(df['A4103'], downcast='float') |
errors='coerce' は「変換できないものは NaN にする」現場で最も安全な選択肢。 SSDSE-B-2026 のように欠損記号が「-」「…」で混在する公的統計の取り扱いに必須。 downcast 後はメモリ消費が半減〜1/4 になり、 大規模パネル分析でも実用速度になる。datetime64[ns] 型に変換し、 日付差・週次集計・タイムゾーン換算を可能にする。tz_localize + tz_convert を組み合わせる。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 | import pandas as pd # この抜粋だけで動くように、日付を持つ表を作る # (SSDSE-B に日付列は無いので、年度から 4/1 の日付を組み立てる) df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df[df['Prefecture'] == '東京都'][['SSDSE-B-2026', 'A1101']].copy() df['year'] = df['SSDSE-B-2026'].astype(int) df['month'] = 4 df['day'] = 1 df['日付'] = df['year'].astype(str) + '/04/01' df['日時'] = df['日付'] + ' 09:00:00' # ISO 形式は自動認識 print(pd.to_datetime(df['year'].astype(str) + '-04-01').head(3)) # format 指定(速度・確実性 UP) df['日付'] = pd.to_datetime(df['日付'], format='%Y/%m/%d') # 年月日が別列なら df['日付2'] = pd.to_datetime(df[['year', 'month', 'day']]) # タイムゾーン付き df['UTC日時'] = (pd.to_datetime(df['日時']) .dt.tz_localize('Asia/Tokyo').dt.tz_convert('UTC')) print(df[['日付', '日付2', 'UTC日時']].head(3)) |
format= を指定すると変換速度が 10〜100 倍速くなる(自動推測を回避)。 SSDSE のような国内データは Asia/Tokyo で localize → UTC に揃えると、 海外データと突合するときに DST (夏時間)バグが起きない。pd.CategoricalDtype(categories=[...], ordered=True) を渡す。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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][['Prefecture', 'A1101']].copy() df = df.rename(columns={'Prefecture': '都道府県'}) df['規模'] = pd.cut(df['A1101'], bins=[0, 1_500_000, 3_000_000, 1e9], labels=['小', '中', '大']).astype(str) # 単純なカテゴリ化 df['都道府県'] = df['都道府県'].astype('category') # 順序付きカテゴリ size_order = pd.CategoricalDtype(categories=['小', '中', '大'], ordered=True) df['規模'] = df['規模'].astype(size_order) # メモリ削減効果を確認 print(df.dtypes) print(df.memory_usage(deep=True)) |
sort_values、 groupby、 不等式比較で「小 < 中 < 大」を自動認識するので、 学年・震度・優先度などの順序データに最適。1 2 3 4 5 6 7 8 9 10 11 12 | def optimize_dtypes(df): for col in df.select_dtypes(include=['int']).columns: df[col] = pd.to_numeric(df[col], downcast='integer') for col in df.select_dtypes(include=['float']).columns: df[col] = pd.to_numeric(df[col], downcast='float') for col in df.select_dtypes(include=['object']).columns: if df[col].nunique() / len(df) < 0.5: df[col] = df[col].astype('category') return df df = optimize_dtypes(df) print(df.info(memory_usage='deep')) |
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]) # 全列の dtype を一覧 print(df.dtypes.value_counts()) # int64 104 # float64 6 # object 2 # 各列ごとのメモリ使用量 mem = df.memory_usage(deep=True).sort_values(ascending=False) print(mem.head(10)) |
1 2 3 4 5 6 7 8 9 10 11 12 13 | df_small = df.copy() for col in df_small.select_dtypes(include='int64').columns: cmin, cmax = df_small[col].min(), df_small[col].max() if cmin >= -32768 and cmax <= 32767: df_small[col] = df_small[col].astype('int16') elif cmin >= -2_147_483_648 and cmax <= 2_147_483_647: df_small[col] = df_small[col].astype('int32') for col in df_small.select_dtypes(include='float64').columns: df_small[col] = df_small[col].astype('float32') print(f"Before: {df.memory_usage(deep=True).sum() / 1024:.1f} KB") print(f"After: {df_small.memory_usage(deep=True).sum() / 1024:.1f} KB") |
jis_order = [
'北海道','青森県','岩手県','宮城県','秋田県','山形県','福島県',
'茨城県','栃木県','群馬県','埼玉県','千葉県','東京都','神奈川県',
'新潟県','富山県','石川県','福井県','山梨県','長野県','岐阜県',
'静岡県','愛知県','三重県','滋賀県','京都府','大阪府','兵庫県',
'奈良県','和歌山県','鳥取県','島根県','岡山県','広島県','山口県',
'徳島県','香川県','愛媛県','高知県','福岡県','佐賀県','長崎県',
'熊本県','大分県','宮崎県','鹿児島県','沖縄県']
df['Prefecture'] = pd.Categorical(df['Prefecture'], categories=jis_order, ordered=True)
# これで sort_values('Prefecture') は北→南の自然順になる
1 2 3 4 5 6 7 8 9 10 11 | import numpy as np # 仮に A1101 に欠損を 1 つ混入させた例 df_test = df.copy() df_test.loc[0, 'A1101'] = np.nan print(df_test['A1101'].dtype) # float64 に昇格 # Int64 (大文字) で NaN を保持しつつ整数列に戻す df_test['A1101'] = df_test['A1101'].astype('Int64') print(df_test['A1101'].dtype) # Int64 print(df_test['A1101'].iloc[0]) # <NA> |
raw = pd.Series(['1000','2,500','3500人','-','4500'])
# 方法 A: 各種クリーニング + to_numeric
clean = (raw.str.replace(',', '', regex=False)
.str.replace('人', '', regex=False)
.replace('-', None))
result = pd.to_numeric(clean, errors='coerce')
# 0 1000.0
# 1 2500.0
# 2 3500.0
# 3 NaN
# 4 4500.0
1 2 3 4 5 6 7 8 | # 都道府県コード R01000 → R01000 のまま文字列で保持したい場合 # (数値化するとゼロ落ちで '01' が 1 になる) df_str = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1], dtype={'Code': str}) print(df_str['Code'].head()) # 0 R01000 # 1 R01000 # 2 R01000 # (省略) |
1 2 3 4 5 6 7 8 9 10 | # 高齢化率 = 65歳以上人口 / 総人口 df['elderly_ratio'] = df['A1303'] / df['A1101'] print(df['elderly_ratio'].dtype) # float64 # float32 へ縮小(精度落ちは小数 7 桁程度なので影響軽微) df['elderly_ratio'] = df['elderly_ratio'].astype('float32') # 沖縄を確認: 比率は 22-23% 程度 oki = df[df['Prefecture'] == '沖縄県']['elderly_ratio'].iloc[0] print(f"沖縄高齢化率: {oki:.4f}") |
1 2 3 4 5 6 7 8 | # 「人口 100 万人超」 フラグ df['big_pref'] = df['A1101'] > 1_000_000 print(df['big_pref'].dtype) # bool(NumPy)= 1 バイト print(df['big_pref'].sum()) # 12 都府県 (東京, 神奈川, 大阪, 愛知, 埼玉, 千葉, 兵庫, 北海道, 福岡, 静岡, 茨城, 広島) # NaN を保持したい場合は pandas BooleanDtype df['big_pref'] = df['big_pref'].astype('boolean') |
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 | import numpy as np def reduce_mem_usage(df): start = df.memory_usage(deep=True).sum() / 1024**2 for col in df.columns: col_type = df[col].dtype if col_type != object: c_min, c_max = df[col].min(), df[col].max() if str(col_type).startswith('int'): if c_min >= np.iinfo(np.int8).min and c_max <= np.iinfo(np.int8).max: df[col] = df[col].astype(np.int8) elif c_min >= np.iinfo(np.int16).min and c_max <= np.iinfo(np.int16).max: df[col] = df[col].astype(np.int16) elif c_min >= np.iinfo(np.int32).min and c_max <= np.iinfo(np.int32).max: df[col] = df[col].astype(np.int32) else: df[col] = df[col].astype(np.float32) else: df[col] = df[col].astype('category') end = df.memory_usage(deep=True).sum() / 1024**2 print(f"Memory: {start:.3f} MB -> {end:.3f} MB ({(1 - end/start)*100:.1f}% reduction)") return df df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = reduce_mem_usage(df) # Memory: 0.016 MB -> 0.008 MB (47.9% reduction) |
df['col'].astype(int) は NaN があれば ValueError。 → 'Int64'(pandas 拡張型)か fillna() で対応。int 化できない。 str.replace(',', '').astype(int) で除去してから変換。to_datetime 失敗。 専用パーサが必要。0.1 + 0.2 != 0.3 の罠。 金額・率では Decimal や整数(円単位)の方が安全。observed=True で抑制。Int64 なら NaN を保ったまま整数を維持。== が成立しない同じ「0.1」でも float32 と float64 で内部表現が違うため、 np.float32(0.1) == np.float64(0.1) は False。 SSDSE-B-2026 の比率列を float32 化した上で「特定の値と一致する県」を探すと、 期待した結果が空集合になる。 比較は常に np.isclose(a, b, atol=1e-6) で行う。
category 列にない値を代入すると NaN になるPrefecture を category 化した後、 新しい県を追加しようとして df.loc[len(df)] = ['仮想県', ...] をすると、 「仮想県」がカテゴリ辞書にないため NaN に化ける(警告は出るが pandas 2.x では FutureWarning)。 新規値を追加する場合は df['Prefecture'] = df['Prefecture'].cat.add_categories(['仮想県']) で辞書を拡張する。
astype(str) は NaN を "nan" 文字列にしてしまう数値列を文字列化するときに NaN がそのまま "nan" という 3 文字の文字列に変換され、 後段の集計で「nan という県名」のように扱われる。 SSDSE-B-2026 で問題になる場合は pandas.StringDtype() ('string')を使うと、 NaN は pd.NA として保持される。
pd.to_datetime はタイムゾーンを暗黙に剥がす日本標準時の文字列を to_datetime すると、 デフォルトでは tz-naive(タイムゾーン情報なし)になる。 国際比較で UTC と混ぜると 9 時間ズレる。 SSDSE-B-2026 は年単位なので影響は出にくいが、 月次/日次の人口動態統計と接続する場合は pd.to_datetime(s, utc=True).tz_convert('Asia/Tokyo') を徹底する。
convert_dtypes() は便利だが結果が一意でないdf.convert_dtypes() は「pandas 拡張型に変換できそうなら変換」する関数だが、 NaN の有無や値の範囲によって Int64 になったり Float64 になったりするため、 同じ列でも実行タイミングで dtype が変わる。 本番では明示変換(astype)を使い、 convert_dtypes は探索フェーズの初手限定にする。
47 行のままだとメモリ差は実感しにくいので、 同じデータを pd.concat で 1000 回繰り返して 47,000 行にして計測しました(縦に時系列で重ねるイメージ)。
| 処理 | 素の dtype | 最適化後 dtype | 所要時間(秒) | メモリピーク |
|---|---|---|---|---|
| read_csv | — | — | 0.045 | — |
| concat ×1000 | int64/float64/object | — | 0.32 | 16.5 MB |
| 型最適化(int32/float32/category) | — | — | 0.08 | — |
| groupby('Prefecture').sum()(最適化前) | int64/object | — | 0.21 | 16.5 MB |
| groupby('Prefecture').sum()(最適化後) | — | int32/category | 0.07 | 4.2 MB |
| describe()(最適化後) | — | int32/float32 | 0.04 | 4.2 MB |
結論: 型最適化は メモリ 1/4、 集計速度 3 倍 程度の効果がある。 さらに大規模化すれば差は拡大する。 SSDSE-B-2026 自体は小さいが、 全国市町村(1,724 行)や時系列接続(数十万行)で必須の習慣。
read_csv 直後と read_csv(dtype=...) 指定後でメモリが違うのか?デフォルトでは pandas は「列ごとに最大公約数の型」を推測します。 整数だけの列でも int64 (最大)を選ぶことが多いため、 値域が int8 で済む列でも int64 になり 8 倍のメモリを使います。 dtype={'A1101': 'int32'} のように明示すれば、 読み込み時点から最適化できます。 SSDSE-B-2026 のように既知のスキーマがあるなら、 dtype 辞書を作っておくのが速度面でも有利。
int8 って実務で使う場面はあるのか?あります。 SSDSE-B-2026 で言えば、 「年齢階層コード(0–10)」「都道府県区分(1–47)」のような有限の小さな整数列に最適です。 さらに ML モデルへの入力では、 ラベルエンコーディング後のクラス番号(0–9)に int8 を使えばメモリと CPU 効率の両方で勝てます。 ただし集計や演算で int8 同士の積をするとオーバーフロー警告なしで wrap するため、 集計の前に int32 へ昇格させる癖が必要。
最低でも次の 3 タイミングで df.dtypes を確認すべきです: (1) read_csv 直後、 (2) merge/join 直後、 (3) モデル入力前。 SSDSE-B-2026 と他データを結合した瞬間、 object 列が発生して数値演算が壊れることがあるため、 結合直後の dtype チェックは特に重要です。
convert_dtypes と infer_objects の違いは?infer_objects() は object 型を NumPy 型に推測 する控えめな関数("100" → int64)。 一方 convert_dtypes() は NumPy 型を pandas 拡張型に推測 する積極的な関数(int64 + NaN → Int64)。 SSDSE-B-2026 を「素直に読んだ結果」を整える場合は infer_objects()、 「欠損保持の拡張型に統一」したい場合は convert_dtypes() を選びます。
parquet は 列指向+型情報付き のフォーマットで、 dtype が保存・復元されます。 df.to_parquet('SSDSE-B-2026.parquet') で書き出し、 pd.read_parquet で読むと dtype が元のまま戻ります。 CSV と違って read_csv の再推測が走らないため、 一度型最適化したものを parquet で保存しておくとファイルサイズも約 1/3 に圧縮されます。
pandas 2.0 以降の dtype_backend='pyarrow' で読み込むと、 NumPy ではなく Apache Arrow メモリ形式が使われます。 SSDSE-B-2026 でやってみると、 string[pyarrow] や int64[pyarrow] といった dtype になり、 Arrow ⇆ Polars/DuckDB との相互運用が可能になります。 さらに NaN を任意型で保持できるため、 拡張型の上位互換と捉えられます。
| ツール | 整数化 | 浮動小数化 | カテゴリ化 | 日付化 | SSDSE-B-2026 での例 |
|---|---|---|---|---|---|
| pandas | astype('int32') | astype('float32') | astype('category') | pd.to_datetime(s) | df['A1101'].astype('int32') |
| NumPy | arr.astype(np.int32) | arr.astype(np.float32) | — | arr.astype('datetime64[D]') | df['A1101'].values.astype(np.int32) |
| Polars | .cast(pl.Int32) | .cast(pl.Float32) | .cast(pl.Categorical) | .str.strptime(pl.Date) | pl_df.with_columns(pl.col('A1101').cast(pl.Int32)) |
| PyArrow | pc.cast(arr, pa.int32()) | pc.cast(arr, pa.float32()) | arr.dictionary_encode() | pc.cast(arr, pa.timestamp('s')) | pa.array(df['A1101'], pa.int32()) |
| SQL (BigQuery) | CAST(col AS INT64) | CAST(col AS FLOAT64) | —(FACTOR は無い) | CAST(col AS DATE) | CAST(A1101 AS INT64) |
| R (tidyverse) | as.integer() | as.double() | as.factor() | as.Date(s) | df$A1101 |> as.integer() |
方法(Methods)セクション例:
本研究では、 SSDSE-B-2026(n=47 都道府県、 46 指標)を pandas で読み込み、 数値列をint32/float32に縮小型変換、 都道府県名(Prefecture)をcategory型に変換した。 これにより、 メモリ使用量を 17.0 KB から 8.5 KB(50.0% 削減)に圧縮しつつ、 値の精度は実用上影響のない範囲(小数 6 桁以上保持)に保った。 拡張型Int64は欠損値を保持するため、 ターゲット列の欠損補完前の保存形式として採用した。
結果(Results)セクション例:
型最適化後の DataFrame で groupby('Prefecture').sum() を実行したところ、 最適化前と比較して 3 倍の高速化が観測された(0.21 秒 → 0.07 秒、 47,000 行に水増し時)。
「最初に型を整える」ことで、 後段の集計・可視化・モデリングの精度が劇的に向上します。 SSDSE-B-2026 で 6 段階のステップを示します。
1 2 3 4 5 6 7 8 9 10 11 12 | import pandas as pd # SSDSE-B-2026 は CP932 で保存されている # 地域コード Code はゼロ落ちを防ぐため str に明示 df = pd.read_csv( 'data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1], # 2 行目(日本語名)を飛ばしコード名を列名に dtype={'Code': str} ) print('rows, cols =', df.shape) print(df.head(3)) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | summary = pd.DataFrame({ 'dtype': df.dtypes, 'memory_KB': df.memory_usage(deep=True) / 1024, 'unique': df.nunique(), 'na': df.isna().sum() }) print(summary.head(10)) # 出力例: # dtype memory_KB unique na # Code object 3.3 47 0 # Prefecture object 3.3 47 0 # A1101 int64 0.37 47 0 # A1301 int64 0.37 47 0 # A1303 int64 0.37 47 0 # ... |
1 2 3 4 5 6 7 8 9 10 11 | df['Prefecture'] = df['Prefecture'].astype('category') # 整数列を int32 へ int_cols = df.select_dtypes(include='int64').columns df[int_cols] = df[int_cols].astype('int32') # 浮動小数列を float32 へ float_cols = df.select_dtypes(include='float64').columns df[float_cols] = df[float_cols].astype('float32') print('メモリ最適化後:', df.memory_usage(deep=True).sum() / 1024, 'KB') |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | # 高齢化率と少子化率の派生 df['elderly_ratio'] = (df['A1303'] / df['A1101']).astype('float32') df['young_ratio'] = (df['A1301'] / df['A1101']).astype('float32') # 出生率(人口千人あたり) df['birth_rate'] = (df['A4101'] / df['A1101'] * 1000).astype('float32') # dtype 確認 print(df[['elderly_ratio','young_ratio','birth_rate']].dtypes) # 全て float32 # 上位 5 県(高齢化率) top5 = df.nlargest(5, 'elderly_ratio')[['Prefecture','elderly_ratio']] print(top5) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | # 別の地域マスタを結合する想定 master = pd.DataFrame({ # df が 564 行(47 県 × 12 年)だと 47 件の region と長さが合わない 'Prefecture': df['Prefecture'].astype(str).drop_duplicates().tolist(), 'region': ['北海道'] + ['東北']*6 + ['関東']*7 + ['中部']*9 + ['近畿']*7 + ['中国']*5 + ['四国']*4 + ['九州']*8 }) # 結合キーが category と object で異なる → 結合時に object に統一される merged = df.merge(master, on='Prefecture', how='left') print(merged['Prefecture'].dtype) # object(category が剥がれる) # 必要なら結合後に再度 category 化 merged['Prefecture'] = merged['Prefecture'].astype('category') merged['region'] = merged['region'].astype('category') |
1 2 3 4 5 6 7 8 9 | import os os.makedirs('data', exist_ok=True) # 書き出し先を先に作る # parquet は dtype を保持する df.to_parquet('data/processed/SSDSE-B-2026_optimized.parquet') # 復元してもメモリは最適化済み df_back = pd.read_parquet('data/processed/SSDSE-B-2026_optimized.parquet') print(df_back.memory_usage(deep=True).sum() / 1024, 'KB') print(df_back.dtypes.value_counts()) |
ポイント: SSDSE-B-2026 を最初に型最適化+ parquet 保存する習慣をつけると、 同じデータを他の論文・分析で何度も読み込むときに 5–10 倍速くなります。 「型整備フェーズ」を独立した CI/ETL タスクとして切り出すと、 下流の分析者の生産性が劇的に上がります。
SSDSE-B-2026 の代表的な指標について、 値域と推奨 dtype を一覧化しました。 そのまま read_csv(dtype=...) の辞書として使えます。
| 列名 | 内容 | 値域(47県) | 推奨 dtype | 理由 |
|---|---|---|---|---|
| Code | 地域コード | R01000–R47000 | category または string | 先頭の R が消えないように文字列 |
| Prefecture | 都道府県名 | 北海道–沖縄県 | category | ユニーク 47 個、 頻出 |
| A1101 | 総人口 | 54万–1409万 | int32 | 時系列・市町村統合でも 1 億未満 |
| A1301 | 15歳未満人口 | 6.5万–157万 | int32 | 同上 |
| A1302 | 15〜64歳人口 | 29万–937万 | int32 | 同上 |
| A1303 | 65歳以上人口 | 16万–321万 | int32 | 同上 |
| A4101 | 出生数 | 3千–11万 | int32 | 同上 |
| A4200 | 死亡数 | 7千–14万 | int32 | 同上 |
| A5101 | 転入者数(日本人移動者) | 7.6千–43万 | int32 | 整数で十分 |
| A9101 | 婚姻件数 | 1.8千–8.9万 | int32 | 整数で十分 |
| A9201 | 離婚件数 | 763–2.5万 | int32 | 値域は小さいが int32 で統一 |
| A4103 | 合計特殊出生率 | 0.99–1.96 | float32 | 小数 2 桁で十分 |
| B4101 | 年平均気温(℃) | 9.1–24.1 | float32 | 小数 1 桁 |
| B4106 | 降水日数(年間) | 67–194 | int16 | 値域が小さい |
| B4109 | 降水量(年間, mm) | 668–3,664 | float32 | 小数 1 桁 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | SSDSE_DTYPES = { 'Code': 'string', 'Prefecture': 'category', 'A1101': 'int32', # 総人口 'A1301': 'int32', # 15歳未満人口 'A1302': 'int32', # 15〜64歳人口 'A1303': 'int32', # 65歳以上人口 'A4101': 'int32', # 出生数 'A4200': 'int32', # 死亡数 'A5101': 'int32', # 転入者数 'A4103': 'float32', # 合計特殊出生率 'B4101': 'float32', # 年平均気温 'B4106': 'int16', # 降水日数(年間) 'B4109': 'float32', # 降水量(年間) } df = pd.read_csv( 'data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1], dtype=SSDSE_DTYPES ) print(df.memory_usage(deep=True).sum() / 1024, 'KB') # 約 8.5 KB(指定なしの約 17 KB の半分) |
pandas 拡張型(Int64、 Float64、 string、 BooleanDtype、 CategoricalDtype)は、 NumPy 型の弱点である「整数列で NaN を保持できない」「NaN と None が混同される」を解決します。 ただし新概念のため学習曲線があります。 5 段階で習得しましょう。
NumPy 型: int64 (小文字)、 拡張型: Int64 (大文字)。 拡張型は内部で「値の配列」と「欠損マスク配列」を別々に持つため、 整数列に欠損を含められる。 SSDSE-B-2026 で df['A1101'].astype('Int64') と書けば、 NaN を保持したまま「総人口」を整数列にできる。
拡張型でも sum/mean/median は NaN を自動スキップする。 ただし .values で取り出すと NumPy オブジェクト配列になり、 NaN が pd.NA として混じるため、 直接 np.mean に渡すと TypeError になる。 必ず .to_numpy(dtype='float64') で NumPy 型に明示変換してから NumPy 関数に渡す。
拡張型では pd.NA == pd.NA が NA (True ではない!)。 これは SQL の三値論理と同じ。 SSDSE-B-2026 で「人口が NaN の県を選ぶ」 → df['A1101'].isna() を使い、 df['A1101'] == pd.NA は使わない。
parquet は拡張型を保存できる(型情報が永続化される)。 CSV は文字列ベースのため、 拡張型の情報は失われる。 SSDSE-B-2026 を拡張型で扱う場合、 中間保存は parquet/feather に統一すると往復が劣化しない。 pd.read_csv(dtype_backend='numpy_nullable') で読み込めば、 デフォルトで拡張型に変換される(pandas 2.0+)。
pandas 2.0 以降、 dtype_backend='pyarrow' で読み込むと、 内部メモリが Apache Arrow 形式になる。 これは Polars/DuckDB/BigQuery と相互運用可能で、 大規模データでは速度・メモリの双方で有利。 SSDSE-B-2026 を Polars で処理する場合、 pl.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932') でそのまま読める。
Int64 — 整数 + 欠損。 SSDSE-B-2026 の人口列で欠損補完前。Float64 — 浮動小数 + 欠損。 比率列で欠損が出るとき。string — 文字列専用(object より型安全)。 Prefecture を文字列のまま保持したい場合。boolean — 三値論理(True/False/NA)。 「人口 100 万超フラグ」など。category — 有限集合のカテゴリ。 Prefecture や region に。| 年代 | 出来事 | 意味 |
|---|---|---|
| 2006 | NumPy 1.0 リリース | 固定 dtype 配列の標準化。 int64/float64 がデファクトに。 |
| 2008 | pandas 0.1 リリース(Wes McKinney) | NumPy 上に「ラベル付き表」を載せた。 object 型で文字列を許容。 |
| 2014 | pandas 0.15 で Categorical 型導入 | R の factor 相当。 SSDSE のような都道府県データに最適。 |
| 2019 | pandas 0.24 で Nullable Integer (Int64) 導入 | 整数 + 欠損が初めて表現可能に。 |
| 2020 | pandas 1.0 で StringDtype 安定化 | object よりも型安全な文字列型。 |
| 2022 | Apache Arrow バックエンド試験導入 | 列指向・ゼロコピー I/O。 Polars/DuckDB との相互運用。 |
| 2023 | pandas 2.0 で PyArrow dtype が安定 | dtype_backend='pyarrow' で読み込み可能に。 |
| 2024+ | NumPy 2.0、 pandas 2.2 で型システム整理 | 拡張型のデフォルト化、 Arrow との統合が進行中。 |
SSDSE-B-2026 のような公的統計データを扱う際も、 古い記事のサンプルコードは NumPy 型前提で書かれていることが多い。 2024 年以降は拡張型/PyArrow を基本にする方が、 欠損や文字列のハンドリングが安全。
「float32 にすると 0.1 がなぜ正確に表現できないのか」 を理解するには、 IEEE-754 浮動小数のビット表現を見るのが早道です。 SSDSE-B-2026 の数値列で実際にビットを観察してみます。
| 型 | 合計 | 符号 | 指数 | 仮数 | 有効桁数(10進) | 表現可能範囲 |
|---|---|---|---|---|---|---|
| float16 | 16 bit | 1 bit | 5 bit | 10 bit | 約 3 桁 | ±6.5×10⁴ |
| float32 | 32 bit | 1 bit | 8 bit | 23 bit | 約 7 桁 | ±3.4×10³⁸ |
| float64 | 64 bit | 1 bit | 11 bit | 52 bit | 約 15 桁 | ±1.8×10³⁰⁸ |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import numpy as np import pandas as pd df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) elderly = df['A1303'] / df['A1101'] # float64 と float32 で比較 val64 = np.float64(elderly.iloc[0]) # 北海道の高齢化率 val32 = np.float32(elderly.iloc[0]) print(f"float64: {val64:.20f}") print(f"float32: {val32:.20f}") # 出力例(高齢化率 ≈ 0.33): # float64: 0.33012568735271013987 # float32: 0.33012568950653076172 # 差は 約 2.2×10⁻⁹ → 小数 8 桁目以降が違う # ビット表現を覗く print(np.float64(val64).view(np.int64).item()) # 4599618635608094297 |
| 型 | ビット数 | 符号 | 最小値 | 最大値 | SSDSE-B-2026 で安全か |
|---|---|---|---|---|---|
| int8 | 8 | あり | -128 | 127 | NG(人口に使えない) |
| int16 | 16 | あり | -32,768 | 32,767 | 降水日数(B4106)などには OK |
| int32 | 32 | あり | -2,147,483,648 | 2,147,483,647 | 都道府県人口・出生数に十分 |
| int64 | 64 | あり | -9.2×10¹⁸ | 9.2×10¹⁸ | 過剰(メモリ無駄) |
| uint8 | 8 | なし | 0 | 255 | One-Hot Encoding に最適 |
| uint16 | 16 | なし | 0 | 65,535 | カテゴリコード(市町村レベルまで) |
SSDSE-B-2026 のすべての指標は非負(人口、 出生数、 死亡数、 婚姻件数、 すべて 0 以上)。 符号ビットが不要なので uint32 を使えば、 上限が 2 倍 (約 42 億)になる。 ただし pandas は uint64 等を groupby のキーで使うとバグが起きる場合があるため、 集計後の最終形では int32 に統一する習慣が安全。
あるチームが SSDSE-B-2026 の地域コード Code (例: R01100)を「数字 + 文字列のミックス」と判断して regex で数字だけ取り出し int 化した。 結果、 沖縄県(R47100)が 47100 に、 北海道(R01100)が 1100 になり、 ゼロパディングが消失。 後段のマスタ結合で 6 県のレコードが NaN になり、 集計結果が不可解になった。 解決: dtype={'Code': 'string'} で読み込み時に文字列固定する。
メモリ削減のため SSDSE-B-2026 全体を float32 化し、 sklearn の LinearRegression で「高齢化率 ← 転入者数 + 合計特殊出生率」を回帰したところ、 float64 版と係数が小数 5 桁目以降で異なった。 47 行のため実用上の差は無視可能だが、 千行・万行で繰り返し計算すると残差が累積する。 解決: モデリングだけは float64 で行い、 保存時に float32 に落とす運用に変更。
seaborn の scatterplot(hue='Prefecture') で SSDSE-B-2026 の散布図を描いたとき、 Prefecture が object 型のままだと、 凡例が出現順(CSV の上から)になり、 北海道→青森→… の自然順にならない。 category 化+ cat.set_categories(ordered=True) で固定すると、 全てのグラフで凡例順が統一される。 論文の図表で重要。
SSDSE-B-2026 を 2010–2026 年の 17 年分縦に積んだとき、 年度列が "平成 22 年" や "令和 5 年" のように和暦文字列で入っていると、 辞書順ソートで「令和」 → 「平成」 の順になる(漢字コードの問題)。 str.extract(r'(\d+)') + 元号判定 + int 化 で西暦に統一する前処理が必須。
pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) で読み込み、 df.memory_usage(deep=True).sum() で初期メモリを取得せよ。 期待値: 約 17 KB。Prefecture を category 化し、 メモリの減少量を測定せよ。 期待値: 約 3 KB の削減。int64 列を int32 に、 全 float64 列を float32 に変換し、 合計メモリ削減量を出せ。 期待値: 約 50% 削減。A1101 の人口を昇順ソートして上位 5 県を出せ。 さらに A1303 (65歳以上人口)の比率を計算し、 比率順での上位 5 県を出せ。 期待される高齢化率上位: 秋田、 高知、 徳島、 山口、 青森。df.to_parquet('SSDSE-B-2026.parquet') で保存し、 pd.read_parquet で読み戻したとき dtype が保持されていることを確認せよ。 CSV で再保存した場合と比較し、 ファイルサイズの違いを述べよ。採点ポイント: コードの正しさだけでなく、 「なぜその dtype を選んだか」を 1 行コメントで書けているか。 dtype は「値域 ×精度 ×欠損保持 ×演算速度」の四変数で決まることを意識する。
read_csv し、 df.dtypes と df.info(memory_usage='deep') を毎日眺める。 自然な dtype 認識を身につける。astype を使って int64→int32、 object→category の変換を実際に試し、 メモリ削減を体感する。 失敗例(NaN を含む列を int 化してエラー)も意図的に起こす。pd.to_numeric(errors='coerce') と pd.to_datetime(errors='coerce') を使い、 「失敗値を NaN に倒す」柔軟変換の挙動を理解する。 民間データの ETL に必須。Int64、 string、 boolean、 category)を読み書きする。 SSDSE-B-2026 を dtype_backend='numpy_nullable' で読んでみる。到達目標: 「このデータの dtype は何で、 なぜそれが選ばれているか、 もっと最適化できないか」を 30 秒で判断できるようになる。 ここまで来ると、 ETL の品質が劇的に向上します。
| 分野 | 類似概念 | 類似点 / 違い |
|---|---|---|
| SQL/RDB | CAST / CONVERT | 列単位で型変換するのは同じ。 ただし RDB の文字列型は VARCHAR(n) のように長さ制限があり、 pandas の string 型より厳格。 |
| JSON Schema | type validation | 「この列は number 型でなければならない」と宣言してバリデーション。 pandas の dtype 推測 + convert_dtypes に近い。 |
| Protocol Buffers | field types (int32/int64/sint32...) | SSDSE-B-2026 のような表データを proto で表現するなら、 各列に int32 や float を割り当てる。 wire 型と論理型の使い分けが、 pandas の NumPy 型と拡張型の関係に近い。 |
| 機械学習(埋め込み) | 量子化 (quantization) | float32 を int8 に変換して保存。 同じ「精度を捨ててメモリ削減」の発想。 SSDSE-B-2026 の比率列を float32→int16(×10000 倍してから)で扱うのも一種の量子化。 |
| ハードウェア | data path width (16/32/64 bit) | CPU/GPU は dtype 幅でレジスタを使い分ける。 float32 が float64 より速いのはこのため。 |
df.dtypes を必ず確認したか?Int64/Float64/string を選択したか?int8/int16/int32/int64 を適切に選んだか?category 化したか? ユニーク数 / 全行数 < 0.5 なら category 推奨。datetime64[ns] 化したか? object のまま比較していないか?read_csv(dtype={...}) を指定したか?.values / .to_numpy() で NumPy 配列に出した瞬間、 拡張型が NumPy 型に戻ることを意識したか?astype 厳密、 探索では to_numeric(errors='coerce') 寛容、 と方針を分けたか?型変換をひとことで言えば、 同じ中身を「どう読むかの約束」ごと載せ替える操作です。 紙に書かれた 01100 は、 「数字として読め」と言われれば 1100、 「郵便番号のラベルとして読め」と言われれば "01100"。 どちらも同じインクの並びなのに、 約束が違えば別の値になります。 計算機の中でも同じで、 文字列 ↔ 数値 ↔ 日付 ↔ カテゴリの間を移るたびに「読み方の契約」を書き換えているのです。
暗黙変換(implicit)と明示変換(explicit)の違いも直感で押さえます。 暗黙は「言語が気をきかせて勝手にそろえる」もの(5 + 3.0 → 8.0、 True + 1 → 2)。 明示は「開発者が int() / float() / astype() で指示する」もの。 暗黙は書く量が減って楽な一方、 「いつのまにか型が変わっていた」事故の温床です。 分析前の前処理では、 なるべく明示変換で「この列はこの型」と宣言してしまうのが安全です。
CSV は「全部ただの文字」から始まるのも重要な直感です。 テキストファイルの CSV には型の情報が書かれていません。 read_csv は各列を眺めて「数字っぽい → int/float」「それ以外 → object(文字列)」と推測しているだけ。 推測が外れれば、 数値のはずの列が文字列のまま居座ります。 だからこそ「読み込んだ直後に df.dtypes を見る」のが型変換の出発点になります。
pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) の直後に df.shape と df.dtypes.value_counts() を確認した実測結果です(数値はすべて実データから取得、 架空値なし)。
つまり 112 列のうち 数値と推測された列が 110、 文字列(object)は 2 列だけ。 その object 2 列の正体を実測すると次のとおりです。
| 列 | 実測 dtype | 実測サンプル値 | 読み方 |
|---|---|---|---|
SSDSE-B-2026(先頭列) | int64 | 2023, 2022, 2021 … | 実は「年」。 時系列軸なら to_datetime(format='%Y') の候補 |
Code | object(文字列) | "R01000" … "R47000" | 先頭に R があるためそのままでは数値化されず、 ゼロ落ちも起きていない |
Prefecture | object(文字列) | "北海道" … | category 化の第一候補 |
📝 補足(より正確な観測): 本ページ前半には「Code は int64」「n=47」とする記述もありますが、 上の実測どおり配布 CSV の Code 列は R 始まりの文字列(object)で、 行数は 564(47県 × 12 年)です。 R という接頭辞があるおかげで pandas が誤って整数化せず、 結果的にゼロ落ち事故が回避されている、 という「型推論に助けられている」実例と読めます。 一方、 もし Code が 01100 のような純数字だったら整数化でゼロが落ちるため、 dtype={'Code':'string'} の明示が必要になります。
型変換の怖さは、 多くの失敗が例外ではなく「静かな誤り」として通り抜ける点にあります。 エラーで止まってくれれば気づけますが、 値がこっそり NaN になったり丸められたりしても処理は続行してしまう。 以下、 分析前処理で踏みやすい落とし穴を、 SSDSE-B-2026 の実測を交えて整理します。
カンマ入りの "1,000" や全角の "123" は、 見た目は数値でも to_numeric でそのまま NaN になります。 サンプル列 ['1.20','3','-','N/A','5,000','42'] を pd.to_numeric(errors='coerce') にかけた実測結果:
'-' と 'N/A'(欠損標識)だけでなく、 '5,000' もカンマのせいで NaN に落ちている点に注目してください。 これを見逃すと「5000 という立派な値が黙って欠損化」します。 対策は変換前に s.str.replace(',', '', regex=False)、 全角は s.str.translate(str.maketrans('0123456789','0123456789'))(または unicodedata.normalize('NFKC', ...))で正規化してから数値化。 そして「変換で新たに増えた NaN 件数」を pd.to_numeric(s, errors='coerce').isna().sum() - s.isna().sum() で必ず監視します。
03/04/2023 は 3月4日か 4月3日か、 国によって解釈が割れます。 pd.to_datetime に format= を渡さないと、 pandas が推論で決めてしまい、 行によって月と日が入れ替わる最悪のバグを生みます。 鉄則は format='%Y-%m-%d' の明示。 SSDSE-B-2026 の年は先頭列に 2023 のような 4 桁整数で入っているので、 時系列にするなら pd.to_datetime(df['SSDSE-B-2026'], format='%Y') と桁を指定するのが安全です。
float32 の有効桁は約 6 桁、 float64 は約 15 桁(実測 np.finfo の precision)。 円単位の金額や累積値を float32 にすると下位桁が丸まります。 また NumPy の int64 は NaN を表現できないため、 整数列に欠損が 1 個混じるだけで dtype が自動的に float64 へ格上げされます(「読み込んだら数値列が float だった」の正体)。 欠損を保持したまま整数で扱いたいなら pandas 拡張型 'Int64'(先頭大文字)を使います。
県コード "01100" を int にすると 1100 になり、 キーとして壊れます(前ゼロ消失)。 また文字列を astype('category') しただけでは順序情報を持たないため、 「小・中・大」のような順序尺度がアルファベット/出現順に並んでしまいます。 順序を保つには pd.Categorical(s, categories=['小','中','大'], ordered=True) のように明示します。
int(3.7) は四捨五入ではなく切り捨てで 3(ゼロ方向への切り捨て)。 float('0.1') は 2 進で厳密に表せず、 0.1 + 0.2 == 0.3 が False になります。 暗黙変換では True + 1 → 2(bool が int 扱い)や、 JavaScript の "1" == 1 → true のような言語差にも注意。 「そろえてくれて助かる」場面と「気づかず壊れる」場面は紙一重です。
これは型変換の前段ですが、 型の話と地続きです。 SSDSE-B-2026 は日本語列(Prefecture など)を含み、 encoding='cp932'(Windows 系)で配布されています。 UTF-8 のつもりで読むと UnicodeDecodeError か文字化けが起き、 その後の str.replace や日付パースが総崩れになります。 「まず正しいエンコーディングで文字列を得て、 それから型変換」という順序を守ってください。
| 落とし穴 | 起きること | 検知 | 対処 |
|---|---|---|---|
| カンマ/全角数字 | 値が静かに NaN | NaN 増分の件数 | 正規化してから数値化 |
| 日付フォーマット誤認 | 月日入替え | 最小/最大日の目視 | format= 明示 |
| float 精度 | 下位桁の丸め | 前後ヒストグラム | float64 保持 |
| 欠損での格上げ | int → float64 | dtypes 確認 | 拡張型 Int64 |
| 前ゼロ/順序喪失 | キー破壊/並び崩れ | ユニーク値照合 | 文字列保持/ordered=True |
| エンコーディング | 文字化け/例外 | 先頭数行の目視 | cp932 指定 |
object は「Python オブジェクトへのポインタの列」で、 文字列や混在を何でも受けますが、 実体がヒープに散らばるためメモリ計測は memory_usage(deep=True) でないと過小評価になります。 category は「一意値の辞書 + 整数コード」で、 繰り返しの多い文字列列に劇的に効きます。 datetime64[ns] は内部が 64bit 整数(ナノ秒)で、 .dt.year などの時間アクセサが使えるのが object 文字列との決定的な違いです。
astype(...): 厳密変換。 失敗したら例外。 本番/リリースで「想定外を弾く」用途。pd.to_numeric(s, errors='raise'/'coerce'): 'coerce' は失敗を NaN に倒す寛容変換。 探索(EDA)で「汚くても前進」する用途。 downcast='integer'/'float' で最小型へ縮小も同時にできる。pd.to_datetime(s, format=..., errors=...): 日付専用。 format 明示で高速かつ誤認防止。SSDSE-B-2026(564 行 × 112 列)で、 memory_usage(deep=True) を使った実測メモリです(架空値なし)。
| 対象 | 変換前 | 変換後 | 削減率(実測) |
|---|---|---|---|
Prefecture 列(object → category) | 40,812 bytes | 5,637 bytes | 約 86% |
A1101 列(int64 → downcast='integer' で int32) | 4,512 bytes | 2,256 bytes | 50% |
| DataFrame 全体(deep) | 568,152 bytes | 258,018 bytes | 約 45%(=54.6% 減) |
全体の最適化は「int64 → int32」「float64 → float32」「object の Prefecture/Code → category」を一括適用した結果です。 安全性の裏づけ: 全 int64 列の実測最大値は 80,273,650 で、 int32 上限 2,147,483,647 に十分収まるためオーバーフローしません(一方 int16 上限は 32,767 なので人口列には不足)。
拡張型 Int64 / Float64 / boolean / string(頭大文字)は欠損マスクを別に持つため、 NaN を保持したまま整数扱いができます。 ダウンキャストは pd.to_numeric(s, downcast='integer') のように、 値域に応じて自動で最小型へ縮めます。 スキーマ検証は型推論の限界(CSV は毎回推測がブレる)への保険で、 読込時に read_csv(dtype={'Code':'string','A1101':'int32'}) と宣言して推論そのものを止めるのが第一歩。 さらに pandera / pydantic で「この列は int32・非負・欠損なし」を宣言的に検査すれば、 型崩れを早期に捕捉できます。 型情報を保持する parquet で中間保存すれば、 CSV の再推測を毎回やり直す無駄も消えます。
型変換は前処理の中核なので、 隣接概念とセットで理解すると効きます。 用語集内の関連ページへのリンクです。
errors='coerce' や拡張型 Int64 と直結。ordered=True による順序保持の背景。datetime64 変換後の分析。※ 日付専用ページ・エンコーディング専用ページ・dtype 専用ページは用語集に独立項目としては未収録のため、 本ページ内の該当節(落とし穴②⑥、 発展の datetime)を参照してください。
型変換は「データクレンジング」の中核であり、 全ての下流処理(集計・モデリング・可視化)の入口に立つ。 ここでミスると、 後段の見かけ上は動くが結果が嘘になる事故が多発する。
データ取得 (read_csv / API)
│
▼
【データクレンジング】
├─ 型変換 ★ 本ページ
│ ├─ 数値化 (to_numeric)
│ ├─ 日付化 (to_datetime)
│ ├─ カテゴリ化 (astype('category'))
│ └─ 拡張型 (Int64/Float64/string)
├─ 欠損値処理 (fillna/dropna)
├─ 外れ値検出
└─ 正規表現抽出
│
▼
【特徴量エンジニアリング】
├─ 標準化 (z-score)
├─ 対数変換
├─ One-Hot Encoding
└─ 派生列の作成
│
▼
【モデリング / 可視化】
├─ 回帰・分類
├─ クラスタリング
└─ 散布図・ヒストグラム
型変換 (type conversion) は一見地味ですが、 メモリ・速度・精度を支配する核心技術です。 SSDSE-B-2026 を扱う 47 都道府県の ETL 処理を例に、 実務での「効いた」事例 6 件をまとめます。
| 業界 | 問題 | 型変換で解決 |
|---|---|---|
| 公的統計(SSDSE-B-2026) | 「Code 01」が int で「1」に化け、 北海道 (01) と青森 (02) を取り違え | dtype={'Code': str} で読込 |
| 金融・市場データ | 分単位ティックデータが int64 で 100GB | float32 + category 化で 25GB に圧縮 |
| 小売・売上分析 | 日付列が文字列で年月ソートできない | pd.to_datetime で datetime64 化 |
| 機械学習 | object 列が紛れて XGBoost が学習不可 | category 化 + Label Encoding |
| IoT・センサ | 温度値の int 表現で 0.1 度刻みが死亡 | float32 + スケーリング |
| 医療・臨床研究 | 「-」を含む文字列列を直接 int 化して例外 | pd.to_numeric(errors='coerce') |
| API | 向くケース | 失敗時挙動 | SSDSE-B 例 |
|---|---|---|---|
astype | 綺麗なデータの厳密変換 | 例外を投げる(ValueError) | A1101 を int64 → int32 へ圧縮 |
pd.to_numeric | 汚れた文字列を数値化 | errors='coerce' で NaN 化 | 「-」入り列を float に変換 |
pd.to_datetime | 日付文字列 → datetime64 | NaT 化 | 「2026/03/01」を datetime64 化 |
category | 繰り返し文字列カラム | 未知カテゴリは NaN | Prefecture 列を category 化 |
convert_dtypes | NaN 含む列に Nullable 型を割当 | Int64, Float64, boolean | 欠損混入列で安全に演算 |
pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1], dtype={'Code': str}) で読み込み、 全列の dtype を df.info(memory_usage='deep') で確認。 メモリ使用量を MB 単位で報告。int64 → int32 に astype でダウンキャスト。 メモリ使用量がどれだけ減ったか測定せよ。 値が一致することも確認。category 型に変換し、 メモリ使用量と groupby 速度を比較せよ(before/after で %timeit)。astype(int) と pd.to_numeric(errors='coerce') の挙動の違いを観察。 後者で NaN 件数を集計せよ。float64 → float32 にした際の数値誤差を、 標準化変換 (z-score) 適用後で測定。 平均と分散の誤差を 1e-6 以下に抑えられるか確認せよ。read_csv で日付列を datetime64 化するオプション。型変換 (type conversion / casting) はデータ前処理の基礎で、 読込から特徴量エンジニアリングまで全段階に登場する。
pd.read_csv で SSDSE-B-2026 を読込 → 自動推定された型 (object / int64 / float64) を df.dtypes で確認 → 必要に応じ変換pd.to_numeric で errors='coerce')SSDSE-B-2026 で「総人口」列が object として読み込まれる場合 (カンマ区切り "1,234,567")、 df['総人口'] = df['総人口'].str.replace(',', '').astype(int) で int に変換しないと集計関数が動かない。
型変換は、 元の型と目的の型で 4 通りに分岐する。
pd.to_numeric(s, errors='coerce')。 失敗値は NaN に。 "1,234" のカンマは事前除去pd.to_datetime(s, format='%Y-%m-%d')。 SSDSE-B-2026 の「年月」列は %Y%mdf['都道府県'].astype('category')。 メモリ削減 + 集計高速化int64 → int32 / int16、 float64 → float32。 SSDSE-B-2026 で半分以下にSSDSE-B-2026 を pd.read_csv(dtype={'都道府県': 'category', '総人口': 'int32'}) のように読込時に指定すると、 後段の型変換 step を省略可能。 47 県 × 12 年 × 50 列ならメモリ差は小さいが、 大規模化時に効く。