Pandas
DataFrame 提供するライブラリ
型
Series
: 列の型(要素の名前付きベクター)DataFrame
: Seriesを列とするテーブル
データフレームのメタデータの取得
df.columns # 列名(=文字列型の列インデックス)
df.index # 行インデックス
df.values # データフレームの値ををNumPy配列(ndarray)として取り出す
df.dtypes # 各列の型を返す
メソッドを繋げる
DataFrameを何回も再帰代入するより、メソッドを繋いだほうが書きやすいし読みやすい。 カッコかバックスラッシュを使えばドット前に改行やスペースを入れて整形可能。
(iris.query('species != "setosa"')
.filter(regex='^(?!sepal)')
.assign(petal_area=lambda x: x['petal_length'] * x['petal_width'] * 0.5)
.groupby('species')
.aggregate(np.mean)
.sort_values(['petal_length'], ascending=False)
)
ファイルの読み書き
import pandas as pd
# 読み込み
df = pd.read_excel("test.xlsx", sheet_name="mysheet", skiprows = 1)
# 他にもいろいろある
# pd.read_csv()
# pd.read_parquet()
# pd.read_feather()
# 書き出し
df.to_excel(filepath, index=False)
# df.to_csv()
# df.to_parquet()
# df.to_feather()
列の選択
# df[]の返値はSeries
# 列名での列の抽出
col = df.loc["col1"]
# 列番号での列の抽出
col = df.iloc[:, 1]
# df[[]]での返値はデータフレーム
df2 = df[["col1", "col2"]]
# 複数列を抽出(特定の列番号リストで指定)
df2 = df.iloc[:, [0, 2]] # A列とC列を抽出
# 複数列を抽出(列の範囲を指定)
df2 = df.iloc[:, 0:5] # A列~D列を抽出 0:5は[0,1,2,3,4]と同義
# 特定の文字列を含む列を抽出
df.filter(like='得点', axis=1)
# 列名を正規表現で抽出
df.filter(regex='^点数', axis=1)
# 数値型の列だけを抽出
df.select_dtypes(include='number')
# 文字列型の列だけを抽出
df.select_dtypes(include='object')
行の選択
行インデックスや行番号での指定
DataFrameの「行インデックス」と「行番号」は異なる。行番号は先頭行を0番とした連番だが、行インデックスには「文字列や数値・日付など」を自由に付けらえる。なので3番目の行の行インデックスが0であるかもしれない。
# 行インデックスで行を取得
df.loc[1000] # インデックスが1000の行
df.loc["A"] # インデックスがAの行
# 行番号で行を取得
df.iloc[1000] # 0から始まる行番号が1000番目の行
# 範囲指定もできる
df.loc[1:10] # 行インデックスが1~9
df.iloc[1:10] # 行番号が1~9
# 行番号と列番号の範囲指定で部分データフレーム
df2 = df.iloc[1:5, 6:10] # 行番号1~4, 列番号6~9の範囲を取得
# 列番号の範囲指定
df2 = df.iloc[:, 6:10]
# 行番号の範囲指定
df2 = df.iloc[1:5, :] # df.iloc[1:5] と同義
先頭行、末尾行の取得
df.head(10)
df.tail(10)
特定の列の値で行を抽出
数値型の列の例
# 数値がある値より大きい
ddf_high_score f2 = df[df['点数'] > 80]
# 数値がある値の範囲
df_age_range = df[(df['年齢'] >= 30) & (df['年齢'] < 40)]
# 数値が特定のリストに含まれる
df_selected_scores = df[df['点数'].isin([70, 90])]
# ある列に欠損値を含まない行
df_notna = df[df['年齢'].notna()]
# 特定条件に合致する行のインデックスのみを取得する場合
index_high_score = df[df['点数'] > 80].index
文字列型の列の例
# 特定の文字列と一致する行
df_sales = df[df['部署'] == '営業']
# 複数の文字列のどれかと一致する行
df_selected = df[df['部署'].isin(['営業', '人事'])]
# 特定の文字列を含んでいる行
df_tanaka = df[df['氏名'].str.contains('田中', na=False)]
# 特定の文字列を含んでいる行(アルファベットで大文字小文字を区別しない)
df_ignore_case = df[df['name'].str.contains('TARO', case=False, na=False)]
# 特定の文字列を含まない行
df_not_tanaka = df[~df['氏名'].str.contains('田中', na=False)]
# 特定の文字列で始まる行
df_start_tanaka = df[df['氏名'].str.startswith('田中', na=False)]
# 特定の文字列で終わる行
df_rou = df[df['氏名'].str.endswith('郎', na=False)]
重複行の削除
df_nodup = df.drop_duplicates()
単一要素の取得
# fast scalar (single value) lookup
df.at[0,'col'] # 行番号(行インデックス?)と列名での単一要素の取り出し
df.iat[0,1] # 行番号と列番号で単一要素の取り出し
列の加工
既存の列を加工して新しい列を追加する。
# assign()とlambdaの組み合わせ
# xはdf全体を意味する
import pandas as pd
df = pd.DataFrame({
'price': [100, 200, 300],
'quantity': [1, 3, 2]
})
# よくあるやり方
df['total'] = df['price'] + df['quantity']
# assign() & lambdaを使った方法
# xはdf全体を意味する
df = (
df
.assign(
total=lambda x: x['price'] * x['quantity'],
price_with_tax=lambda x: x['price'] * 1.10
)
)
集計
groupby()
import pandas as pd
df = pd.DataFrame({
'category': ['A', 'B', 'A', 'B', 'A'],
'price': [100, 200, 150, 250, 120],
'quantity': [1, 2, 1, 1, 3]
})
# 基本はこの形で覚える
result = (
df
# category列の値でグループ化
.groupby('category', as_index=False)
# as_index=False だと結果に category 列を含める
# as_index=True だと category の値が行インデックスになる
# グループごとに集計
.agg(
# price列の meanを計算して、price_mean列を作成
price_mean=('price', 'mean'),
price_max=('price', 'max'),
total_quantity=('quantity', 'sum')
)
)
# この形だと、結果の行('category')と列('price', 'quantity')にインデックスが付与される
result = (
df
.groupby('category')
.agg({
'price': 'sum',
'quantity': 'sum'
})
)
# 複数の関数を適用することもできる
# ただし結果が列名ではなく、マルチインデックス(入れ子になったインデックス)になるのでわかりにくい
result = (
df
.groupby('category')
.agg({
'price': ['mean', 'sum', 'max'],
'quantity': ['sum', 'count']
})
)
関数名 | 説明 |
---|---|
'sum' |
合計 |
'mean' |
平均 |
'median' |
中央値 |
'max' |
最大値 |
'min' |
最小値 |
'count' |
件数(NaNを除く) |
'size' |
件数(NaN含む、countとの違い) |
'std' |
標準偏差 |
'var' |
分散 |
'first' |
最初の値 |
'last' |
最後の値 |
'nunique' |
重複を除いた件数 |
'any' |
真偽値でいずれかがTrue |
'all' |
真偽値で全てTrue |
データの変形
https://pandas.pydata.org/pandas-docs/stable/user_guide/reshaping.html
df.melt(), df.pivot()
df.melt(id_vars=None, value_vars=None, var_name=None, value_name='value', ...)
縦長に変形。Rでいう tidyr::pivot_longer()
。
df.pivot(index=None, columns=None, values=None)
横長に変形。Rでいう tidyr::pivot_wider()
。
df.pivot_table()
インデックス
# 指定した列を**インデックス(行ラベル)**に変換します。
# # inplace=Trueを指定すると元のデータフレームに対して直接変更されます。
df.set_index('列名', inplace=False)
# 現在のインデックスを通常の列として戻し、デフォルトの数値インデックスに戻します。
# drop=Trueを指定すると、インデックス列をデータフレームに戻さず削除します。
# inplace=Trueを使えば元のDataFrameを直接変更します。
df.reset_index(inplace=False, drop=False)
Pandas.Series
名前付きベクトルのようなもの、データフレームの列や行をSeriesとして取り出すこともできる。
s = pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'])
print(s['a']) # 出力: 1
print(s[0]) # 出力: 1
# 値で抽出
print(s[s > 2])
# 計算集計
print(s.sum()) # 出力: 10
print(s.mean()) # 出力: 2.5
BigQuery との連携
import pandas_gbq
df = pandas_gbq.read_gbq("SELECT my_col FROM `my_project.my_dataset.my_table`")
初回に実行した時にブラウザが開いてアクセスコードの入力が求められる
ユーザーごとのアクセス情報は以下に保存される。
- Windows
%APPDATA%\pandas_gbq\bigquery_credentials.dat
- Linux/Mac/Unix
~/.config/pandas_gbq/bigquery_credentials.dat
これらのパスをLinux/Mac/Windowsの環境変数として保存しておく
GOOGLE_APPLICATION_CREDENTIALS