pandas入門:Pythonでのデータ処理・分析の第一歩

はい、承知いたしました。Pandas入門に関する詳細な記事を約5000語で記述します。記事の内容を直接表示します。


Pandas入門:Pythonでのデータ処理・分析の第一歩

データは現代のビジネスや研究において非常に重要です。Webサイトのログ、顧客の購買履歴、実験結果、センサーデータなど、様々なデータが日々蓄積されています。これらの生データをそのまま利用することは難しく、目的に応じて整理、加工、分析する必要があります。

Pythonはデータ処理・分析の分野で非常に強力な言語ですが、特にデータ操作を効率的に行うためのライブラリとして欠かせないのが Pandas です。Pandasは、R言語のデータフレームにインスパイアされており、表形式データ(スプレッドシートやデータベースのテーブルのような構造)を扱うのに特化しています。NumPyの上に構築されており、高速な数値計算と柔軟なデータ操作機能を提供します。

この入門記事では、Pandasを使ったデータ処理・分析の基本的な流れと、その核となる機能について、約5000語の詳細な説明を通じてじっくりと学んでいきます。これからデータサイエンスや機械学習を学びたい方にとって、Pandasは間違いなく最初のステップとなります。

記事の目的

  • Pandasの基本的なデータ構造(Series, DataFrame)を理解する。
  • 外部ファイル(CSVなど)からデータを読み込み、DataFrameとして扱う方法を知る。
  • DataFrameの基本的な操作(データの表示、情報の確認)ができるようになる。
  • データの選択、抽出、フィルタリングの方法を習得する。
  • 欠損値や重複データの扱いの基本を学ぶ。
  • データの並べ替えや集計(GroupBy)の方法を知る。
  • 複数のDataFrameを結合する方法を理解する。
  • 簡単なデータ可視化へのつなぎを理解する。

対象読者

  • Pythonの基本的な文法(変数、リスト、辞書、関数、ループなど)を理解している方。
  • これからデータ分析を始めたいと考えている方。
  • Pandasに興味があるが、どこから手を付けて良いかわからない方。

必要な準備

Pandasを利用するためには、Python環境が必要です。Pythonがインストールされていない場合は、公式サイトからダウンロードしてインストールしてください。Pythonのインストールについてはここでは詳しく触れませんが、多くのデータ分析タスクにはAnacondaディストリビューションを利用するのがおすすめです。AnacondaにはPython本体、Pandas、NumPy、Matplotlib、Jupyter Notebookなど、データ分析に必要な主要ライブラリがあらかじめ含まれています。

Pandasは以下のコマンドでインストールできます(pipの場合)。

bash
pip install pandas

Anacondaを使っている場合は、通常すでにインストールされています。

コードを実行するための環境として、Jupyter NotebookやJupyterLabを利用すると、対話的にコードを実行しながら結果を確認できるため便利です。

bash
pip install jupyter notebook

または

bash
pip install jupyterlab

インストール後、ターミナルで jupyter notebook または jupyter lab コマンドを実行するとブラウザで利用できるようになります。

では、Pandasの世界へ踏み出しましょう。


1. Pandasとは?なぜPandasを使うのか?

Pandasは、Pythonプログラミング言語用の高速、強力、柔軟で使いやすいオープンソースのデータ分析・操作ツールです。特に、ラベル付きのデータやリレーショナルデータ(SQLテーブルのような構造を持つデータ)を扱うのに適しています。

私たちが扱うデータの多くは、表形式(tabular data)です。例えば、Excelのスプレッドシート、CSVファイル、データベースのテーブルなどです。これらのデータは行と列を持ち、各列には特定の種類のデータ(数値、テキスト、日付など)が含まれています。Pandasはこのような表形式データを効率的に扱うための DataFrame という主要なデータ構造を提供します。

なぜ標準のPythonリストや辞書ではなくPandasを使うのでしょうか?

  • 効率性: 大量のデータを扱う際に、標準のPythonオブジェクトよりもはるかに高速に処理できます。Pandasは内部でNumPyを利用しており、ベクトル化された演算を行うため、高速化を実現しています。
  • 機能の豊富さ: データ読み込み、整形、クリーニング、集計、結合、統計処理など、データ分析に必要な多くの機能が組み込まれています。
  • 使いやすさ: 直感的で柔軟なAPIを提供しており、複雑なデータ操作も比較的簡単に記述できます。
  • 欠損値の扱い: 欠損値(データがない部分)を効率的に扱うための機能が標準で備わっています。
  • インデックス: 各データ要素にラベル(インデックス)を付けることができ、これによりデータの参照や位置合わせが容易になります。

これらの利点から、Pandasはデータサイエンス、機械学習、統計モデリング、ETL(Extract, Transform, Load)などの分野で広く利用されています。

2. Pandasの主要なデータ構造:SeriesとDataFrame

Pandasの核となるデータ構造は SeriesDataFrame の2つです。これらを理解することがPandasマスターへの第一歩です。

2.1. Series

Seriesは、1次元のラベル付き配列です。NumPyの配列に似ていますが、各要素にインデックス(ラベル)が付いている点が異なります。インデックスは数字である必要はなく、文字列など任意のハッシュ可能なPythonオブジェクトにすることができます。

Seriesの作成

最も簡単なSeriesの作成方法は、リストやNumPy配列から行うことです。

“`python
import pandas as pd
import numpy as np

リストからSeriesを作成

data_list = [10, 20, 30, 40, 50]
s1 = pd.Series(data_list)
print(“Series from list:”)
print(s1)

NumPy配列からSeriesを作成

data_array = np.array([1.1, 2.2, 3.3, 4.4, 5.5])
s2 = pd.Series(data_array)
print(“\nSeries from NumPy array:”)
print(s2)
“`

実行結果:

“`
Series from list:
0 10
1 20
2 30
3 40
4 50
dtype: int64

Series from NumPy array:
0 1.1
1 2.2
2 3.3
3 4.4
4 5.5
dtype: float64
“`

デフォルトでは、0から始まる整数がインデックスとして自動的に割り当てられます。

インデックスを指定してSeriesを作成

インデックスを明示的に指定することもできます。

“`python

インデックスを指定してSeriesを作成

data_dict = {‘a’: 100, ‘b’: 200, ‘c’: 300, ‘d’: 400}
s3 = pd.Series(data_dict)
print(“\nSeries with custom index (from dict):”)
print(s3)

リストとインデックスリストを指定して作成

data_values = [90, 80, 70, 60]
data_index = [‘東京’, ‘大阪’, ‘名古屋’, ‘福岡’]
s4 = pd.Series(data_values, index=data_index)
print(“\nSeries with custom index (from list and index list):”)
print(s4)
“`

実行結果:

“`
Series with custom index (from dict):
a 100
b 200
c 300
d 400
dtype: int64

Series with custom index (from list and index list):
東京 90
大阪 80
名古屋 70
福岡 60
dtype: int64
“`

辞書から作成する場合、辞書のキーがインデックス、値がSeriesの値になります。

スカラー値からSeriesを作成

単一のスカラー値とインデックスリストからSeriesを作成すると、指定したすべてのインデックスに対して同じ値を持つSeriesが作成されます。

“`python

スカラー値とインデックスからSeriesを作成

s5 = pd.Series(5, index=[‘x’, ‘y’, ‘z’])
print(“\nSeries from scalar value:”)
print(s5)
“`

実行結果:

Series from scalar value:
x 5
y 5
z 5
dtype: int64

Seriesの属性

Seriesオブジェクトはいくつかの便利な属性を持っています。

  • .values: Seriesの値を含むNumPy配列
  • .index: Seriesのインデックス
  • .dtype: Seriesのデータ型
  • .name: Seriesの名前
  • .size: Seriesの要素数
  • .shape: Seriesの形状(要素数をタプルで返す)

“`python
print(“\nSeries Attributes (s4):”)
print(“Values:”, s4.values)
print(“Index:”, s4.index)
print(“Dtype:”, s4.dtype)
print(“Name:”, s4.name) # まだ名前がない
print(“Size:”, s4.size)
print(“Shape:”, s4.shape)

Seriesに名前を付ける

s4.name = ‘地域別売上’
s4.index.name = ‘地域’
print(“\nSeries with Name:”)
print(s4)
print(“Name:”, s4.name)
print(“Index Name:”, s4.index.name)
“`

