Social Data Science Courseware

4. Pandas と DataFrame 入門

学習目標

  • pandas をインポートし、SeriesDataFrame を生成できる
  • shape / columns / dtypes / info() / describe() で DataFrame の全体像を把握できる
  • df["列名"] で列を、df.iloc[i] / df.loc[label] で行を、df.loc[行, 列] でセルを取り出せる
  • Series と DataFrame の違いを 1 行で説明できる

本文

Pandas を使う理由

Python だけでも CSV ファイルを 1 行ずつ読み込んで処理することはできます。ただし「表形式のデータ」を扱うとなると、行と列の構造を自分で管理しないといけないので、すぐに煩雑になります。

pandas は「表をオブジェクトとして扱う」ためのライブラリで、Python のデータ分析の事実上の標準になっています。CSV を 1 行で読み込み、列を指定して集計し、結果を CSV に書き戻す、という一連の操作がすべてこのライブラリ内で完結します。本コースで扱う集計・前処理・可視化はすべて pandas を中心に進めます。

インポート

Colab には pandas が最初からインストールされているので、import するだけで使えます。

import pandas as pd

pd という短い別名を付けるのが慣例で、世界中のサンプルコードがこの書き方をしています。本コースでも以後 pd で統一します。

Series — 1 列ぶんの「ラベル付き配列」

pandas の最も基本的なデータ構造が Series(シリーズ) です。Python の標準のリストに似ていますが、各要素に インデックス(ラベル) がついている点が違います。

s = pd.Series([10, 20, 30], index=["a", "b", "c"])
s

実行すると次のように表示されます:

a    10
b    20
c    30
dtype: int64

左側の a, b, c がインデックス、右側の 10, 20, 30 が値です。値を取り出すときはラベルでアクセスできます:

s["a"]   # → 10

整数の位置でもアクセスできますが(s[0])、ラベルを付けられるのが Series の利点です。後で DataFrame を扱うとき、この「ラベル」が列名として活きてきます。

DataFrame — 表の構造

実際の業務や研究では 1 列だけのデータより、Excel のシートのような を扱う場面の方が圧倒的に多くなります。表は DataFrame(データフレーム) として扱います。

DataFrame は 行(row)と列(column)から構成される 2 次元の表形式 で、各行には 0 から始まるインデックス番号が自動的に割り当てられ、各列にはカラム名が付いています。

DataFrame の構造(行・列・インデックス)

主な構成要素:

  • 列(Column) — 縦方向のデータのまとまり。各列には名前(カラム名)が付いている
  • 行(Row) — 横方向のデータのまとまり。各行に 1 つのデータレコードが含まれる
  • インデックス(Index) — 各行を識別するための重複しない値(通常は 0 から始まる連番)

DataFrame を作る

DataFrame は「複数の Series が縦に並んだもの」と考えると分かりやすいです。次は辞書から DataFrame を作る例:

df = pd.DataFrame({
    "name": ["太郎", "花子", "一郎"],
    "age":  [25, 30, 22],
    "city": ["東京", "大阪", "福岡"],
})
df

出力:

  name  age city
0   太郎   25   東京
1   花子   30   大阪
2   一郎   22   福岡

辞書のキー(name, age, city)が 列名 になり、リストの中身が各列の値として並びます。左端の 0, 1, 2行のインデックス(デフォルトでは整数の連番)。

実データは普通、CSV から読み込みます(前単元):

df = pd.read_csv("/content/drive/MyDrive/datasets/users.csv")
df.head()

df.head() は先頭 5 行を表示する関数。読み込んだ直後にまず眺めて、データの形と中身をイメージするのが定石です。

df.head() で先頭 5 行を表示した例

全体像をつかむ 6 つの操作

新しいデータを開いたら、いきなり中身を加工する前に まず全体像を眺める のが定石です。次の 6 つを順番に呼ぶだけで、データの規模・列構成・型・分布の感触がつかめます。

