数値・カテゴリ・日時・欠損|表・単位・割合・グラフ

このページはプロモーション(広告)を含みます。

表・単位・割合・グラフ(初級・読む22分+演習28分)

この授業のゴール

仕事でどう使うか

列の種類を取り違えると、計算が実行できても意味のない数字が出ます。会員番号の平均、欠損を0とした平均、文字列として並んだ日付の並べ替え、どれも画面にはもっともらしい結果が出ます。

路線 この授業の使いどころ
DS(データサイエンティスト) 分析の最初に各列の型と欠損数を一覧にする(データ点検)。欠損を除外するか補完するかを、報告書に書く
ML(機械学習エンジニア) 特徴量(予測に使う入力の列)を、数値・カテゴリ・日時に分けて前処理する。会員番号を数値のまま入れると、大小関係に意味があるとモデルが誤解する
DX(DX推進・AI企画) 業務システムの項目定義を確認する。「未入力」と「0件」と「該当なし」を別々の値として設計する
RS(研究者) 調査の無回答と、回答が0だったことを区別して集計する。測定日時を正しい形式で記録する

この授業は、数字に見える値をそのまま計算に使ってよいかを判断する練習です。

前提

この授業は、表の1行が何を表すかを前提にします。まだなら先に確認してください。

四則計算ができれば足ります。画面内のPythonは読むだけでも追えます。

身近な状況:アンケートの平均がおかしい

ある学習サービスが、会員5人に「昨日の学習時間(時間)」を尋ねました。回収した表は次のとおりです。

member_id plan signup_date study_hours
00123 ライト 2025-01-20 2
00045 スタンダード 2025-02-03 0
00311 ライト 2025-02-10 (空欄)
00078 プレミアム 2025-02-28 3
00199 スタンダード 2025-03-05 5

担当者は「平均学習時間は?」と聞かれ、空欄を0として 10 ÷ 5 = 2.0時間 と答えました。ところが、空欄の人は「答えなかった」のであって、「0時間勉強した」わけではありません。回答のあった4人で計算すると 10 ÷ 4 = 2.5時間です。

同じ表で、00045さんは0と回答しています。この0は本物の値で、平均に入れます。空欄の人と0と答えた人を同じに扱うと、結果が変わります。

この表には、扱いの違う4種類の列があります。

直感的な説明:列は「何ができるか」で分ける

列を、「その列にどんな計算をしてよいか」で分けます。

数字で書かれているかどうかではなく、その値に足し算の意味があるかで判断します。会員番号 00123 と 00045 を足しても、「00168番の会員」は存在しません。一方、学習時間 2時間と3時間を足せば、合計5時間という意味があります。

簡単な判定の質問が3つあります。

  1. 2つの値を足して意味が通るか。通れば数値の候補。
  2. 大きいほど「多い」と言えるか。言えなければカテゴリ。
  3. 時点を表すか。表すなら日時。

正確な定義:型の分類と欠損の扱い

用語を正確に定めます。

数値(quantitative)は2つに分かれます。

カテゴリ(categorical)も2つです。

日時(datetime)は、年月日や時刻を表します。表示の形が複数あり、03/04/2025 は3月4日か4月3日かが国によって違います。本講座では 2025-03-04(年-月-日)の形を使います。

欠損(missing value)は、値が記録されていないことです。システムによって呼び名が異なります。Pythonのpandasでは NaN(Not a Number)、データベースでは NULL、表計算では空欄です。

欠損と0の違いを表にします。

状態 意味 件数に数えるか 平均に入れるか
0 「0」と観測された 数える 入れる
欠損 値が得られていない 数えない(欠損数として別に数える) 入れない(分母から外す)

平均は、欠損を除いた有効な値の個数 $n$ を使います。

$$\bar{x} = \frac{1}{n}\sum_{i=1}^{n} x_i$$

ここで $x_i$ は $i$ 番目の有効な値(時間)、$n$ は欠損でない値の個数、$\bar{x}$ は平均(時間)です。冒頭の例では $x$ は 2, 0, 3, 5 で、$n=4$ なので $\bar{x} = (2+0+3+5)/4 = 2.5$ 時間です。

欠損の割合(欠損率)は、欠損の個数を全体の個数で割ります。冒頭の例では $1 \div 5 = 20\%$ です。

段階付き例題:欠損を含む平均を2通りで比べる

問題:study_hours が 2, 0, (欠損), 3, 5 のとき、平均を求める。

ステップ1:列の種類を決める。時間は量なので数値。足し算に意味がある。

ステップ2:欠損と0を数える。値が0の人は1人(本物の値)。欠損は1人。

ステップ3:有効な値の個数と合計を出す。有効な値は 2, 0, 3, 5 の4個で、合計は10。

ステップ4:平均を計算する。10 ÷ 4 = 2.5時間。

ステップ5:誤った方法と比べる。欠損を0として分母に入れると、10 ÷ 5 = 2.0時間。0.5時間(25%)低く見えます。