実行結果:

“`
Series Attributes (s4):
Values: [90 80 70 60]
Index: Index([‘東京’, ‘大阪’, ‘名古屋’, ‘福岡’], dtype=’object’, name=’地域’)
Dtype: int64
Name: None
Size: 4
Shape: (4,)

Series with Name:
地域
東京 90
大阪 80
名古屋 70
福岡 60
Name: 地域別売上, dtype: int64
Name: 地域別売上
Index Name: 地域
“`

Seriesのインデックス参照

インデックスを使ってSeriesの値にアクセスできます。NumPy配列と同様に、位置によるインデックス参照も可能です。

python
print("\nSeries Indexing:")
print("s4['大阪']:", s4['大阪']) # ラベルによる参照
print("s4[1]:", s4[1]) # 位置による参照
print("s4[['東京', '福岡']]:") # ラベルリストによる複数参照
print(s4[['東京', '福岡']])
print("s4[1:3]:") # 位置によるスライス (終点含まない)
print(s4[1:3])
print("s4['大阪':'福岡']:") # ラベルによるスライス (終点含む)
print(s4['大阪':'福岡'])

実行結果:

Series Indexing:
s4['大阪']: 80
s4[1]: 80
s4[['東京', '福岡']]:
地域
東京 90
福岡 60
Name: 地域別売上, dtype: int64
s4[1:3]:
地域
大阪 80
名古屋 70
Name: 地域別売上, dtype: int64
s4['大阪':'福岡']:
地域
大阪 80
名古屋 70
福岡 60
Name: 地域別売上, dtype: int64

位置によるスライスはNumPyと同様に終点が含まれないのに対し、ラベルによるスライスは通常終点が含まれる点に注意が必要です。

Seriesの演算

SeriesはNumPy配列と同様に、算術演算や比較演算を要素ごとに行うことができます。同じインデックスを持つSeries同士の演算では、インデックスを基準に要素が結合されます。インデックスが一致しない場合は、結果のSeriesでは該当する位置の値が欠損値(NaN – Not a Number)になります。

“`python
s6 = pd.Series([1, 2, 3, 4], index=[‘a’, ‘b’, ‘c’, ‘d’])
s7 = pd.Series([10, 20, 30, 40], index=[‘b’, ‘d’, ‘a’, ‘e’])

print(“\nSeries Operations:”)
print(“s6 + 5:\n”, s6 + 5) # ブロードキャスト
print(“s6 * 2:\n”, s6 * 2)
print(“s6 > 2:\n”, s6 > 2) # 真偽値Series

print(“s6 + s7:\n”, s6 + s7) # インデックスによる結合
“`

実行結果:

Series Operations:
s6 + 5:
a 6
b 7
c 8
d 9
dtype: int64
s6 * 2:
a 2
b 4
c 6
d 8
dtype: int64
s6 > 2:
a False
b False
c True
d True
dtype: bool
s6 + s7:
a 31.0
b 22.0
c NaN
d 24.0
e NaN
dtype: float64

s6 + s7 の結果では、インデックス ‘c’ と ‘e’ の要素が NaN になっています。これは、s6には’e’がなく、s7には’c’がないため、対応する要素が存在しないからです。Pandasでは、このようなインデックスの不一致による欠損値を自動的に処理してくれます。

2.2. DataFrame

DataFrameは、Pandasで最も頻繁に利用される主要なデータ構造です。複数のSeriesを列としてまとめた2次元のラベル付きデータ構造と考えることができます。スプレッドシートやデータベースのテーブルによく似ています。

DataFrameは行と列を持ちます。
* 列 (Columns): 各列はSeriesオブジェクトであり、名前(列名)を持ちます。列内のデータは同じデータ型であることが多いですが、必須ではありません(ただし、同じ型である方が処理効率は良いです)。
* 行 (Rows): 各行はインデックス(行ラベル)を持ちます。

DataFrameの作成

DataFrameを作成する方法はいくつかあります。

  • 辞書から作成: キーを列名、値をリストやNumPy配列、またはSeriesとする辞書から作成するのが一般的です。
  • Seriesの辞書から作成: 各要素がSeriesである辞書から作成します。
  • リストのリスト/NumPy配列から作成: 2次元のリストやNumPy配列から作成し、列名やインデックスを別途指定します。
  • 他のDataFrameや構造化データから: CSVファイル、データベース、JSONなど、様々なソースから読み込むことができます。

辞書から作成 (最も一般的)

“`python

辞書からDataFrameを作成

data = {
‘都市’: [‘東京’, ‘大阪’, ‘福岡’, ‘札幌’, ‘名古屋’],
‘人口’: [1400, 270, 160, 190, 230], # 単位: 万人
‘面積’: [2190, 223, 343, 1121, 326] # 単位: km^2
}
df1 = pd.DataFrame(data)
print(“DataFrame from dictionary:”)
print(df1)
“`

実行結果:

DataFrame from dictionary:
都市 人口 面積
0 東京 1400 2190
1 大阪 270 223
2 福岡 160 343
3 札幌 190 1121
4 名古屋 230 326

デフォルトでは、0から始まる整数が行インデックスとして、辞書のキーが列名として使用されます。

インデックスと列名を指定して作成

DataFrameを作成する際に、インデックスや列名の順序を明示的に指定することもできます。

“`python

インデックスと列名を指定してDataFrameを作成

df2 = pd.DataFrame(data, columns=[‘都市’, ‘面積’, ‘人口’], index=[‘A’, ‘B’, ‘C’, ‘D’, ‘E’])
print(“\nDataFrame with custom index and column order:”)
print(df2)
“`

実行結果:

DataFrame with custom index and column order:
都市 面積 人口
A 東京 2190 1400
B 大阪 223 270
C 福岡 343 160
D 札幌 1121 190
E 名古屋 326 230

指定しなかった列名があった場合、その列は欠損値(NaN)で埋められます。

python
data2 = {'col1': [1, 2], 'col2': [3, 4]}
df3 = pd.DataFrame(data2, columns=['col1', 'col3', 'col2'])
print("\nDataFrame with missing column:")
print(df3)

実行結果:

DataFrame with missing column:
col1 col3 col2
0 1 NaN 3.0
1 2 NaN 4.0

DataFrameの属性

DataFrameオブジェクトもSeriesと同様に便利な属性を持っています。

  • .values: DataFrameの値を含む2次元NumPy配列
  • .index: DataFrameの行インデックス
  • .columns: DataFrameの列インデックス(列名のリスト)
  • .dtypes: 各列のデータ型を含むSeries
  • .shape: DataFrameの形状(行数, 列数)をタプルで返す
  • .size: DataFrameの全要素数 (行数 * 列数)
  • .T: DataFrameを転置したもの

python
print("\nDataFrame Attributes (df1):")
print("Values:\n", df1.values)
print("\nIndex:", df1.index)
print("\nColumns:", df1.columns)
print("\nDtypes:\n", df1.dtypes)
print("\nShape:", df1.shape)
print("Size:", df1.size)
print("\nTransposed (df1.T):\n", df1.T)

実行結果:

“`
DataFrame Attributes (df1):
Values:
[[ ‘東京’ 1400 2190]
[‘大阪’ 270 223]
[‘福岡’ 160 343]
[‘札幌’ 190 1121]
[‘名古屋’ 230 326]]

Index: RangeIndex(start=0, stop=5, step=1)

Columns: Index([‘都市’, ‘人口’, ‘面積’], dtype=’object’)

Dtypes:
都市 object
人口 int64
面積 int64
dtype: object

Shape: (5, 3)
Size: 15

Transposed (df1.T):
0 1 2 3 4
都市 東京 大阪 福岡 札幌 名古屋
人口 1400 270 160 190 230
面積 2190 223 343 1121 326
“`

.dtypes は、各列のデータ型を示すSeriesを返します。object 型は通常、文字列や混合型のデータを表します。

