2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

データサイエンティストとしてPythonライブラリ中級

2
Posted at

データ読み込み

共通処理(第3部で共通のインポートと設定)

  • データ分析で繰り返し使うライブラリのインポートと表示設定を最初にまとめて行う。
# 日本語化ライブラリ導入
!pip install japanize-matplotlib | tail -n 1

# ライブラリのimport
import numpy as np
import matplotlib.pyplot as plt
import japanize_matplotlib
import pandas as pd
from IPython.display import display

# 表示オプション調整
np.set_printoptions(
    suppress=True, precision=4, floatmode='fixed'
)
plt.rcParams["font.size"] = 14
plt.rcParams['figure.figsize'] = (6, 6)
plt.rcParams['axes.grid'] = True
pd.options.display.float_format = '{:.4f}'.format
pd.set_option("display.max_columns", None)

CSVファイルの読み込み

  • データの内容を事前に !head コマンドで確認し、適切なオプションを判断する。
url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/bridges/bridges.data.version1'

# ファイルダウンロードと内容確認
!wget -nc $url
!head -2 bridges.data.version1
  • ヘッダ行がない場合は header=Nonenames で列名を指定する。
columns = [
    'ID', 'RIVER', 'LOCATION', 'ERECTED', 'PURPOSE',
    'LENGTH', 'LANES', 'CLEAR-G', 'T-OR-D', 'MATERIAL',
    'SPAN', 'REL-L', 'TYPE'
]

df1 = pd.read_csv(
    url, header=None, names=columns)
display(df1.head())
  • na_values オプションで特定の文字列(例:?)を欠損値として扱う。
df2 = pd.read_csv(
    url, na_values='?', header=None,
    names=columns)
display(df2.head())
  • index_col オプションで特定の列を行インデックスに指定する。
df3 = pd.read_csv(
    url, na_values='?', header=None,
    names=columns, index_col='ID')
display(df3.head())
  • 区切り文字がセミコロンの場合は sep=';' を指定する。true_values / false_values で yes/no をブール値に変換する。
df = pd.read_csv(
    fn,
    sep=';',
    na_values='unknown',
    true_values=['yes'],
    false_values=['no'])
display(df.head(2))
  • TSVファイル(タブ区切り)の読み込みは sep='\t' を使う(演習問題)。

CSV・Excelファイルへの出力

  • to_csv でCSVファイルとして保存する。index=False で行インデックスを出力しない。
# 行インデックスをファイルに含めない場合
df2.to_csv('bridge2.csv', index=False)

# 行インデックスをファイルに含める場合(index_colで読み込んだ場合)
df3.to_csv('bridge3.csv')
  • to_excel でExcelファイルとして保存する。
df2.to_excel('bridge2.xlsx', index=False)

データ前処理

項目名の変更

  • columns 属性に新しい項目名リストを代入することで列名を一括変更する。
  • copy() でオリジナルデータに影響を与えないようにする。
cols_jp = [
    '川コード', '位置', '竣工年', '目的', '長さ', '車線数',
    '垂直クリアランス', '道路位置', '建築資材', '長さ区分', '相対長', '橋種別'
]
df2 = df1.copy()
df2.columns = cols_jp
display(df2.head())

欠損値の確認

  • isnull() で各要素がNULLかどうかを判定する(True/False)。
  • isnull().sum() で項目ごとの欠損値の件数を確認する(重要パターン)。
# 各要素ごとのNULL判定
df2.isnull()

# 項目ごとの欠損値件数(メソッドチェイン)
df2.isnull().sum()

欠損値の除去

  • dropna(subset=[...]) で指定した列に欠損値がある行を削除する。
df3 = df2.copy()
df3 = df3.dropna(subset=['位置'])
df3.isnull().sum()

データ型の確認と変換

  • dtypes で各列のデータ型を確認する。
  • 本来整数型の列が浮動小数点型になっている場合は、欠損値の存在を疑う。
  • astype() でデータ型を変換する(欠損値を除去してから行う必要がある)。
# データ型確認
df3.dtypes

# 「位置」のデータ型を整数型に変換(欠損値除去済みが前提)
df3['位置'] = df3['位置'].astype('int')
print(df3.dtypes)

統計量の計算 (describeメソッド)

  • describe() で数値型項目の基本統計量(件数、平均、標準偏差、最小値、四分位数、最大値)を取得する。
  • describe(include='O') で文字列型(object型)項目の統計量(件数、ユニーク数、最頻値、頻度)を取得する。
