【Python】pandasのDataFrameとSeriesの違いや扱い方を学ぶ

Pythonのデータ処理の定番ライブラリであるpandasで、最も基本的なデータ構造がDataFrameとSeriesになります。

今回は、PandasにおけるDataFrameとSeriesの違いについて解説し、基本的な操作方法をまとめます。

本記事の内容

  • DataFrameとSeriesの違い
  • DataFrameの作成と基本操作
  • Seriesの作成と基本操作
  • データの取得・フィルタリング

DataFrameとSeriesの違い

DataFrameとSeriesの違いを一言で言うと、DataFrameは2次元のテーブル構造で、Seriesは1次元のラベル付き配列です。

何かしらのテーブル構造のデータがあった際に、DataFrameはデータ全体を、Seriesはデータの1列もしくは1行を表します。

特徴 DataFrame Series
次元 2次元 1次元
構造 行 × 列のテーブル ラベル付き配列
取得 df['列名'] で Series を取得 インデックスで要素を取得

DataFrameの作成

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

辞書からの作成

import pandas as pd
import numpy as np

# 辞書からDataFrameを作成
data = {
    'name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'],
    'age': [25, 30, 35, 28, 32],
    'score': [85.5, 92.0, 78.3, 95.1, 88.7],
    'city': ['Tokyo', 'Osaka', 'Tokyo', 'Nagoya', 'Osaka']
}
df = pd.DataFrame(data)
print(df)
print(f"\nshape: {df.shape}")
print(f"dtypes:\n{df.dtypes}")

NumPy配列からの作成

import pandas as pd
import numpy as np

# NumPy配列からDataFrameを作成
arr = np.random.randn(5, 3)
df = pd.DataFrame(arr, columns=['A', 'B', 'C'], index=['row1', 'row2', 'row3', 'row4', 'row5'])
print(df)

DataFrameの基本操作

データの概要確認

import pandas as pd
import numpy as np

data = {
    'name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'],
    'age': [25, 30, 35, 28, 32],
    'score': [85.5, 92.0, 78.3, 95.1, 88.7],
    'city': ['Tokyo', 'Osaka', 'Tokyo', 'Nagoya', 'Osaka']
}
df = pd.DataFrame(data)

# 先頭/末尾の確認
print("=== head(3) ===")
print(df.head(3))

# 基本統計量
print("\n=== describe() ===")
print(df.describe())

# データ型とメモリ使用量
print("\n=== info() ===")
df.info()

列の操作

import pandas as pd

data = {
    'name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'],
    'age': [25, 30, 35, 28, 32],
    'score': [85.5, 92.0, 78.3, 95.1, 88.7],
}
df = pd.DataFrame(data)

# 列の追加
df['grade'] = ['A', 'A+', 'B', 'A+', 'A']
print("列の追加:")
print(df)

# 計算による列の追加
df['score_normalized'] = (df['score'] - df['score'].min()) / (df['score'].max() - df['score'].min())
print("\n計算列の追加:")
print(df)

# 列の削除
df_dropped = df.drop(columns=['grade'])
print("\n列の削除後:")
print(df_dropped)

データのフィルタリング

import pandas as pd

data = {
    'name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'],
    'age': [25, 30, 35, 28, 32],
    'score': [85.5, 92.0, 78.3, 95.1, 88.7],
    'city': ['Tokyo', 'Osaka', 'Tokyo', 'Nagoya', 'Osaka']
}
df = pd.DataFrame(data)

# 条件でフィルタリング
print("age > 28:")
print(df[df['age'] > 28])

# 複数条件(AND)
print("\nage > 25 AND score > 85:")
print(df[(df['age'] > 25) & (df['score'] > 85)])

# 特定の値でフィルタリング
print("\ncity == 'Tokyo':")
print(df[df['city'] == 'Tokyo'])

# isinによるフィルタリング
print("\ncity in ['Tokyo', 'Osaka']:")
print(df[df['city'].isin(['Tokyo', 'Osaka'])])

Seriesの作成と操作

SeriesはDataFrameの1列に相当する1次元データ構造です。

import pandas as pd
import numpy as np

# リストからSeriesを作成
s = pd.Series([10, 20, 30, 40, 50], index=['a', 'b', 'c', 'd', 'e'], name='values')
print("Series:")
print(s)
print(f"\ndtype: {s.dtype}")
print(f"index: {s.index.tolist()}")
print(f"name: {s.name}")