3. データの読み込みと保存

実際のデータ分析では、多くの場合、CSVやExcelなどの外部ファイルからデータを読み込みます。Pandasは様々な形式のファイルを読み込むための関数を提供しています。

3.1. CSVファイルの読み込み

CSV (Comma Separated Values) は、データをカンマで区切ったテキストファイル形式で、最も一般的によく使われます。Pandasでは read_csv() 関数を使って読み込みます。

サンプルCSVデータを作成します(ここでは文字列として)。

“`python
csv_data = “””name,age,city,occupation
Alice,25,New York,Engineer
Bob,30,London,Artist
Charlie,35,Paris,Doctor
David,28,Tokyo,Teacher
Eve,22,New York,Student
Frank,40,London,Manager
“””

一時的にファイルに保存(実際のファイル読み込みをシミュレート)

with open(“sample.csv”, “w”) as f:
f.write(csv_data)

CSVファイルを読み込む

df_csv = pd.read_csv(“sample.csv”)
print(“\nDataFrame from sample.csv:”)
print(df_csv)
“`

実行結果:

DataFrame from sample.csv:
name age city occupation
0 Alice 25 New York Engineer
1 Bob 30 London Artist
2 Charlie 35 Paris Doctor
3 David 28 Tokyo Teacher
4 Eve 22 New York Student
5 Frank 40 London Manager

read_csv() は非常に多くの引数を持っています。いくつか重要なものを紹介します。

  • filepath_or_buffer: 読み込むファイルのパスまたはURL。
  • sep: 区切り文字。デフォルトはカンマ , です。タブ区切りなら '\t' を指定します。
  • header: ヘッダー(列名)がある行の番号を指定します。デフォルトは 0 (最初の行) です。ヘッダーがない場合は None を指定します。
  • index_col: 行インデックスとして使用する列を指定します(列番号または列名)。
  • names: ヘッダーがない場合に、列名のリストを指定します。header=None と組み合わせて使います。
  • dtype: 各列のデータ型を辞書で指定します。 { '列名': 型名 } の形式。
  • na_values: 欠損値として扱う文字列または文字列のリストを指定します。
  • encoding: ファイルのエンコーディングを指定します(例: 'utf-8', 'shift-jis')。

例: ヘッダーなし、インデックス列指定

“`python
csv_data_no_header = “””1,Alice,25,New York,Engineer
2,Bob,30,London,Artist
3,Charlie,35,Paris,Doctor
4,David,28,Tokyo,Teacher
5,Eve,22,New York,Student
6,Frank,40,London,Manager
“””
with open(“sample_no_header.csv”, “w”) as f:
f.write(csv_data_no_header)

ヘッダーなしとして読み込み、列名を指定し、最初の列をインデックスとして使用

df_no_header = pd.read_csv(“sample_no_header.csv”,
header=None,
names=[‘id’, ‘name’, ‘age’, ‘city’, ‘occupation’],
index_col=’id’)
print(“\nDataFrame from sample_no_header.csv (with options):”)
print(df_no_header)
“`

実行結果:

DataFrame from sample_no_header.csv (with options):
name age city occupation
id
1 Alice 25 New York Engineer
2 Bob 30 London Artist
3 Charlie 35 Paris Doctor
4 David 28 Tokyo Teacher
5 Eve 22 New York Student
6 Frank 40 London Manager

3.2. その他の形式の読み込み

PandasはCSV以外にも様々な形式をサポートしています。

  • pd.read_excel(): Excelファイル (.xls, .xlsx)
  • pd.read_json(): JSONファイル
  • pd.read_html(): HTMLテーブル
  • pd.read_sql(): データベースからの読み込み
  • pd.read_parquet(), pd.read_feather(): 高速なバイナリ形式

3.3. DataFrameの保存

DataFrameをファイルに保存することも簡単です。

“`python

DataFrameをCSVファイルとして保存

df_csv.to_csv(“output.csv”, index=False) # index=Falseで行インデックスを保存しない

保存したCSVを読み込んで確認

df_output = pd.read_csv(“output.csv”)
print(“\nDataFrame from output.csv:”)
print(df_output)

DataFrameをExcelファイルとして保存

df_csv.to_excel(“output.xlsx”, index=False)

その他の形式への保存も同様に to_json(), to_html(), to_sql(), to_parquet(), to_feather() などがあります。

“`

to_csv()index=False 引数は重要です。これを指定しないと、DataFrameの行インデックスも一緒にファイルに保存されてしまいます。通常、行インデックスはファイルに含める必要がないことが多いです。

4. 基本的なデータ検査 (Inspection)

データを読み込んだら、まずその内容を確認することが重要です。PandasはDataFrameの内容を素早く把握するための便利なメソッドを提供しています。

“`python
print(“\n— Basic Data Inspection (df_csv) —“)

最初の5行を表示 (デフォルト)

print(“\nHead (first 5 rows):”)
print(df_csv.head())

最初の3行を表示

print(“\nHead (first 3 rows):”)
print(df_csv.head(3))

最後の5行を表示 (デフォルト)

print(“\nTail (last 5 rows):”)
print(df_csv.tail())

最後の2行を表示

print(“\nTail (last 2 rows):”)
print(df_csv.tail(2))

DataFrameの簡易情報 (列名、非欠損値数、データ型、メモリ使用量など)

print(“\nInfo:”)
df_csv.info()

各数値列の要約統計量 (個数、平均、標準偏差、最小値、最大値、四分位数)

print(“\nDescribe:”)
print(df_csv.describe())

オブジェクト型(文字列など)の列も含む要約統計量

print(“\nDescribe (include objects):”)
print(df_csv.describe(include=’object’))

列名とインデックスを表示

print(“\nColumns:”, df_csv.columns)
print(“Index:”, df_csv.index)

DataFrameの形状 (行数, 列数)

print(“\nShape:”, df_csv.shape)

各列の非欠損値の数をカウント

print(“\nNumber of non-null values per column:”)
print(df_csv.count())
“`

実行結果(一部抜粋):

“`
— Basic Data Inspection (df_csv) —

Head (first 5 rows):
name age city occupation
0 Alice 25 New York Engineer
1 Bob 30 London Artist
2 Charlie 35 Paris Doctor
3 David 28 Tokyo Teacher
4 Eve 22 New York Student

Info:

RangeIndex: 6 entries, 0 to 5
Data columns (total 4 columns):
# Column Non-Null Count Dtype
— —— ————– —–
0 name 6 non-null object
1 age 6 non-null int64
2 city 6 non-null object
3 occupation 6 non-null object
dtypes: int64(1), object(3)
memory usage: 320.0+ bytes

Describe:
age
count 6.000000
mean 30.000000
std 6.324555
min 22.000000
25% 26.500000
50% 29.000000
75% 33.750000
max 40.000000

Shape: (6, 4)
Size: 24

Number of non-null values per column:
name 6
age 6
city 6
occupation 6
dtype: int64
“`

これらのメソッドを使うことで、データの全体像、データ型、欠損値の有無、基本的な統計量などを素早く把握し、その後の分析計画を立てるのに役立てることができます。

5. データの選択、抽出、フィルタリング

DataFrameから特定のデータを取り出す操作は非常に頻繁に行われます。Pandasにはいくつかの方法があります。

5.1. 列の選択

列を選択するには、DataFrameの列名を使います。

“`python
print(“\n— Data Selection: Columns —“)

1つの列を選択 (Seriesとして取得)

ages = df_csv[‘age’]
print(“\nSelecting ‘age’ column (Series):”)
print(ages)
print(“Type of ‘age’ column:”, type(ages))

複数の列を選択 (DataFrameとして取得)

subset = df_csv[[‘name’, ‘city’]]
print(“\nSelecting multiple columns (‘name’, ‘city’) (DataFrame):”)
print(subset)
print(“Type of subset:”, type(subset))

ドット記法での列選択 (列名にスペースや特殊文字が含まれない場合のみ推奨)

print(“\nSelecting ‘age’ column using dot notation:”)

print(df_csv.age)

“`

実行結果(一部抜粋):