# 数値項目の統計量
df2.describe()

# 文字列型項目の統計量
df2.describe(include='O')

値の出現回数 (value_countsメソッド)

  • value_counts() で特定の列の値ごとの出現回数をカウントする。
df2['建築資材'].value_counts()

特徴量計算 (mapメソッド)

  • 変換用の関数を定義し、map() メソッドで全行に適用して新しい列を作成する。
# 西暦年から竣工年区分を返す関数
def get_year_cd(x):
    thres = [1850, 1900, 1950]
    thres_np = np.array(thres)
    return (thres_np < x).sum() + 1

# map関数ですべての行に適用
df3['竣工年区分'] = df3['竣工年'].map(get_year_cd)
display(df3.head(2))
display(df3.tail(4))

データ集計

項目の順番入れ替え

  • 列名リストの操作で項目の順番を変更し、データフレームに適用する。
columns1 = list(df2.columns)
# 最後の項目を先頭に移動し、後ろの9項目を削除
columns2 = columns1[-1:] + columns1[:-9]
df2 = df2[columns2]

グループごとの集計 (groupbyメソッド)

  • groupby() で特定の列をキーにグループ化し、集約関数(mean, sum など)を適用する。
  • sort_values() でソートを組み合わせる。
# 学歴による集計(平均値)
df_gr1 = df2.groupby('学歴').mean()
display(df_gr1)

# 職業による集計(平均値)、特定列で降順ソート
df_gr2 = df2.groupby('職業').mean(
).sort_values('今回販促結果', ascending=False)
display(df_gr2)

出現頻度のクロス集計 (crosstabメソッド)

  • pd.crosstab() で2軸の出現頻度を集計する。margins=True で合計行・列を追加する。
  • normalize='index' で行方向の比率計算を行う。
# 頻度集計
df_crosstab = pd.crosstab(
    index=df2['職業'],
    columns=df2['学歴'],
    margins=True)
display(df_crosstab)

# 行方向の比率計算
df_crosstab2 = pd.crosstab(
    index=df2['職業'],
    columns=df2['学歴'],
    normalize='index',
    margins=True)
display(df_crosstab2)

項目値のクロス集計 (pivot_tableメソッド)

  • pivot_table() で2軸を指定し、特定の値について集約(mean, sum など)を行う。
df_pivot = df2.pivot_table(
    index='職業',
    columns='学歴',
    values='今回販促結果',
    aggfunc='mean')
display(df_pivot)

データ可視化

ヒストグラム (histメソッド)

  • df.hist() でデータフレーム内の数値型項目のヒストグラムを一括表示する。
  • bins でビン数、layout でレイアウトを指定する。
# 簡易版
df.hist()
plt.show()

# レイアウトを調整した版
plt.rcParams['figure.figsize'] = (15, 4)
df.hist(bins=20, layout=(1, 4))
plt.tight_layout()
plt.show()

棒グラフによる頻度表示 (plotメソッド)

  • value_counts().plot(kind='bar') でカテゴリ項目の頻度を棒グラフで表示する。
plt.rcParams['figure.figsize'] = (4, 4)
c = '長さ区分'
df[c].value_counts().plot(kind='bar', title=c)
plt.show()
  • dtypes == object で文字列型の列だけを抽出し、ループで全カテゴリ項目の棒グラフを一括表示する。
# データ型がobjectの項目のみ抽出
col = df.columns
col2 = col[df.dtypes == object]
df2 = df[col2]

# ループで全カテゴリ項目の棒グラフ表示
plt.rcParams['figure.figsize'] = (12, 8)
for i, c in enumerate(df2.columns):
    ax = plt.subplot(2, 4, i+1)
    df2[c].value_counts().plot(
        kind='bar', title=c, ax=ax)
plt.tight_layout()
plt.show()

箱ひげ図 (seaborn boxplot関数)

  • seaborn ライブラリの boxplot() でカテゴリ別の分布を可視化する。
  • order で表示順を指定する。
import seaborn as sns

plt.rcParams['figure.figsize'] = (6, 6)
sns.boxplot(
    x='建築資材', y='竣工年', data=df,
    order=['WOOD', 'IRON', 'STEEL'])
plt.title('建築資材と竣工年の関係')
plt.show()

散布図 (seaborn scatterplot関数)

  • sns.scatterplot()hue による色分け、style によるマーカー変更、s でサイズ指定ができる。
