この記事はデータサイエンスを勉強しながら、データサイエンス協会が提供する__データサイエンス100本ノック(構造化データ加工編)__を解く過程を自分用にまとめたものです。
| チャプター | リンク | チャプター | リンク |
|---|---|---|---|
| P-001~P-016 | part1 | P-052~P-062 | part6 |
| P-017~P-022 | part2 | P-063~P-068 | part7 |
| P-023~P-031 | part3 | P-069~P-078 | part8 |
| P-032~P-039 | part4 | P-079~P-088 | part9 |
| P-040~P-051 | part5 | P-089~P-100 | part10 |
groupby()agg()- 最大値
max() - 最小値
min() - 平均値
mean() - 中央値
median() - 最頻値
mode() apply()- 分散
var() - 標準偏差
std()
P-023 groupby(), agg()
P-023: レシート明細データフレーム(df_receipt)に対し、店舗コード(store_cd)ごとに売上金額(amount)と売上数量(quantity)を合計せよ。
df_receipt.groupby('store_cd').agg({'amount':'sum', 'quantity':'sum'}).reset_index()
groupby('列名'):引数に列名を指定するとその列の値ごとにグルーピングされる。
返されるのはGroupByオブジェクトでそれ自体をprint()で出力しても中身は表示されない。
グルーピングしたオブジェクトの処理に関しては以下のページを参照。
P-024 max()
P-024: レシート明細データフレーム(df_receipt)に対し、顧客ID(customer_id)ごとに最も新しい売上日(sales_ymd)を求め、10件表示せよ。
df_receipt.groupby('customer_id').sales_ymd.max().reset_index().head(10)
P-025 min()
P-025: レシート明細データフレーム(df_receipt)に対し、顧客ID(customer_id)ごとに最も古い売上日(sales_ymd)を求め、10件表示せよ。
df_receipt.groupby('customer_id').sales_ymd.min().reset_index().head(10)
P-026
P-026: レシート明細データフレーム(df_receipt)に対し、顧客ID(customer_id)ごとに最も新しい売上日(sales_ymd)と古い売上日を求め、両者が異なるデータを10件表示せよ。
df_max = df_receipt.groupby('customer_id').sales_ymd.max().reset_index()
df_min = df_receipt.groupby('customer_id').sales_ymd.min().reset_index()
df_max_min = pd.concat([df_max, df_min['sales_ymd']], axis=1)
df_max_min.columns = ['customer_id', 'sales_ymd_max', 'sales_ymd_min']
df_max_min.query('sales_ymd_max != sales_ymd_min').head(10)
最も新しい売り上げ日(df_max)と最も古い売り上げ日(df_min)のデータフレームを作り、それを横方向に結合したdf_max_minを作成。
その後、カラム名を変更し、条件に合うものを出力した。
df_tmp = df_receipt.groupby('customer_id').agg({'sales_ymd':['max','min']}).reset_index()
df_tmp.columns = ["_".join(pair) for pair in df_tmp.columns]
# df_tmp.columns = ['customer_id', 'sales_ymd_max', 'sales_ymd_min'] でも可
df_tmp.query('sales_ymd_max != sales_ymd_min').head(10)
解答ではagg()メソッドを使って上のように書いている。
2行目でリスト内包表記を使いカラム名を変更している。
P-027 mean()
P-027: レシート明細データフレーム(df_receipt)に対し、店舗コード(store_cd)ごとに売上金額(amount)の平均を計算し、降順でTOP5を表示せよ。
df_receipt.groupby('store_cd').amount.mean().reset_index().sort_values('amount', ascending=False).head(5)
agg()メソッドを使うなら
df_receipt.groupby('store_cd').agg({'amount':'mean'}).reset_index().sort_values('amount', ascending=False).head(5)
P-028 median()
P-028: レシート明細データフレーム(df_receipt)に対し、店舗コード(store_cd)ごとに売上金額(amount)の中央値を計算し、降順でTOP5を表示せよ。
df_receipt.groupby('store_cd').amount.median().reset_index().sort_values('amount', ascending=False).head(5)
これもP-027と同様にamount.median()をagg({'amount':'median'})と書いても同様の結果を得られる。
P-029 mode(), apply()
P-029: レシート明細データフレーム(df_receipt)に対し、店舗コード(store_cd)ごとに商品コード(product_cd)の最頻値を求めよ。
df_receipt.groupby('store_cd').product_cd.apply(lambda x: x.mode()).reset_index()
GroupByオブジェクトの最頻値は他の関数sum,mean,median等と違い、そのまま使うことができない。
apply()やvalue_counts()を組み合わせる必要がある。
apply():引数に一次元配列に適用可能な関数を渡す。
デフォルトで各列、axis=1で各行に適用される。
解答では無名関数( lambda )を使っている。
P-030 var()
P-030: レシート明細データフレーム(df_receipt)に対し、店舗コード(store_cd)ごとに売上金額(amount)の標本分散を計算し、降順でTOP5を表示せよ。
df_receipt.groupby('store_cd').amount.var(ddof=0).reset_index().sort_values('amount', ascending=False).head(5)
分散の式
$$ s^2 = \frac{1}{n} \sum_{i=1}^{n} (x_i - \bar{x})^2 $$
解答にも書いてあるんですがNumpyとPandasでは分散var()と標準偏差std()のデフォルト値が変わってきます。
これはNumpyはデフォルト値で標本分散が求まり、Pandasでは不偏分散の値が求まるようになっているからだそうです。
var()で分散を計算する際、平均との偏差の2乗の和をn - ddofで割ります。
Pandasではデフォルトがddof=1であるため、引数にddof=0を渡します。
P-031 std()
P-031: レシート明細データフレーム(df_receipt)に対し、店舗コード(store_cd)ごとに売上金額(amount)の標本標準偏差を計算し、降順でTOP5を表示せよ。
df_receipt.groupby('store_cd').amount.std(ddof=0).reset_index().sort_values('amount', ascending=False).head(5)
P-030と同じようにstd()の引数にddof=0を渡します。