メソッド/属性内容
df.shape(行数, 列数) のタプル
df.columns列名の一覧
df.dtypes各列のデータ型(数値か文字列か)
df.info()列数・行数・型・欠損数をまとめて表示
df.head()先頭 5 行(引数で件数指定可:df.head(10)
df.describe()数値列の要約統計(平均・標準偏差・最小・最大ほか)

実行例:

print(df.shape)       # (6, 6)
print(df.columns)     # Index(['名前', '年齢', '都市', '性別', 'カテゴリ', '備考'], dtype='object')
print(df.dtypes)
df.info()
df.head()
df.describe()

ここで気を付けたいのが、属性とメソッドの区別 です。shapecolumnsdtypes は属性なので () を付けず、head()info()describe() はメソッドなので () を付けて呼びます。最初は紛らわしいですが、エラーメッセージで「shape() takes 0 positional arguments...」のような表示が出たら属性とメソッドを間違えている合図です。

df.info() の読み方

df.info() を呼ぶと、次のような表が出ます:

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 6 entries, 0 to 5
Data columns (total 6 columns):
 #   Column  Non-Null Count  Dtype
---  ------  --------------  -----
 0   名前      6 non-null      object
 1   年齢      6 non-null      int64
 2   都市      6 non-null      object
 3   性別      6 non-null      object
 4   カテゴリ    6 non-null      object
 5   備考      6 non-null      object
dtypes: int64(1), object(5)
memory usage: 420.0+ bytes

主な読み方:

  • RangeIndex: 6 entries, 0 to 5 — 行数は 6、インデックスは 0 から 5 まで
  • Data columns (total 6 columns) — 列数は 6
  • Non-Null Count — 各列の 欠損ではない値の数。たとえば「年齢」列が 5 non-null なら、6 行中 1 行が欠損
  • Dtype — データ型。int64 は整数、object は文字列など
  • dtypes: int64(1), object(5) — 全列の型の内訳

「欠損があるか」「数値列はどれか」を一目で確認できるため、データを開いた直後に必ず呼ぶ習慣を付けたい関数です。

df.describe() の読み方

df.describe()数値列 に対して、代表的な要約統計を一覧で返します:

        年齢
count   6.000000
mean   30.000000
std     6.603030
min    22.000000
25%    25.750000
50%    29.000000
75%    33.750000
max    40.000000
出力意味
count有効なデータの個数(欠損を除く)
mean平均値
std標準偏差(ばらつきの指標)
min最小値
25%第 1 四分位数(下から 25% の位置)
50%中央値(50% の位置)
75%第 3 四分位数(下から 75% の位置)
max最大値

文字列の列は集計対象から外れ、数値列だけが表示されます。

列にアクセスする

特定の列だけを取り出す書き方は 2 つあります:

df["name"]              # 1 列だけ → Series が返る
df[["name", "age"]]     # 複数列 → DataFrame が返る(リストで囲む)

df["name"]df[["name"]]見た目が似ているが戻り値の型が違う 点に注意してください。前者は Series(1 列分のラベル付き配列)、後者は DataFrame(1 列だけの表)です。可視化など「DataFrame を渡したい」場面では後者を使い分けます。

行にアクセスする

行を取り出すには 2 つの基本方法があります:

df.iloc[0]      # 位置で取り出す:先頭行
df.iloc[-1]     # 末尾行
df.iloc[0:2]    # スライス:0 行目と 1 行目(2 行目は含まない)

df.loc[0]       # ラベルで取り出す:インデックスラベルが 0 の行
  • ilocinteger location(位置) で指定
  • loclabel(ラベル) で指定

デフォルトのインデックスは整数の連番(0, 1, 2, ...)なので両者は似た見た目になりますが、インデックスを別の値(たとえば学籍番号や日付)に振り直すと挙動が分かれます。最初のうちは「数字で当てるなら iloc、名前で当てるなら loc」と覚えておけば十分です。

セル(特定の値)にアクセスする

1 つのセルだけを取り出すなら:

df.loc[0, "age"]    # 行ラベル 0、列名 "age" の値 → 25
df.at[0, "age"]     # 単一セルの高速版

普段は loc で十分ですが、ループ内で何千回も単一セルにアクセスする場面では at の方が体感で速くなります。

値の出現回数を調べる

カテゴリ列で「どの値が何個あるか」を知りたいときは value_counts() が便利です:

df["都市"].value_counts()

結果:

都市
東京    2
大阪    2
福岡    2

多い順に並べた表が返るため、「どんなカテゴリが含まれていて、それぞれがどれくらいの規模か」をすぐ把握できます。前処理を始める前に呼んでおくと、表記揺れ(「東京」と「東京都」の混在)も発見しやすくなります。

よく出る躓きどころ

  • df["name"]df[["name"]] — 戻り値の型(Series か DataFrame か)が違う。可視化メソッドに渡すと挙動が変わる
  • df.iloc[0]df.loc[0] — デフォルトでは見た目が同じだが、インデックスを振り直すと別物になる
  • df.head()df.head() を付け忘れると関数オブジェクトを返すだけで実行されない
  • 属性とメソッドの混同df.shape には () を付けず、df.info() には付ける。エラーメッセージで「takes 0 positional arguments...」が出たら間違いのサイン

サンプルコード

Drive にアップロードした users.csv を Colab で開く例:

from google.colab import drive
drive.mount('/content/drive')

import pandas as pd
df = pd.read_csv("/content/drive/MyDrive/datasets/users.csv")

print(df.shape)
print(df.columns.tolist())
df.dtypes

演習

users.csv を Colab に読み込み、次の操作を順番に試してください。

  1. df.shape / df.columns / df.dtypes を表示する
  2. df.info()df.describe() を呼んで、欠損数・型・年齢の要約統計を確認する
  3. 「年齢」列だけを Series として取り出して表示する
  4. 「名前」と「都市」の 2 列だけを DataFrame として表示する
  5. iloc[0] で先頭行を取り出して表示する
  6. df.loc[0, "年齢"] で「太郎の年齢」を単一値として取り出す
  7. df["都市"].value_counts() で都市の出現回数を確認する

各操作の出力を見て、戻り値の型(Series か DataFrame か単一値か)を意識しながら進めると、本文の説明と結びつきやすくなります。

発展課題(オプション)

  • 辞書から自分で 5 行 4 列の DataFrame を作成し、head() / tail() / describe() を呼んでみる
  • df["年齢"] + 1 のように Series 全体に演算をかけて、新しい列 年齢_翌年 を作成する
  • df.sort_values("年齢") で年齢順に並べ替えて表示する(次回以降の単元で詳しく扱います)