plt.rcParams['figure.figsize'] = (6, 6)
sns.scatterplot(
    x='竣工年', y='長さ', data=df, hue='建築資材',
    hue_order=['WOOD', 'IRON', 'STEEL'],
    s=150, style='建築資材')
plt.title('竣工年と長さの関係')
plt.show()

ヒストグラム (seaborn histplot関数)

  • sns.histplot()hue によるカテゴリ分け付きヒストグラムを描画する。
  • multiple='dodge' で並列表示、shrink でバーの幅を調整する。
plt.rcParams['figure.figsize'] = (8, 4)
sns.histplot(
    data=df, x='竣工年', hue='建築資材',
    palette=['blue', 'cyan', 'grey'], multiple='dodge',
    shrink=0.7)
plt.title('竣工年による建築資材の推移')
plt.show()

ヒートマップ (seaborn heatmap関数)

  • クロス集計表を sns.heatmap() で可視化する。
  • square=True で正方形、annot=True で数値表示、fmt='d' で整数表記にする。
# クロス集計表の生成
pv = pd.crosstab(
    index=df['長さ区分'], columns=df['橋種別'])
display(pv)

# ヒートマップ表示
plt.rcParams['figure.figsize'] = (6, 6)
sns.heatmap(
    pv, square=True, annot=True,
    fmt='d', cmap='Blues', cbar=False)
xlabel = pv.columns.name
ylabel = pv.index.name
plt.title(xlabel + ' vs ' + ylabel)
plt.xlabel(xlabel)
plt.ylabel(ylabel)
plt.show()

データ検索・結合

ソート (sort_valuesメソッド)

  • sort_values() で特定の列を基準にデータフレームをソートする。
  • ascending=False で降順ソートにする。
  • inplace=True で元のデータフレームを直接変更する。
# 貸出日時で昇順ソート
df3 = df1.sort_values('貸出日時')
display(df3.head())

# 降順ソート(inplaceで直接変更)
df11.sort_values('貸出日時', inplace=True)

インデックスの初期化 (reset_indexメソッド)

  • ソートや検索後にインデックスを振り直すには reset_index(drop=True) を使う。
  • drop=True を指定しないと、旧インデックスがデータ列として残る。
# ソート後のインデックス初期化
df4 = df3.reset_index(drop=True)
display(df4.head())
  • groupby 後のインデックスを列に戻すには reset_index() を使う(drop なし)。
# カテゴリ別の売上集計
df5 = df4[['カテゴリ名', 'レンタル代']]
df6 = df5.groupby('カテゴリ名').sum()

# カテゴリ名をインデックスから列に戻す
df7 = df6.reset_index()

# 売上の多い順にソート
df8 = df7.sort_values('レンタル代', ascending=False)
display(df8.head(2))

特定の行参照 (loc / iloc)

  • loc はインデックスのラベル値で参照する。行と列のラベルを同時に指定できる。
# 1行全体の参照(ラベルが1の行)
print(df3.loc[1])

# 行ラベルと列名の同時指定
print(df3.loc[1, '貸出日時'])
  • iloc は位置(整数インデックス)で参照する。NumPy的なアクセスが可能。
# 位置1(0始まりで2番目)の行全体
print(df3.iloc[1])

# 位置1の行、位置8の列
print(df3.iloc[1, 8])
  • iloc を使ったループ処理で各行のデータを辞書的にアクセスする。
for i in range(5):
    x = df3.iloc[i]
    rental = x['貸出ID']
    customer = x['顧客ID']
    title = x['タイトル']
    amount = x['レンタル代']
    rental_date = x['貸出日時']
    print(i, rental, customer, title, amount, rental_date)

検索 (queryメソッド)

  • query() で条件式を文字列として渡し、データを絞り込む。
# 単純な検索
x1 = df4.query('顧客ID == 459')
print(len(x1))
display(x1.head())
  • and / or で複数条件を組み合わせた複合検索ができる。
x2 = df4.query('顧客ID == 459 and レンタル代 >= 4.0')
print(len(x2))
display(x2.head())
  • Python変数の値を検索条件に使うには @変数名 と記述する。
cust_id = df4.iloc[1]['顧客ID']

# @変数名で Python変数を参照
x3 = df4.query('顧客ID == @cust_id')
display(x3.head())
  • リスト変数を使って in 条件で複数値にマッチさせる。
cust_ids = list(df4.iloc[:3]['顧客ID'])