“`
— Data Selection: Columns —

Selecting ‘age’ column (Series):
0 25
1 30
2 35
3 28
4 22
5 40
Name: age, dtype: int64
Type of ‘age’ column:

Selecting multiple columns (‘name’, ‘city’) (DataFrame):
name city
0 Alice New York
1 Bob London
2 Charlie Paris
3 David Tokyo
4 Eve New York
5 Frank London
Type of subset:
“`

単一の列を選択するとSeriesが返されます。複数の列を選択する場合は、列名のリストを渡す必要があり、結果はDataFrameになります。

5.2. 行の選択: .loc.iloc

特定の行を選択するには、主に .loc.iloc という2つのプロパティを使用します。

  • .loc: ラベルによるインデックス参照
  • .iloc: 位置による整数ベースのインデックス参照 (NumPyと同様)

.loc (ラベルによる参照)

行インデックスのラベル、列インデックスのラベルを使って選択します。

“`python
print(“\n— Data Selection: Rows (.loc) —“)

単一の行を選択 (行インデックスラベルを指定)

row_0 = df_csv.loc[0]
print(“\nSelecting row with index label 0 (.loc):”)
print(row_0) # 結果はSeries (行全体)

複数の行を選択 (行インデックスラベルのリストを指定)

rows_0_2 = df_csv.loc[[0, 2]]
print(“\nSelecting rows with index labels 0 and 2 (.loc):”)
print(rows_0_2)

ラベルによる行スライス (終点を含む)

rows_0_to_2 = df_csv.loc[0:2]
print(“\nSelecting rows with index labels 0 to 2 (inclusive) (.loc):”)
print(rows_0_to_2)

特定の行インデックスラベルと列インデックスラベルで特定の要素を選択

cell_0_name = df_csv.loc[0, ‘name’]
print(“\nSelecting cell at index 0, column ‘name’ (.loc):”, cell_0_name)

特定の行インデックスラベルと列インデックスラベルのリストで複数の要素を選択

subset_0_2_name_city = df_csv.loc[[0, 2], [‘name’, ‘city’]]
print(“\nSelecting subset of rows [0, 2] and columns [‘name’, ‘city’] (.loc):”)
print(subset_0_2_name_city)

ラベルスライスと列名リストでサブDataFrameを選択

subset_0_to_2_name_city = df_csv.loc[0:2, [‘name’, ‘city’]]
print(“\nSelecting subset of rows 0 to 2 and columns [‘name’, ‘city’] (.loc):”)
print(subset_0_to_2_name_city)

全ての行に対して特定の列を選択

all_rows_name_city = df_csv.loc[:, [‘name’, ‘city’]]
print(“\nSelecting all rows, columns [‘name’, ‘city’] (.loc):”)
print(all_rows_name_city)
“`

loc は行インデックスのラベルと列インデックスのラベルを受け付けます。スライスを使う場合、loc は終点のラベル を含みます

.iloc (位置による参照)

NumPy配列と同じように、0から始まる整数位置を使って行や列を選択します。

“`python
print(“\n— Data Selection: Rows (.iloc) —“)

単一の行を選択 (行の整数位置を指定)

row_pos_0 = df_csv.iloc[0]
print(“\nSelecting row at position 0 (.iloc):”)
print(row_pos_0) # 結果はSeries

複数の行を選択 (行の整数位置のリストを指定)

rows_pos_0_2 = df_csv.iloc[[0, 2]]
print(“\nSelecting rows at positions 0 and 2 (.iloc):”)
print(rows_pos_0_2)

位置による行スライス (終点を含まない)

rows_pos_0_to_2 = df_csv.iloc[0:3] # position 0, 1, 2 を選択
print(“\nSelecting rows at positions 0 to 2 (exclusive of 3) (.iloc):”)
print(rows_pos_0_to_2)

特定の行位置と列位置で特定の要素を選択

cell_pos_0_0 = df_csv.iloc[0, 0]
print(“\nSelecting cell at position 0, 0 (.iloc):”, cell_pos_0_0)

特定の行位置リストと列位置リストで複数の要素を選択

subset_pos_0_2_0_1 = df_csv.iloc[[0, 2], [0, 1]]
print(“\nSelecting subset at positions [0, 2] and columns [0, 1] (.iloc):”)
print(subset_pos_0_2_0_1)

位置スライスと列位置リストでサブDataFrameを選択

subset_pos_0_to_2_0_to_2 = df_csv.iloc[0:3, 0:2]
print(“\nSelecting subset at positions 0 to 2 and columns 0 to 1 (.iloc):”)
print(subset_pos_0_to_2_0_to_2)

全ての行に対して特定の列を選択 (列位置で指定)

all_rows_pos_0_1 = df_csv.iloc[:, [0, 1]]
print(“\nSelecting all rows, columns at positions [0, 1] (.iloc):”)
print(all_rows_pos_0_1)
“`

iloc は行と列の整数位置を受け付けます。スライスを使う場合、iloc は終点の位置 を含みません (Python/NumPyのスライスと同じ動作)。

.loc.iloc の使い分けは非常に重要です。特にインデックスがデフォルトの整数 (0, 1, 2, ...) の場合、混乱しやすいので注意が必要です。
* インデックスラベルを使いたいなら .loc
* 整数位置を使いたいなら .iloc

5.3. 条件によるフィルタリング (Boolean Indexing)

特定の条件を満たす行だけを選択したい場合は、真偽値Seriesを使ったインデックス参照(Boolean Indexing)を使います。条件式は各行に対してTrue/Falseを返す真偽値Seriesを生成し、それをDataFrameに適用することでTrueに対応する行のみを選択できます。

“`python
print(“\n— Data Filtering (Boolean Indexing) —“)

ageが30以上の行を選択

older_than_30 = df_csv[df_csv[‘age’] >= 30]
print(“\nRows where age is 30 or older:”)
print(older_than_30)

cityが’New York’の行を選択

from_new_york = df_csv[df_csv[‘city’] == ‘New York’]
print(“\nRows where city is ‘New York’:”)
print(from_new_york)

複数の条件を組み合わせる (論理演算子 & (AND), | (OR), ~ (NOT) を使用)

各条件は括弧で囲む必要がある

ageが30以上 かつ cityが’London’の行を選択

older_than_30_london = df_csv[(df_csv[‘age’] >= 30) & (df_csv[‘city’] == ‘London’)]
print(“\nRows where age is 30 or older AND city is ‘London’:”)
print(older_than_30_london)

cityが’New York’ または ‘Paris’の行を選択

isin() メソッドが便利

from_ny_or_paris = df_csv[df_csv[‘city’].isin([‘New York’, ‘Paris’])]
print(“\nRows where city is ‘New York’ OR ‘Paris’ (using isin):”)
print(from_ny_or_paris)

フィルタリングした結果から特定の列を選択

names_from_london = df_csv[df_csv[‘city’] == ‘London’][‘name’]
print(“\nNames of people from ‘London’:”)
print(names_from_london)

.loc と組み合わせて使うのが推奨される (SettingWithCopyWarningを避けるため)

例: フィルタリングした結果に対して新しい値を代入する場合など

df_copy = df_csv.copy() # コピーを作成してWarningを避ける
df_copy.loc[df_copy[‘age’] >= 30, ‘is_adult’] = True
df_copy.loc[df_copy[‘age’] < 30, ‘is_adult’] = False
print(“\nDataFrame with ‘is_adult’ column added using .loc with boolean indexing:”)
print(df_copy)
“`

実行結果(一部抜粋):

“`
— Data Filtering (Boolean Indexing) —

Rows where age is 30 or older:
name age city occupation
1 Bob 30 London Artist
2 Charlie 35 Paris Doctor
5 Frank 40 London Manager

Rows where city is ‘New York’:
name age city occupation
0 Alice 25 New York Engineer
4 Eve 22 New York Student

Names of people from ‘London’:
1 Bob
5 Frank
Name: name, dtype: object

DataFrame with ‘is_adult’ column added using .loc with boolean indexing:
name age city occupation is_adult
0 Alice 25 New York Engineer False
1 Bob 30 London Artist True
2 Charlie 35 Paris Doctor True
3 David 28 Tokyo Teacher False
4 Eve 22 New York Student False
5 Frank 40 London Manager True
“`