画面で確かめます。pd.Series は1列ぶんの値の並び(シリーズ)を作る関数です。None を入れると欠損の NaN になり、全体が小数(float)型になります。s.mean() は欠損を自動で除いて平均を返します。s.count() は欠損でない個数を整数で返し、s.isna().sum() は欠損の個数を返します。s.fillna(0) は欠損を0に置き換えた新しいシリーズを返します。

import pandas as pd
s = pd.Series([2, 0, None, 3, 5])
print('有効な個数', s.count())
print('欠損の個数', s.isna().sum())
print('平均(欠損を除く)', s.mean())
print('平均(欠損を0にした)', s.fillna(0).mean())
print('欠損率', s.isna().mean())

ステップ6:結果を読む。有効な個数4、欠損1、平均2.5と2.0、欠損率0.2(20%)です。どちらを使うかは、欠損の意味で決めます。「答えなかった」なら除外、「該当する行動がなく0にした」と業務ルールで決まっているなら0とします。いずれにしても、報告書に欠損の人数と扱い方を書いてください。

操作して理解する:型を確認して変換する

CSVから読み込んだ列は、文字列(object 型)になっていることがあります。「1,200」のようにカンマが入った金額や、2025-03-01 のような日付は、変換しないと計算できません。

次のコードで、型を確認して変換します。df.dtypes は各列の型を表示します。str.replace(',', '') は文字列からカンマを取り除き、astype(int) は整数型に変換します。pd.to_datetime は日付の文字列を日時型に変換します。日時どうしの引き算は日数の差(Timedelta)になり、.days で整数の日数を取り出せます。

import pandas as pd
df = pd.DataFrame({
    'member_id': ['00123', '00045'],
    'amount': ['1,200', '800'],
    'date': ['2025-03-01', '2025-03-15'],
})
print(df.dtypes)
df['amount_num'] = df['amount'].str.replace(',', '').astype(int)
df['date_dt'] = pd.to_datetime(df['date'])
print('金額の合計', df['amount_num'].sum())
print('日付の差(日)', (df['date_dt'].iloc[1] - df['date_dt'].iloc[0]).days)

出力は、3列とも object と表示され、金額の合計は 2000、日付の差は 14 です。

次の点を試してください。

発展:欠損の理由と「特別な値」による隠れた欠損

欠損は、なぜ欠けたかで扱いが変わります。

また、入力システムによっては、欠損を 9999 や -1、「不明」「なし」「N/A」 のような特別な値で入れていることがあります。この値を数値として平均すると、結果が大きく崩れます。たとえば年齢の列に 9999 が1つ入っていると、平均年齢が数百歳になります。列の最小値・最大値・種類を最初に見て、ありえない値や、同じ値の不自然な集中を探します。

欠損への対応は、(1) 欠損のある行を除く、(2) 平均や中央値で埋める(補完)、(3) 「欠損」というカテゴリを作る、のように複数あります。どれが妥当かは目的と欠損の理由しだいです。補完は便利ですが、実際には観測していない値を作る行為なので、結果の解釈には注意が必要です。詳しい手法はデータ前処理の授業で扱います。この授業では、まず欠損の数を数え、0と混ぜないことを徹底してください。

よくある失敗

失敗1:欠損を0として平均する

誤った例:空欄をそのまま0にして、全員で割る。

import pandas as pd
s = pd.Series([2, 0, None, 3, 5])
print('誤:欠損を0にして平均', s.fillna(0).mean())
print('正:欠損を除いて平均', s.mean())

誤りは2.0、正しくは2.5です。欠損は「測っていない」であり、「0だった」ではありません。分母が5になり、平均が実際より低く出ています。直し方:欠損の個数を先に数え、除外か補完かを決めて報告に書く。0が業務上の正しい値の場合だけ0を入れる。

失敗2:会員番号を数値として扱う

誤った例:会員番号が数字なので、整数に変換して保存した。

member_id = '00123'
as_int = int(member_id)
print('整数に変換', as_int)
print('元に戻す', str(as_int))
print('元の番号と同じか', str(as_int) == member_id)

先頭の0が消えて '123' になり、元の番号 '00123' とは別の値になります。別の会員 '123' が存在すれば、他人のデータと結合してしまう危険があります。番号の合計や平均も無意味です。直し方:番号、郵便番号、電話番号は文字列(カテゴリ)のまま読み込み、保存する。

失敗3:日付を文字列のまま並べ替える

誤った例:月を「2025-9」「2025-10」のように0なしで書いた文字列を、そのまま並べ替える。

months = ['2025-9', '2025-10', '2025-11']
print('誤:文字列として並べ替え', sorted(months))
fixed = ['2025-09', '2025-10', '2025-11']
print('正:0を付けた形式で並べ替え', sorted(fixed))

文字列の並べ替えは、左から1文字ずつ比べます。'2025-1...' の '1' は '2025-9' の '9' より小さいため、10月と11月が9月より前に来ます。時系列のグラフが崩れ、増減の判断を誤ります。直し方:2025-09 のように桁を揃えるか、日時型に変換してから並べ替える。

まとめ

用語

欠損(missing value)

値が記録されていない状態。0とは別で、pandasではNaN、データベースではNULLと表す。

カテゴリ(質的変数)(categorical variable)

種類やラベルを表す列。数えることはできるが、足し算や平均に意味がない。