DataFrameからSeriesを取得

import pandas as pd

data = {
    'name': ['Alice', 'Bob', 'Charlie'],
    'age': [25, 30, 35],
    'score': [85.5, 92.0, 78.3],
}
df = pd.DataFrame(data)

# 列名で取得するとSeriesになる
age_series = df['age']
print(f"type: {type(age_series)}")
print(age_series)

# 複数列を指定するとDataFrameになる
subset = df[['name', 'age']]
print(f"\ntype: {type(subset)}")
print(subset)

Seriesの演算

import pandas as pd
import numpy as np

s1 = pd.Series([1, 2, 3, 4, 5])
s2 = pd.Series([10, 20, 30, 40, 50])

# 要素ごとの演算
print("加算:", (s1 + s2).tolist())
print("乗算:", (s1 * s2).tolist())

# 集約関数
print(f"\n合計: {s1.sum()}")
print(f"平均: {s1.mean()}")
print(f"標準偏差: {s1.std():.4f}")
print(f"最大値: {s1.max()}, 最小値: {s1.min()}")

# NumPyの関数も適用可能
print(f"\nnp.sqrt: {np.sqrt(s2).tolist()}")

locとilocによるデータアクセス

DataFrameのデータにアクセスする方法として、loc(ラベルベース)と iloc(位置ベース)があります。

import pandas as pd

data = {
    'name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'],
    'age': [25, 30, 35, 28, 32],
    'score': [85.5, 92.0, 78.3, 95.1, 88.7],
}
df = pd.DataFrame(data, index=['p1', 'p2', 'p3', 'p4', 'p5'])

# loc: ラベルで指定
print("loc['p2', 'name']:", df.loc['p2', 'name'])
print("\nloc['p1':'p3', 'name':'age']:")
print(df.loc['p1':'p3', 'name':'age'])

# iloc: 位置(整数)で指定
print("\niloc[1, 0]:", df.iloc[1, 0])
print("\niloc[0:3, 0:2]:")
print(df.iloc[0:3, 0:2])

loc はラベル(インデックスや列名)でアクセスし、スライスの終端を含みますiloc は整数位置でアクセスし、スライスの終端を含みません(Pythonの標準的なスライスと同じ)。

実践: データの集計

実際のデータ分析でよく使うグループ化と集計を試してみましょう。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

np.random.seed(42)

# サンプルデータの生成
n = 100
df = pd.DataFrame({
    'department': np.random.choice(['Engineering', 'Sales', 'Marketing', 'HR'], n),
    'experience': np.random.randint(1, 20, n),
    'salary': np.random.normal(500, 100, n).astype(int),
    'performance': np.random.choice(['A', 'B', 'C'], n, p=[0.3, 0.5, 0.2])
})

# グループ化と集計
summary = df.groupby('department').agg(
    count=('salary', 'count'),
    mean_salary=('salary', 'mean'),
    mean_experience=('experience', 'mean'),
).round(1)
print("=== Department Summary ===")
print(summary)

# 可視化
fig, axes = plt.subplots(1, 2, figsize=(12, 4))

summary['mean_salary'].plot(kind='bar', ax=axes[0], color='steelblue', alpha=0.8)
axes[0].set_title("Mean Salary by Department")
axes[0].set_ylabel("Salary")
axes[0].tick_params(axis='x', rotation=45)
axes[0].grid(True, alpha=0.3, axis='y')

df.groupby('department')['performance'].value_counts().unstack().plot(
    kind='bar', ax=axes[1], alpha=0.8)
axes[1].set_title("Performance Distribution by Department")
axes[1].set_ylabel("Count")
axes[1].tick_params(axis='x', rotation=45)
axes[1].legend(title='Performance')
axes[1].grid(True, alpha=0.3, axis='y')

plt.tight_layout()
plt.show()

まとめ

本記事では、pandasのDataFrameとSeriesの違いと基本的な操作方法を解説しました。

  • DataFrameは2次元のテーブル構造、Seriesは1次元のラベル付き配列
  • DataFrameから列を1つ取得するとSeriesになる
  • locはラベルベース、ilocは位置ベースのデータアクセス
  • フィルタリングはブーリアンインデックスで直感的に行える
  • groupbyagg で柔軟なグループ化・集計が可能