Boolean Indexingはデータ分析において非常に強力な手法です。特定の条件を満たすデータだけを抽出して分析したい場合に頻繁に利用します。

6. データのクリーニングと前処理

実際のデータは、欠損値が含まれていたり、重複していたり、データ型が不適切だったりすることがよくあります。分析を始める前に、これらの問題を解決するためのデータクリーニング(前処理)が必要です。

6.1. 欠損値の扱い

欠損値は、データが存在しない、または取得できなかった部分です。Pandasではデフォルトで NaN (Not a Number) という特殊な値で表現されます。

欠損値を含むDataFrameを作成して試してみましょう。

“`python
data_missing = {
‘A’: [1, 2, np.nan, 4, 5],
‘B’: [np.nan, 6, 7, 8, np.nan],
‘C’: [9, 10, 11, 12, 13],
‘D’: [14, np.nan, np.nan, 15, 16]
}
df_missing = pd.DataFrame(data_missing)
print(“\n— Handling Missing Data —“)
print(“\nOriginal DataFrame with missing values:”)
print(df_missing)

欠損値があるか確認

print(“\nCheck for missing values (boolean DataFrame):”)
print(df_missing.isnull())

print(“\nCheck for non-missing values (boolean DataFrame):”)
print(df_missing.notnull())

各列の欠損値の数をカウント

print(“\nNumber of missing values per column:”)
print(df_missing.isnull().sum())

DataFrame全体の欠損値の数をカウント

print(“\nTotal number of missing values in DataFrame:”, df_missing.isnull().sum().sum())
“`

実行結果(一部抜粋):

“`
— Handling Missing Data —

Original DataFrame with missing values:
A B C D
0 1.0 NaN 9 14.0
1 2.0 6.0 10 NaN
2 NaN 7.0 11 NaN
3 4.0 8.0 12 15.0
4 5.0 NaN 13 16.0

Check for missing values (boolean DataFrame):
A B C D
0 False True False False
1 False False False True
2 True False False True
3 False False False False
4 False True False False

Number of missing values per column:
A 1
B 2
C 0
D 2
dtype: int64

Total number of missing values in DataFrame: 5
“`

欠損値の削除 (dropna)

欠損値を含む行や列を削除することができます。

“`python

欠損値を含む行を削除 (デフォルト: axis=0, how=’any’)

df_dropped_rows = df_missing.dropna()
print(“\nDataFrame after dropping rows with any missing value:”)
print(df_dropped_rows) # 欠損値を持つ行がすべて削除される

全ての要素が欠損値である行のみを削除

df_dropped_rows_all = df_missing.dropna(how=’all’)
print(“\nDataFrame after dropping rows with all missing values:”)
print(df_dropped_rows_all) # この例では該当する行はない

欠損値を含む列を削除 (axis=1を指定)

df_dropped_cols = df_missing.dropna(axis=1)
print(“\nDataFrame after dropping columns with any missing value:”)
print(df_dropped_cols) # ‘A’, ‘B’, ‘D’ 列が削除される

全ての要素が欠損値である列のみを削除

df_dropped_cols_all = df_missing.dropna(axis=1, how=’all’)
print(“\nDataFrame after dropping columns with all missing values:”)
print(df_dropped_cols_all) # この例では該当する列はない
“`

dropna() は新しいDataFrameを返します。元のDataFrameを直接変更したい場合は inplace=True 引数を使用します(推奨は新しい変数に代入)。

欠損値の補完 (fillna)

欠損値を特定の値や、他の行/列の値で埋めることができます。

“`python

欠損値を特定の値で埋める

df_filled_zero = df_missing.fillna(0)
print(“\nDataFrame after filling missing values with 0:”)
print(df_filled_zero)

欠損値を列の平均値で埋める (数値列のみに適用)

df_filled_mean = df_missing.fillna(df_missing.mean())
print(“\nDataFrame after filling missing values with column mean:”)
print(df_filled_mean)

前の行の値で埋める (Forward Fill)

df_filled_ffill = df_missing.fillna(method=’ffill’)
print(“\nDataFrame after forward fill (method=’ffill’):”)
print(df_filled_ffill)

次の行の値で埋める (Backward Fill)

df_filled_bfill = df_missing.fillna(method=’bfill’)
print(“\nDataFrame after backward fill (method=’bfill’):”)
print(df_filled_bfill)

特定の列だけ欠損値を埋める

df_missing[‘A’] = df_missing[‘A’].fillna(df_missing[‘A’].mean())
print(“\nDataFrame after filling missing values in column ‘A’ with its mean:”)
print(df_missing)
“`

fillna() もデフォルトでは新しいDataFrameを返します。inplace=True で元のDataFrameを変更できます。

method='ffill''bfill' は時系列データなどでよく使われます。

6.2. 重複データの扱い

DataFrameに行の重複がある場合、それを検出したり削除したりできます。

“`python
data_duplicates = {
‘col1’: [‘A’, ‘B’, ‘A’, ‘C’, ‘B’, ‘A’],
‘col2’: [1, 2, 1, 3, 2, 1],
‘col3’: [‘X’, ‘Y’, ‘X’, ‘Z’, ‘Y’, ‘X’]
}
df_duplicates = pd.DataFrame(data_duplicates)
print(“\n— Handling Duplicate Data —“)
print(“\nOriginal DataFrame with duplicates:”)
print(df_duplicates)

重複している行を検出 (最初の出現はFalse)

print(“\nBoolean Series indicating duplicate rows (keeping first):”)
print(df_duplicates.duplicated())

最後の出現を重複と見なす

print(“\nBoolean Series indicating duplicate rows (keeping last):”)
print(df_duplicates.duplicated(keep=’last’))

全ての重複行を検出 (元の行もTrueになる)

print(“\nBoolean Series indicating all duplicate occurrences:”)
print(df_duplicates.duplicated(keep=False))

重複行を削除 (デフォルト: 最初の出現を残す)

df_no_duplicates = df_duplicates.drop_duplicates()
print(“\nDataFrame after dropping duplicate rows (keeping first):”)
print(df_no_duplicates)

重複行を削除 (最後の出現を残す)

df_no_duplicates_last = df_duplicates.drop_duplicates(keep=’last’)
print(“\nDataFrame after dropping duplicate rows (keeping last):”)
print(df_no_duplicates_last)

特定の列の組み合わせで重複を判定・削除

df_duplicates_subset = df_duplicates.drop_duplicates(subset=[‘col1’, ‘col2’])
print(“\nDataFrame after dropping duplicates based on [‘col1’, ‘col2’]:”)
print(df_duplicates_subset)
“`

duplicated() は各行がそれ以前の行と重複しているかどうかの真偽値Seriesを返します。drop_duplicates() は重複した行を削除した新しいDataFrameを返します。keep 引数でどの重複を残すかを指定できます。subset 引数を使うと、特定の列の組み合わせが重複している場合にのみ重複と判定できます。

6.3. データ型の変換 (astype)

列のデータ型が、意図した型になっていない場合があります(例: 数字に見えるが object (文字列) 型になっている)。astype() メソッドを使ってデータ型を変換できます。

“`python
df_types = pd.DataFrame({‘col_int’: [1, 2, 3],
‘col_float’: [1.1, 2.2, 3.3],
‘col_str’: [‘A’, ‘B’, ‘C’],
‘col_num_str’: [‘100’, ‘200’, ‘300’]})
print(“\n— Data Type Conversion —“)
print(“\nOriginal DataFrame dtypes:”)
print(df_types.dtypes)

文字列型の数字列を整数型に変換

df_types[‘col_num_str’] = df_types[‘col_num_str’].astype(int)
print(“\nDataFrame after converting ‘col_num_str’ to int:”)
print(df_types)
print(df_types.dtypes)

整数型を浮動小数点型に変換

df_types[‘col_int’] = df_types[‘col_int’].astype(float)
print(“\nDataFrame after converting ‘col_int’ to float:”)
print(df_types)
print(df_types.dtypes)

複数の列の型を一度に変換 (辞書を使用)

df_types = df_types.astype({‘col_float’: str, ‘col_str’: ‘category’})
print(“\nDataFrame after converting multiple columns:”)
print(df_types)
print(df_types.dtypes)
“`

