はじめに
deepblueインターン生の中山です。
【列追加、重複削除、列名変更、結合、マージ、集約】PythonとRのデータフレーム操作比較 vol.2
の続きです。
集計、ソート、縦持ち・横持ち変換について、tidyverse
を用いたRとPythonの比較をしたいと思います。
連載
- 下記5記事のまとめ:PythonのpandasとRのdplyrを用いたデータフレーム操作比較
- 【読み込み、要約統計量、行・列選択、代入】PythonとRのデータフレーム操作比較 vol.1
- 【列追加、重複削除、列名変更、結合、マージ、集約】PythonとRのデータフレーム操作比較 vol.2
- 【集計、ソート、縦持ち・横持ち変換】PythonとRのデータフレーム操作比較 vol.3
- 【列分割・NA操作・サンプリング】PythonとRのデータフレーム操作比較 vol.4
- 【train/test分割】PythonとRのデータフレーム操作比較 vol.5
使ったデータ
使ったデータは、MineThatData E-Mail Analytics And Data Mining Challenge datasetです。
前回のblogとデータは同じです。本記事では、history
を3000
以上に条件付けている事が多いですが、データ数を減らして確認しやすくしているだけで、深い意味はないです。
データ読み込み
csvデータを読み込むコード。
tail
で末尾のデータを確認しています。
Python
import pandas as pd
PATH = "http://www.minethatdata.com/Kevin_Hillstrom_MineThatData_E-MailAnalytics_DataMiningChallenge_2008.03.20.csv"
df = pd.read_csv(PATH)
df.tail()
# recency history_segment history mens womens zip_code newbie channel segment visit conversion spend
# 63995 10 2) $100 - $200 105.54 1 0 Urban 0 Web Mens E-Mail 0 0 0.0
# 63996 5 1) $0 - $100 38.91 0 1 Urban 1 Phone Mens E-Mail 0 0 0.0
# 63997 6 1) $0 - $100 29.99 1 0 Urban 1 Phone Mens E-Mail 0 0 0.0
# 63998 1 5) $500 - $750 552.94 1 0 Surburban 1 Multichannel Womens E-Mail 0 0 0.0
# 63999 1 4) $350 - $500 472.82 0 1 Surburban 0 Web Mens E-Mail 0 0 0.0
R
library(tidyverse)
PATH <- "http://www.minethatdata.com/Kevin_Hillstrom_MineThatData_E-MailAnalytics_DataMiningChallenge_2008.03.20.csv"
df <- read.csv(PATH)
tail(df)
# recency history_segment history mens womens zip_code newbie channel segment visit conversion spend
# 63995 7 1) $0 - $100 86.46 0 1 Urban 0 Web Mens E-Mail 0 0 0
# 63996 10 2) $100 - $200 105.54 1 0 Urban 0 Web Mens E-Mail 0 0 0
# 63997 5 1) $0 - $100 38.91 0 1 Urban 1 Phone Mens E-Mail 0 0 0
# 63998 6 1) $0 - $100 29.99 1 0 Urban 1 Phone Mens E-Mail 0 0 0
# 63999 1 5) $500 - $750 552.94 1 0 Surburban 1 Multichannel Womens E-Mail 0 0 0
# 64000 1 4) $350 - $500 472.82 0 1 Surburban 0 Web Mens E-Mail 0 0 0
グループ分けした時の要約統計量
グループ分けを行い、欲しい列を抽出した時の、要約統計量を算出するコード。
PythonとRにおけるコードの対応関係は下記のようになります。
ここでは、df
のchannel
とzip_code
でグルーピングを行い、recency
、history
、spend
の平均値と最大値を算出しています。
Python
groupby(["hoge", "fuga"])
agg("mean","max")[["col1", "col2"]]
df_new = df.groupby(["channel","zip_code"]).agg(["mean","max"])[["recency","history","spend"]]
# df_new
# recency history spend
# mean max mean max mean max
# channel zip_code
# Multichannel Rural 4.773451 12 523.810743 2079.58 1.124389 328.36
# Surburban 4.760173 12 521.458574 2809.79 1.357048 499.00
# Urban 4.775813 12 519.422769 3215.97 1.549687 421.76
# Phone Rural 5.827175 12 207.631497 2080.78 1.070033 499.00
# Surburban 5.946969 12 200.612477 3345.93 0.809698 499.00
# Urban 5.868908 12 203.447781 3003.48 0.958960 499.00
# Web Rural 5.889335 12 202.806019 2039.13 1.336614 362.20
# Surburban 5.944801 12 203.407815 2816.01 1.076664 499.00
# Urban 5.864510 12 206.074893 3040.20 1.025505 499.00
R
group_by(hoge, fuga)
summarize_each(funs(mean, max), col1, col2)
df_new <- df %>%
group_by(channel, zip_code) %>%
summarise_each(funs(mean, max),
recency, history, spend)
# df_new
# channel zip_code recency_mean history_mean spend_mean recency_max history_max spend_max
# <fct> <fct> <dbl> <dbl> <dbl> <int> <dbl> <dbl>
# 1 Multichannel Rural 4.77 524. 1.12 12 2080. 328.
# 2 Multichannel Surburban 4.76 521. 1.36 12 2810. 499
# 3 Multichannel Urban 4.78 519. 1.55 12 3216. 422.
# 4 Phone Rural 5.83 208. 1.07 12 2081. 499
# 5 Phone Surburban 5.95 201. 0.810 12 3346. 499
# 6 Phone Urban 5.87 203. 0.959 12 3003. 499
# 7 Web Rural 5.89 203. 1.34 12 2039. 362.
# 8 Web Surburban 5.94 203. 1.08 12 2816. 499
# 9 Web Urban 5.86 206. 1.03 12 3040. 499
データのソート
列を選択し、データのソートをするコード。
history
の値が昇順になるように並び替えています。
Python
sort_values(["hoge"])
df_new = df[df.history > 3000]
df_new = df_new.sort_values(["history"])
# df_new
# recency history_segment history mens womens zip_code newbie channel segment visit conversion spend
# 43060 1 7) $1,000 + 3003.48 1 1 Urban 1 Phone No E-Mail 0 0 0.0
# 52860 1 7) $1,000 + 3040.20 0 1 Urban 1 Web Womens E-Mail 0 0 0.0
# 38680 1 7) $1,000 + 3215.97 1 1 Urban 1 Multichannel Mens E-Mail 1 0 0.0
# 4579 2 7) $1,000 + 3345.93 1 1 Surburban 1 Phone No E-Mail 0 0 0.0
R
arrange(hoge)
df_new <- df %>%
filter(history > 3000) %>%
arrange(history)
# df_new
# recency history_segment history mens womens zip_code newbie channel segment visit conversion spend
# 1 1 7) $1,000 + 3003.48 1 1 Urban 1 Phone No E-Mail 0 0 0
# 2 1 7) $1,000 + 3040.20 0 1 Urban 1 Web Womens E-Mail 0 0 0
# 3 1 7) $1,000 + 3215.97 1 1 Urban 1 Multichannel Mens E-Mail 1 0 0
# 4 2 7) $1,000 + 3345.93 1 1 Surburban 1 Phone No E-Mail 0 0 0
インデックスを列に追加
データの1列目に、元データの行番号を追加するコード。
元の行番号を列に追加して、index
を振り直しています。
Python
insert(0, "id", df.index)
reset_index()
でも可能ですが、場所を選びたい時はinsert
の方が便利です。
df_new = df.copy()
df_new.insert(0, "id", df_new.index)
df_new = df_new[df_new.history > 3000]
df_new = df_new.reset_index(drop=True)
# df_new
# id recency history_segment history mens womens zip_code newbie channel segment visit conversion spend
# 0 4579 2 7) $1,000 + 3345.93 1 1 Surburban 1 Phone No E-Mail 0 0 0.0
# 1 38680 1 7) $1,000 + 3215.97 1 1 Urban 1 Multichannel Mens E-Mail 1 0 0.0
# 2 43060 1 7) $1,000 + 3003.48 1 1 Urban 1 Phone No E-Mail 0 0 0.0
# 3 52860 1 7) $1,000 + 3040.20 0 1 Urban 1 Web Womens E-Mail 0 0 0.0
R
rowid_to_column("id")
df_new <- df %>%
rowid_to_column("id") %>%
filter(history > 3000)
# df_new
# id recency history_segment history mens womens zip_code newbie channel segment visit conversion spend
# 1 4580 2 7) $1,000 + 3345.93 1 1 Surburban 1 Phone No E-Mail 0 0 0
# 2 38681 1 7) $1,000 + 3215.97 1 1 Urban 1 Multichannel Mens E-Mail 1 0 0
# 3 43061 1 7) $1,000 + 3003.48 1 1 Urban 1 Phone No E-Mail 0 0 0
# 4 52861 1 7) $1,000 + 3040.20 0 1 Urban 1 Web Womens E-Mail 0 0 0
データの縦持ち
複数列のデータを1列にするコード。
id
列・変数名列・変数列の3つの列を持つデータに変更しています。
Python、Rともに同じ結果になるようにしています。
Python
stack()
df_vertical = df[df.history > 3000]
df_vertical = pd.DataFrame(df_vertical.stack())
df_vertical = df_vertical.rename(columns={0: "values"})
df_vertical.index.names = ["id", "keys"]
df_vertical
# df_vertical.head(24)
# values
# id keys
# 4579 recency 2
# history_segment 7) $1,000 +
# history 3345.93
# mens 1
# womens 1
# zip_code Surburban
# newbie 1
# channel Phone
# segment No E-Mail
# visit 0
# conversion 0
# spend 0
# 38680 recency 1
# history_segment 7) $1,000 +
# history 3215.97
# mens 1
# womens 1
# zip_code Urban
# newbie 1
# channel Multichannel
# segment Mens E-Mail
# visit 1
# conversion 0
# spend 0
R
gather()
df_vertical <- df %>%
rowid_to_column("id") %>%
filter(history > 3000) %>%
gather(key = keys, value = values, -id) %>%
arrange(id)
# df_vertical %>% head(24)
# id keys values
# 1 4580 recency 2
# 2 4580 history_segment 7) $1,000 +
# 3 4580 history 3345.93
# 4 4580 mens 1
# 5 4580 womens 1
# 6 4580 zip_code Surburban
# 7 4580 newbie 1
# 8 4580 channel Phone
# 9 4580 segment No E-Mail
# 10 4580 visit 0
# 11 4580 conversion 0
# 12 4580 spend 0
# 13 38681 recency 1
# 14 38681 history_segment 7) $1,000 +
# 15 38681 history 3215.97
# 16 38681 mens 1
# 17 38681 womens 1
# 18 38681 zip_code Urban
# 19 38681 newbie 1
# 20 38681 channel Multichannel
# 21 38681 segment Mens E-Mail
# 22 38681 visit 1
# 23 38681 conversion 0
# 24 38681 spend 0
縦持ちの横持ち化
上記でstack
及びgather
によって縦持ちにしたdf_vertical
を横持ちに戻すコード。
Python
unstack()
df_horizontal = df_vertical.unstack()
# df_horizontal
# values
# keys recency history_segment history mens womens zip_code newbie channel segment visit conversion spend
# id
# 4579 2 7) $1,000 + 3345.93 1 1 Surburban 1 Phone No E-Mail 0 0 0
# 38680 1 7) $1,000 + 3215.97 1 1 Urban 1 Multichannel Mens E-Mail 1 0 0
# 43060 1 7) $1,000 + 3003.48 1 1 Urban 1 Phone No E-Mail 0 0 0
# 52860 1 7) $1,000 + 3040.2 0 1 Urban 1 Web Womens E-Mail 0 0 0
ただ、columnが2行になってしまうので、以下のようにすると元に戻ります。
cols = [v2 for v1, v2 in df_horizontal.columns.values]
df_horizontal.columns = cols
# df_horizontal
# recency history_segment history mens womens zip_code newbie channel segment visit conversion spend
# id
# 4579 2 7) $1,000 + 3345.93 1 1 Surburban 1 Phone No E-Mail 0 0 0
# 38680 1 7) $1,000 + 3215.97 1 1 Urban 1 Multichannel Mens E-Mail 1 0 0
# 43060 1 7) $1,000 + 3003.48 1 1 Urban 1 Phone No E-Mail 0 0 0
# 52860 1 7) $1,000 + 3040.2 0 1 Urban 1 Web Womens E-Mail 0 0 0
R
spread()
df_horizontal <- df_vertical %>%
spread(key = keys, value = values)
# df_horizontal
# id channel conversion history history_segment mens newbie recency segment spend visit womens zip_code
# 1 4580 Phone 0 3345.93 7) $1,000 + 1 1 2 No E-Mail 0 0 1 Surburban
# 2 38681 Multichannel 0 3215.97 7) $1,000 + 1 1 1 Mens E-Mail 0 1 1 Urban
# 3 43061 Phone 0 3003.48 7) $1,000 + 1 1 1 No E-Mail 0 0 1 Urban
# 4 52861 Web 0 3040.2 7) $1,000 + 0 1 1 Womens E-Mail 0 0 1 Urban