名義・順序(nominal / ordinal)

名義は順序のない分類、順序は順序のある分類。順序カテゴリは間隔が等しいとは限らない。

日時型(datetime)

年月日や時刻を表す型。引き算で日数などの差を求められる。

よくある質問

なぜ列の型を分ける必要があるのですか。

型によって、意味のある計算が違うからです。数値は平均できますが、カテゴリは数えるだけです。型を誤ると、計算は動いても意味のない結果が出ます。

0と欠損は、実務でどう見分けますか。

データ定義書で、空欄・0・「不明」の意味を確認します。わからなければ、入力した担当者に聞くのが確実です。分析では、欠損の数を最初に数えて報告します。

欠損が多い列は、すぐ捨ててよいですか。

すぐには捨てません。欠損の理由が目的に関わる場合があります。たとえば未購入の人の購入日が欠損なのは、未購入という重要な情報です。欠損率が極端に高いときは、列を使うかを目的ごとに判断します。

順序カテゴリ(低・中・高)を1・2・3にして平均してもよいですか。

実務では平均が使われることもありますが、間隔が等しい前提が必要です。迷ったら、各区分の人数と割合を併記します。厳密な分析では順序を保つ専用の手法を使います。

日付の形式が混在しているときはどうしますか。

まず形式のパターンを数えます。2025-03-04 と 03/04/2025 が混ざっているなら、元のデータの仕様を確認してから、1つの形式に統一します。推測で変換すると、月と日の取り違えが起きます。

条件が変わって、欠損を0にしてよいのはどんなときですか。

業務ルールで「記録がない=0件」と決まっているときです。たとえば「クーポン利用回数」で、未利用の人が空欄になるシステムなら、0に直してよい場合があります。その判断と理由を、定義書に残します。

確認問題

問1 会員番号 '00045' を整数に変換して保存した場合に起こる問題はどれか。

  1. ① 問題は起きない
  2. ② 先頭の0が消えて別の番号と区別できなくなる
  3. ③ 番号が自動で2倍になる
  4. ④ 日付に変換される
解答と解説

正解:②

整数にすると '00045' は 45 になり、先頭の0が失われます。別の会員 '45' と混同する危険があります。

問2 アンケートで、ある設問が空欄の回答者の学習時間を0時間として平均を計算した。どの説明が最も適切か。

  1. ① 問題ない。空欄は0と同じ
  2. ② 欠損と0を混同しており、平均が低く偏る可能性がある
  3. ③ 平均が高く偏る
  4. ④ 平均はどの扱いでも変わらない
解答と解説

正解:②

空欄は「回答がない」で、0時間の回答とは別です。0を入れて分母に加えると、平均が下がります。

問3 次のうち、数値として扱うのが最も適切な列はどれか。

  1. ① 郵便番号
  2. ② 会員ランクの「A・B・C」
  3. ③ 電話番号
  4. ④ 1回の購入金額(円)
解答と解説

正解:④

金額は足したり平均したりして意味があります。郵便番号・電話番号・ランクはラベルです。

問4 年齢の列に 9999 という値が1つ入っていた。これは何を疑うべきか。

  1. ① 本当の年齢である
  2. ② 欠損を特別な値で表している可能性がある
  3. ③ 単なる数値なので外れ値でもない
  4. ④ 平均の計算に影響しない
解答と解説

正解:②

システムによっては、不明を 9999 などで入力します。そのまま平均すると結果が大きく崩れます。

問5 2025年3月1日から3月15日までの日数の差(15日−1日)は何日か。

  1. ① 14日
  2. ② 13日
  3. ③ 15日
  4. ④ 16日
解答と解説

正解:①

引き算で求める差は 15−1=14日です。両端を含めて数えると15日間ですが、差とは数え方が異なります。

問6 満足度を「1=不満、2=普通、3=満足」とコード化した列の平均が2.4だった。解釈として最も適切なのはどれか。

  1. ① 平均2.4は「普通と満足の間の連続した量」として厳密に比べられる
  2. ② コード化した列は数値なので何を計算してもよい
  3. ③ 順序のあるカテゴリなので、平均より各コードの人数や割合を見る方が安全
  4. ④ 2.4は必ず「普通」の人が最多という意味
解答と解説

正解:③

順序カテゴリは間隔が等しいとは限りません。「普通」と「満足」の差が「不満」と「普通」の差と同じとは言えず、平均の意味は曖昧です。各選択肢の人数や割合を見ます。

次に学ぶこと

次は 単位と桁と丸め で、単位・桁・丸めの扱いを学びます。この授業で確認した型の区別は、割合の分母(比率・割合・パーセントポイント)や平均(平均・合計・中央値を手計算)でも使います。

この授業の確認問題だけで習得とはしません。ON04 の総合テストと自力課題で判定します。

キャリア学習:データサイエンティスト・機械学習エンジニア・DX推進・研究者を目指す人の学習アプリ。164モジュール・984授業を、ブラウザで動くSQLとPythonの演習で。基礎と各分野の最初のモジュールは無料。全部は2,980円の買い切り。

アプリを無料で開く有料版と無料版の違い