注意点として、変換できない値(例: 数字ではない文字列を数値型に変換しようとする)があるとエラーが発生します。pd.to_numeric() などの関数にはエラー発生時の挙動を制御する引数(errors='coerce' など)があり、より柔軟な変換が可能です。

6.4. 列名/インデックス名の変更 (rename)

列名や行インデックス名を変更したい場合があります。rename() メソッドを使用します。

“`python
print(“\n— Renaming Columns/Index —“)
print(“\nOriginal DataFrame (df_csv):”)
print(df_csv)

列名を変更

df_renamed_cols = df_csv.rename(columns={‘age’: ‘Age’, ‘occupation’: ‘Job’})
print(“\nDataFrame after renaming columns:”)
print(df_renamed_cols)

インデックス名を変更

df_renamed_index = df_csv.rename(index={0: ‘first’, 1: ‘second’})
print(“\nDataFrame after renaming index labels:”)
print(df_renamed_index)

元のDataFrameを直接変更 (inplace=True)

df_csv.rename(columns={‘city’: ‘City’}, inplace=True)
print(“\nOriginal df_csv after renaming ‘city’ column (inplace=True):”)
print(df_csv)
“`

rename() は新しいDataFrameを返します。元のDataFrameを直接変更したい場合は inplace=True を使用します。変更したい古い名前と新しい名前のペアを辞書で指定します。

7. データの操作と変換

DataFrameの列に対して計算を行ったり、関数を適用したり、値を置換したりすることで、データを変換・加工することができます。

7.1. 列の追加・変更・削除

新しい列を作成したり、既存の列の値を変更したり、列を削除したりするのは簡単です。

“`python
print(“\n— Data Manipulation: Columns —“)
print(“\nOriginal DataFrame (df1):”)
print(df1)

新しい列の追加 (既存の列から計算)

df1[‘人口密度’] = df1[‘人口’] / df1[‘面積’] # 新しい列名 = 計算式
print(“\nDataFrame after adding ‘人口密度’ column:”)
print(df1)

新しい列の追加 (スカラー値で初期化)

df1[‘国’] = ‘日本’
print(“\nDataFrame after adding ‘国’ column:”)
print(df1)

新しい列の追加 (Seriesやリストを指定)

行数が一致している必要がある

new_column_data = [1000, 2000, 1500, 1200, 1800]
df1[‘GDP’] = new_column_data
print(“\nDataFrame after adding ‘GDP’ column:”)
print(df1)

既存の列の値の変更

df1.loc[df1[‘都市’] == ‘東京’, ‘人口’] = 1410 # 東京の人口を更新
print(“\nDataFrame after updating ‘人口’ for 東京:”)
print(df1)

列の削除

df1_dropped = df1.drop(‘GDP’, axis=1) # axis=1 で列を指定
print(“\nDataFrame after dropping ‘GDP’ column:”)
print(df1_dropped)

複数の列を削除

df1_dropped_multi = df1.drop([‘GDP’, ‘国’], axis=1)
print(“\nDataFrame after dropping multiple columns (‘GDP’, ‘国’):”)
print(df1_dropped_multi)

行の削除 (インデックスラベルを指定, axis=0はデフォルトなので省略可)

df1_dropped_row = df1.drop(0)
print(“\nDataFrame after dropping row with index label 0:”)
print(df1_dropped_row)
“`

列の追加や変更は、新しい列名を指定して値を代入するだけで行えます。削除には drop() メソッドを使用し、削除対象が行か列かを axis 引数で指定します。

7.2. 関数の適用 (apply)

DataFrameやSeriesに対して、独自の関数を適用したい場合があります。apply() メソッドを使用します。

  • Seriesに対して適用すると、各要素に関数が適用されます。
  • DataFrameに対して axis=0 (デフォルト) で適用すると、各列に関数が適用されます。
  • DataFrameに対して axis=1 で適用すると、各行に関数が適用されます。

“`python
print(“\n— Applying Functions (.apply) —“)

Seriesに関数を適用 (各要素に+100)

s = pd.Series([10, 20, 30, 40])
s_applied = s.apply(lambda x: x + 100)
print(“\nSeries after applying function (each element + 100):”)
print(s_applied)

DataFrameの列に関数を適用 (age列を年齢層に分類)

def categorize_age(age):
if age < 25:
return ‘Young’
elif 25 <= age < 35:
return ‘Adult’
else:
return ‘Senior’

df_csv[‘age_group’] = df_csv[‘age’].apply(categorize_age)
print(“\nDataFrame after applying function to ‘age’ column:”)
print(df_csv)

DataFrameの行に関数を適用 (axis=1)

各行の ‘age’ と ‘occupation’ を組み合わせて新しい列を作成

def create_description(row):
return f”{row[‘name’]} is a {row[‘occupation’]} aged {row[‘age’]}”

df_csv[‘description’] = df_csv.apply(create_description, axis=1)
print(“\nDataFrame after applying function to rows (axis=1):”)
print(df_csv)
“`

apply() は非常に柔軟ですが、要素ごとの単純な操作であれば、ブロードキャストやベクトル化された演算の方が高速な場合が多いです。apply(axis=1) は行ごとにPythonの関数を実行するため、大きなデータセットでは遅くなる可能性があります。可能な限りベクトル化されたPandas/NumPyの演算を利用することが推奨されます。

7.3. 値の置換 (replace)

DataFrameやSeries内の特定の値を他の値に置換したい場合に使用します。

“`python
print(“\n— Replacing Values (.replace) —“)

df_replace = pd.DataFrame({‘col1’: [‘A’, ‘B’, ‘C’, ‘A’, ‘D’],
‘col2’: [1, 2, 3, 1, 4]})
print(“\nOriginal DataFrame for replacement:”)
print(df_replace)

単一の値の置換

df_replace[‘col1’] = df_replace[‘col1’].replace(‘A’, ‘X’)
print(“\nDataFrame after replacing ‘A’ with ‘X’ in col1:”)
print(df_replace)

複数の値を一度に置換 (リストまたは辞書を使用)

df_replace[‘col2’] = df_replace[‘col2’].replace([1, 2], [10, 20])
print(“\nDataFrame after replacing 1 with 10, 2 with 20 in col2:”)
print(df_replace)

辞書を使った置換 (より柔軟)

df_replace[‘col1’] = df_replace[‘col1’].replace({‘X’: ‘Alpha’, ‘C’: ‘Gamma’})
print(“\nDataFrame after replacing ‘X’ with ‘Alpha’, ‘C’ with ‘Gamma’ in col1:”)
print(df_replace)

DataFrame全体で置換 (特定の列を指定しない場合)

df_replace.replace(10, 100, inplace=True) # DataFrame全体の10を100に置換

“`

replace() は文字列、数値、正規表現など、様々なパターンでの置換に対応しています。

8. 集計とGroupBy

データ分析において、データをカテゴリごとにグループ化し、各グループに対して集計を行う操作(例えば、地域ごとの合計売上、製品ごとの平均価格など)は非常に重要です。Pandasの groupby() メソッドは、この操作を効率的に行います。

groupby() は、「Split-Apply-Combine」という処理の流れに沿っています。
1. Split (分割): 指定したキー(列)の値に基づいてDataFrameをグループに分割します。
2. Apply (適用): 各グループに対して、集計関数(平均、合計、カウントなど)や変換関数、フィルタリング関数を適用します。
3. Combine (結合): 各グループでの処理結果を組み合わせて、新しいDataFrameやSeriesを作成します。

例として、df_csv を拡張したデータセットを使います。