# in @変数名 でリスト変数を参照
x4 = df4.query('顧客ID in @cust_ids')
display(x4.head())

結合 (merge関数)

  • pd.merge() で共通のキー列を基に2つのデータフレームを結合する(SQLのJOINに相当)。
# 貸出情報から必要な項目のみ抽出
df9 = df4[['貸出ID', '顧客ID', '貸出日時', 'タイトル']]

# 顧客情報から必要な項目のみ抽出
df10 = df2[['顧客ID', '', '']]

# 顧客IDをキーに結合
df11 = pd.merge(df9, df10, on='顧客ID')

# 結合結果をソート
df11.sort_values('貸出日時', inplace=True)
display(df11.head())

日付データの処理

read_csvのparse_datesオプション

  • parse_dates で指定した列を自動的にdatetime型に変換する。
url1 = 'https://github.com/makaishi2/samples/raw/main/data/rental5-jp.csv'

# 5番目の列(0始まり4番目)をdatetime型で読み込む
df1 = pd.read_csv(url1, parse_dates=[4])

# ソートとインデックス初期化
df2 = df1.sort_values('貸出日時')
df2 = df2.reset_index(drop=True)

# データ型確認(貸出日時がdatetime64[ns]になる)
print(df2.dtypes)
display(df2.head(2))

日付集計用の項目追加

  • datetime モジュールと map メソッドを使い、日付を「日単位」「週単位」に変換する関数を定義して新しい列を追加する。
from datetime import datetime

# 週単位の日付作成
def conv_week_day(ts):
    year, week, day = ts.isocalendar()
    str = f'{year} {week} 1'
    return datetime.strptime(str, "%Y %W %w")

# 日単位の日付作成
def conv_date(ts):
    str = ts.isoformat()
    return datetime.strptime(str[:10], '%Y-%m-%d')

df3 = df2.copy()
# insert メソッドで指定位置に列を追加
df3.insert(4, '貸出日', df3['貸出日時'].map(conv_date))
df3.insert(5, '貸出週', df3['貸出日時'].map(conv_week_day))
display(df3.head(2))
  • 月単位・年単位への変換関数も同様に定義できる。
# 月単位の日付作成
def conv_month(ts):
    str = ts.isoformat()
    return datetime.strptime(str[:7], '%Y-%m')

# 年単位の日付作成
def conv_year(ts):
    str = ts.isoformat()
    return datetime.strptime(str[:4], "%Y")

週単位の集計

  • groupbysum で日付単位の集計を行う。
df4 = df3.groupby('貸出週')['レンタル代'].sum()
display(df4)
  • pd.date_range() で一定間隔の日付インデックスを生成する。
  • 空のSeries変数を用意し、集計結果をマッピングする。
# 1週間単位のインデックス作成
date_index = pd.date_range(
    "2005-05-23", periods=14, freq="W-MON")
print(date_index)

# 空の集計表作成
rent_fare = pd.Series(0, index=date_index)

# 売上集計
for ts in df4.index:
    rent_fare[ts] += df4[ts]
print(rent_fare)

集計結果の可視化

  • Series.plot(kind='bar') で棒グラフを描画する。
plt.rcParams['figure.figsize'] = (6, 6)
rent_fare.plot(kind='bar')
plt.title('週単位の売上合計')
plt.show()

日付範囲の指定による検索

  • pd.to_datetime() で文字列をdatetime型に変換し、query メソッドの条件に使う。
# 開始日・終了日をdatetime型で定義
sday = pd.to_datetime('2005-06-11')
eday = pd.to_datetime('2005-06-18')
uid = 459

# queryメソッドで日付範囲指定の検索
x2 = df3.query(
    '顧客ID == @uid and 貸出日 >=@sday and 貸出日 <= @eday')
display(x2)

日付への加減算 (relativedelta関数)

  • dateutil.relativedelta で日付の加減算を行う(日、週、月、年単位)。
from dateutil.relativedelta import relativedelta

# 基準日付
t1 = x2['貸出日'].iloc[0]

# 4日後
ts = t1 + relativedelta(days=4)

# 14日後
te = t1 + relativedelta(days=14)

print(t1, type(t1))
print(ts, type(ts))
print(te, type(te))
  • 計算結果をそのまま query メソッドの検索条件に使える。
x3 = df3.query('顧客ID == @uid and 貸出日 >=@ts and 貸出日 <= @te')
display(x3)
2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?