“`python
data_sales = {
‘product’: [‘A’, ‘B’, ‘A’, ‘C’, ‘B’, ‘C’, ‘A’, ‘B’, ‘C’],
‘region’: [‘East’, ‘West’, ‘East’, ‘North’, ‘South’, ‘East’, ‘West’, ‘North’, ‘South’],
‘sales’: [100, 150, 120, 90, 200, 110, 130, 160, 180],
‘quantity’: [10, 15, 12, 9, 20, 11, 13, 16, 18]
}
df_sales = pd.DataFrame(data_sales)
print(“\n— GroupBy and Aggregation —“)
print(“\nOriginal Sales DataFrame:”)
print(df_sales)

product列でグループ化し、salesの合計を計算

sales_by_product = df_sales.groupby(‘product’)[‘sales’].sum()
print(“\nTotal sales by product:”)
print(sales_by_product) # 結果はSeries

region列でグループ化し、quantityの平均を計算

avg_qty_by_region = df_sales.groupby(‘region’)[‘quantity’].mean()
print(“\nAverage quantity by region:”)
print(avg_qty_by_region) # 結果はSeries

複数の列でグループ化し、複数の集計関数を適用

regionとproductでグループ化し、salesの合計とquantityの平均を計算

sales_qty_summary = df_sales.groupby([‘region’, ‘product’]).agg({
‘sales’: ‘sum’,
‘quantity’: ‘mean’
})
print(“\nSales sum and quantity mean by region and product:”)
print(sales_qty_summary) # 結果はDataFrame (MultiIndex)

グループのサイズ(要素数)をカウント

group_sizes = df_sales.groupby(‘product’).size()
print(“\nNumber of records per product:”)
print(group_sizes) # 結果はSeries

グループ化されたオブジェクトを表示(これは実際のデータではない)

grouped = df_sales.groupby(‘product’)

print(“\nGrouped object (not actual data):\n”, grouped)

print(“\nAccessing a specific group (‘A’):\n”, grouped.get_group(‘A’))

“`

groupby() を呼び出すだけでは集計は実行されません。これは「Grouped」オブジェクトを返します。このオブジェクトに対して集計関数(sum(), mean(), count(), size(), min(), max(), std(), var() など)や agg() メソッドを呼び出すことで、はじめて集計計算が行われます。

agg() メソッドを使うと、1つまたは複数の列に対して、1つまたは複数の集計関数をまとめて適用できます。列ごとに異なる集計関数を適用したい場合は、辞書形式で { '列名': '関数名' } または { '列名': ['関数名1', '関数名2'] } のように指定します。

9. DataFrameの結合 (Merge, Join, Concat)

複数のDataFrameを組み合わせたい場合があります。Pandasには主に以下の方法があります。

  • pd.concat(): DataFrameを縦方向(行として追加)または横方向(列として追加)に連結します。
  • pd.merge(): データベースのJOIN操作のように、共通のキー(列)に基づいてDataFrameを結合します。
  • .join(): インデックスをキーとしてDataFrameを結合します(merge の特殊なケースのようなもの)。

9.1. 連結 (concat)

pd.concat() は、SeriesやDataFrameのリストを一つのDataFrameに結合します。デフォルトでは行方向(axis=0)に結合します。

“`python
df1_cat = pd.DataFrame({‘A’: [‘A0’, ‘A1’], ‘B’: [‘B0’, ‘B1’]}, index=[0, 1])
df2_cat = pd.DataFrame({‘A’: [‘A2’, ‘A3’], ‘B’: [‘B2’, ‘B3’]}, index=[2, 3])
df3_cat = pd.DataFrame({‘C’: [‘C4’, ‘C5’], ‘D’: [‘D4’, ‘D5’]}, index=[4, 5])

print(“\n— DataFrame Concatenation (concat) —“)
print(“\ndf1_cat:\n”, df1_cat)
print(“\ndf2_cat:\n”, df2_cat)
print(“\ndf3_cat:\n”, df3_cat)

縦方向(行)に結合 (axis=0, デフォルト)

result_rows = pd.concat([df1_cat, df2_cat])
print(“\nConcatenated vertically (df1_cat, df2_cat):”)
print(result_rows) # インデックスが重複することに注意

縦方向に結合し、元のDataFrameを示すキーを追加

result_rows_keys = pd.concat([df1_cat, df2_cat], keys=[‘df1’, ‘df2’])
print(“\nConcatenated vertically with keys:”)
print(result_rows_keys) # MultiIndexになる

横方向(列)に結合 (axis=1)

result_cols = pd.concat([df1_cat, df3_cat], axis=1)
print(“\nConcatenated horizontally (df1_cat, df3_cat):”)
print(result_cols) # インデックスに基づいて結合される

結合時のインデックスの扱い

インデックスを無視して結合 (新しい連番インデックスになる)

result_ignore_index = pd.concat([df1_cat, df2_cat], ignore_index=True)
print(“\nConcatenated vertically ignoring index:”)
print(result_ignore_index)

インデックスを基準に結合 (outer join: どちらかのインデックスにあれば含める)

result_cols_outer = pd.concat([df1_cat, df3_cat], axis=1, join=’outer’)

print(“\nConcatenated horizontally (outer join):”)

print(result_cols_outer)

インデックスを基準に結合 (inner join: 両方のインデックスに共通するもののみ含める)

df4_cat = pd.DataFrame({‘E’: [‘E0’, ‘E1’], ‘F’: [‘F0’, ‘F1’]}, index=[1, 2])

result_cols_inner = pd.concat([df1_cat, df4_cat], axis=1, join=’inner’)

print(“\nConcatenated horizontally (inner join):”)

print(result_cols_inner)

“`

concat はDataFrameを単純に積み重ねたり並べたりするのに適しています。ignore_index=True は、元のインデックスをリセットして新しい連番インデックスを割り振りたい場合に便利です。join 引数 ('outer' または 'inner') は、横方向に結合する際に、インデックスが完全に一致しない場合の挙動を制御します。

9.2. 結合 (merge)

pd.merge() は、共通の列(キー)を使って2つのDataFrameを結合します。データベースのJOIN操作と概念は同じです。結合方法(how 引数)として、'inner' (デフォルト), 'outer', 'left', 'right' が指定できます。

“`python
df_customers = pd.DataFrame({
‘customer_id’: [101, 102, 103, 104, 105],
‘name’: [‘Alice’, ‘Bob’, ‘Charlie’, ‘David’, ‘Eve’],
‘city’: [‘Tokyo’, ‘Osaka’, ‘Nagoya’, ‘Fukuoka’, ‘Sapporo’]
})

df_orders = pd.DataFrame({
‘customer_id’: [101, 102, 101, 103, 106, 104],
‘order_id’: [1001, 1002, 1003, 1004, 1005, 1006],
‘amount’: [1500, 2000, 1000, 3000, 500, 2500]
})

print(“\n— DataFrame Merging (merge) —“)
print(“\ndf_customers:\n”, df_customers)
print(“\ndf_orders:\n”, df_orders)

Inner Join (デフォルト): 共通のcustomer_idを持つ行のみを結合

merged_inner = pd.merge(df_customers, df_orders, on=’customer_id’, how=’inner’)
print(“\nInner Merge (on ‘customer_id’):”)
print(merged_inner) # customer_id 105 (df_customersのみ), 106 (df_ordersのみ) は含まれない

Outer Join: どちらかのDataFrameにある行はすべて含める (対応するデータがない場合はNaN)

merged_outer = pd.merge(df_customers, df_orders, on=’customer_id’, how=’outer’)
print(“\nOuter Merge (on ‘customer_id’):”)
print(merged_outer) # customer_id 105, 106 も含まれ、NaNで埋められる

Left Join: 左側のDataFrame (df_customers) のすべての行を含める

merged_left = pd.merge(df_customers, df_orders, on=’customer_id’, how=’left’)
print(“\nLeft Merge (on ‘customer_id’):”)
print(merged_left) # customer_id 105 は含まれ、order_id, amount は NaN

Right Join: 右側のDataFrame (df_orders) のすべての行を含める

merged_right = pd.merge(df_customers, df_orders, on=’customer_id’, how=’right’)
print(“\nRight Merge (on ‘customer_id’):”)
print(merged_right) # customer_id 106 は含まれ、name, city は NaN

結合キーが異なる列名の場合

df_other_key = pd.DataFrame({
‘c_id’: [101, 103, 104],
‘membership’: [‘Gold’, ‘Silver’, ‘Bronze’]
})
merged_keys = pd.merge(df_customers, df_other_key, left_on=’customer_id’, right_on=’c_id’)
print(“\nMerge with different key names (left_on, right_on):”)
print(merged_keys) # c_id 列も結果に含まれる

重複する列名がある場合のsuffixの指定

df_address = pd.DataFrame({
‘customer_id’: [101, 102, 103],
‘city’: [‘Kyoto’, ‘Sakai’, ‘Nara’] # ‘city’ 列名が重複
})
merged_suffix = pd.merge(df_customers, df_address, on=’customer_id’, how=’inner’, suffixes=(‘_customer’, ‘_address’))
print(“\nMerge with duplicate column names and suffixes:”)
print(merged_suffix)
“`

merge は、複数のテーブルをリレーショナルデータベースのように結合する際に非常に強力です。on 引数で共通の列名を指定し、how 引数で結合方法を選択します。結合キーの列名が異なる場合は、left_onright_on を使用します。同じ列名が複数存在する場合は、suffixes 引数で区別するための接尾辞を指定できます。

9.3. インデックスを使った結合 (join)

DataFrameの .join() メソッドは、デフォルトで行インデックスをキーとして他のDataFrameを結合します。これは pd.merge() の特殊なケース (left_index=True, right_index=True) と考えることもできます。

“`python
df_join1 = pd.DataFrame({‘A’: [1, 2], ‘B’: [3, 4]}, index=[‘a’, ‘b’])
df_join2 = pd.DataFrame({‘C’: [5, 6], ‘D’: [7, 8]}, index=[‘a’, ‘c’])

print(“\n— DataFrame Joining (.join) —“)
print(“\ndf_join1:\n”, df_join1)
print(“\ndf_join2:\n”, df_join2)

join (デフォルトは left join, インデックスをキーに)

joined_df = df_join1.join(df_join2)
print(“\nLeft Join using .join (index based):”)
print(joined_df) # インデックス ‘c’ は df_join1 にないので含まれず、NaN

outer join

joined_outer = df_join1.join(df_join2, how=’outer’)
print(“\nOuter Join using .join (index based):”)
print(joined_outer) # インデックス ‘c’ も含まれ、NaN

joinは複数のDataFrameを一度に結合できる

df_join3 = pd.DataFrame({‘E’: [9, 10]}, index=[‘a’, ‘b’])
joined_multi = df_join1.join([df_join2, df_join3])
print(“\nJoining multiple DataFrames using .join:”)
print(joined_multi)
“`

join は、インデックスをキーとする結合や、複数のDataFrameをまとめて結合したい場合に便利です。merge の方がより汎用的で、様々なキーや結合方法を明示的に指定できるため、一般的には merge を使うことが多いですが、インデックス結合の場合は join もよく使われます。

10. 簡単なデータ可視化

Pandasは、データ可視化ライブラリであるMatplotlibと連携しており、DataFrameやSeriesから直接グラフを描画するための簡単なメソッドを提供しています。これはデータの探索的分析(EDA: Exploratory Data Analysis)において、データの分布や関係性を素早く把握するのに役立ちます。

“`python

Matplotlibをインポート(グラフ描画に必要)

import matplotlib.pyplot as plt

グラフを表示するために必要な設定 (Jupyter環境以外の場合)

plt.show()

簡単なデータを作成

df_plot = pd.DataFrame({
‘category’: [‘A’, ‘B’, ‘A’, ‘C’, ‘B’, ‘C’, ‘A’],
‘value’: [10, 15, 12, 9, 20, 11, 13],
‘value2’: [5, 8, 6, 4, 10, 5, 7]
})

print(“\n— Basic Data Visualization —“)
print(“\nDataFrame for plotting:\n”, df_plot)

Seriesのヒストグラム

df_plot[‘value’].hist(bins=5)
plt.title(‘Histogram of Value’)
plt.xlabel(‘Value’)
plt.ylabel(‘Frequency’)
plt.show() # グラフを表示

DataFrameの折れ線グラフ (デフォルトでは各数値列をプロット)

df_plot[[‘value’, ‘value2′]].plot(kind=’line’, title=’Value Trends’)
plt.xlabel(‘Index’)
plt.ylabel(‘Value’)
plt.show()

散布図

df_plot.plot(kind=’scatter’, x=’value’, y=’value2′, title=’Value vs Value2 Scatter Plot’)
plt.xlabel(‘Value’)
plt.ylabel(‘Value2’)
plt.show()

箱ひげ図 (カテゴリごとの数値の分布を見るのに便利)

df_plot.boxplot(column=’value’, by=’category’)
plt.title(‘Box Plot of Value by Category’)
plt.suptitle(”) # デフォルトのタイトルを非表示にすることが多い
plt.xlabel(‘Category’)
plt.ylabel(‘Value’)
plt.show()

グループ化してプロット (例: categoryごとのvalueの合計を棒グラフで)

df_plot.groupby(‘category’)[‘value’].sum().plot(kind=’bar’, title=’Total Value by Category’)
plt.xlabel(‘Category’)
plt.ylabel(‘Total Value’)
plt.xticks(rotation=0) # X軸のラベルを回転しない
plt.show()
“`

Pandasのプロット機能はMatplotlibをベースにしているため、さらに細かいカスタマイズはMatplotlibの関数を使って行うことができます。df.plot()kind 引数で様々な種類のグラフを指定できます ('line', 'bar', 'hist', 'box', 'kde', 'area', 'pie', 'scatter')。

本格的な可視化には、Seabornのようなより高レベルなライブラリを利用することが多いですが、Pandas組み込みのプロット機能でもデータの概要を掴むには十分です。

11. さらに進むために

この入門記事では、Pandasの基本的な機能に焦点を当てました。Pandasにはここで紹介しきれなかった多くの強力な機能があります。

  • 時系列データ: 日付や時刻のデータを扱うための高度な機能(リサンプリング、タイムゾーン処理など)。
  • カテゴリ型データ: 離散的なカテゴリデータを効率的に扱うための機能。
  • ウィンドウ関数: 移動平均や累積合計などの計算。
  • ピボットテーブルとクロスタブ: Excelのピボットテーブルのような集計表の作成。
  • データの変換: melt, pivot, stack, unstack によるデータの整形。
  • メモリ使用量の最適化: 大規模データを扱う際のパフォーマンス向上。

これらの機能についても学習することで、より複雑なデータ処理・分析タスクに対応できるようになります。

12. まとめ

Pandasは、Pythonでデータ分析を行う上で最も重要なライブラリの一つです。この入門記事を通じて、以下の基本を学びました。

  • Pandasの2つの主要なデータ構造:SeriesとDataFrame
  • 外部ファイルからのデータ読み込みと保存
  • DataFrameの構造の確認
  • データの選択、フィルタリング、抽出の方法([], .loc, .iloc, Boolean Indexing)
  • 欠損値や重複データの検出と処理
  • データの変換と操作(列の追加・削除、apply, replace
  • データのグループ化と集計 (groupby, agg)
  • 複数のDataFrameの結合 (concat, merge, join)
  • 簡単なデータ可視化

これらの基本操作を習得すれば、様々な形式のデータを読み込み、整形し、必要な情報を取り出し、集計するといった、データ分析の最初のステップを自信を持って進めることができるようになります。

データ分析は実践が重要です。ぜひ、公開されているデータセット(Kaggleや政府の統計データなど)を使って、ここで学んだPandasの知識を実際に試してみてください。

13. 参考資料

  • Pandas公式ドキュメント: 最も正確で詳細な情報源です。最初は難しく感じるかもしれませんが、困ったときに参照すると非常に役立ちます。https://pandas.pydata.org/docs/
  • Python Data Science Handbook: Pandasの章が非常に分かりやすく、実践的な例が豊富です。https://jakevdp.github.io/PythonDataScienceHandbook/ (英語版)
  • Wes McKinney, Python for Data Analysis: Pandasの作者による書籍です。理論的な背景も含めて深く理解したい場合におすすめです。

この入門記事が、あなたのPandasを使ったデータ分析の旅の良い出発点となることを願っています。Happy Coding!


コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール