はじめに

deepblueインターン生の中山です。
【読み込み、要約統計量、行・列選択、代入】PythonとRのデータフレーム操作比較 vol.1の続きです。
列追加、重複削除、列名変更、結合、集約について、tidyverseを用いたRとPythonの比較をしたいと思います。

連載

利用したデータ

使ったデータは、MineThatData E-Mail Analytics And Data Mining Challenge datasetです。
前回のblogとデータは同じです。本記事では、history3000以上に条件付けている事が多いですが、データ数を減らして確認しやすくしているだけで、深い意味はないです。

データ読み込み

csvデータを読み込むコード。
head10個確認しています。

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.head(10)

#    recency history_segment  history  mens  womens   zip_code  newbie channel        segment  visit  conversion  spend
# 0       10  2) $100 - $200   142.44     1       0  Surburban       0   Phone  Womens E-Mail      0           0    0.0
# 1        6  3) $200 - $350   329.08     1       1      Rural       1     Web      No E-Mail      0           0    0.0
# 2        7  2) $100 - $200   180.65     0       1  Surburban       1     Web  Womens E-Mail      0           0    0.0
# 3        9  5) $500 - $750   675.83     1       0      Rural       1     Web    Mens E-Mail      0           0    0.0
# 4        2    1) $0 - $100    45.34     1       0      Urban       0     Web  Womens E-Mail      0           0    0.0
# 5        6  2) $100 - $200   134.83     0       1  Surburban       0   Phone  Womens E-Mail      1           0    0.0
# 6        9  3) $200 - $350   280.20     1       0  Surburban       1   Phone  Womens E-Mail      0           0    0.0
# 7        9    1) $0 - $100    46.42     0       1      Urban       0   Phone  Womens E-Mail      0           0    0.0
# 8        9  5) $500 - $750   675.07     1       1      Rural       1   Phone    Mens E-Mail      0           0    0.0
# 9       10    1) $0 - $100    32.84     0       1      Urban       1     Web  Womens 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)
head(df, 10)
# df %>% head(10) # こちらでも可

#    recency history_segment history mens womens  zip_code newbie channel       segment visit conversion spend
# 1       10  2) $100 - $200  142.44    1      0 Surburban      0   Phone Womens E-Mail     0          0     0
# 2        6  3) $200 - $350  329.08    1      1     Rural      1     Web     No E-Mail     0          0     0
# 3        7  2) $100 - $200  180.65    0      1 Surburban      1     Web Womens E-Mail     0          0     0
# 4        9  5) $500 - $750  675.83    1      0     Rural      1     Web   Mens E-Mail     0          0     0
# 5        2    1) $0 - $100   45.34    1      0     Urban      0     Web Womens E-Mail     0          0     0
# 6        6  2) $100 - $200  134.83    0      1 Surburban      0   Phone Womens E-Mail     1          0     0
# 7        9  3) $200 - $350  280.20    1      0 Surburban      1   Phone Womens E-Mail     0          0     0
# 8        9    1) $0 - $100   46.42    0      1     Urban      0   Phone Womens E-Mail     0          0     0
# 9        9  5) $500 - $750  675.07    1      1     Rural      1   Phone   Mens E-Mail     0          0     0
# 10      10    1) $0 - $100   32.84    0      1     Urban      1     Web Womens E-Mail     0          0     0

vol.1でやった事

前回のblogの後半部分で扱った内容を1つにまとめます。
列の選択、行の抽出、値の置換の3つです。

Python

df_new = df[["recency", "history", "zip_code", "channel", "spend"]]
df_new = df_new[df_new.history > 3000].assign(spend = 1000)

# df_new
#        recency  history   zip_code       channel  spend
# 4579         2  3345.93  Surburban         Phone   1000
# 38680        1  3215.97      Urban  Multichannel   1000
# 43060        1  3003.48      Urban         Phone   1000
# 52860        1  3040.20      Urban           Web   1000

R

df_new <- df %>%
  select(recency, history, zip_code, channel, spend) %>%
  filter(history > 3000) %>%
  mutate(spend = 1000)

# df_new
#   recency history  zip_code      channel spend
# 1       2 3345.93 Surburban        Phone  1000
# 2       1 3215.97     Urban Multichannel  1000
# 3       1 3003.48     Urban        Phone  1000
# 4       1 3040.20     Urban          Web  1000

列追加

新たな列を追加するコード。
上記では、データの置換を実行しましたが、ここでは新たな列を追加します。
下記コードでは、df_selectedに抽出結果を格納し、df_selectedに列を追加したものがdf_newとなっています。

Python

  • df["col"] = df.mutate.apply(lambda x: x.hoge * x.fuga, axis=1)
df_selected = df[["recency", "history", "zip_code", "channel", "spend"]]
df_selected = df_selected[df_selected.history > 3000]
df_new = df_selected.copy()
df_new["col"] = df_new.apply(lambda x: x.recency * x.history, axis=1)
# df_new["col"] = df_new.recency * df_new.history # こちらでも可

# df_new
#        recency  history   zip_code       channel  spend      col
# 4579         2  3345.93  Surburban         Phone    0.0  6691.86
# 38680        1  3215.97      Urban  Multichannel    0.0  3215.97
# 43060        1  3003.48      Urban         Phone    0.0  3003.48
# 52860        1  3040.20      Urban           Web    0.0  3040.20

R

  • df %>% mutate(col = hoge * fuga)
df_selected <- df %>%
  select(recency, history, zip_code, channel, spend) %>%
  filter(history > 3000)
df_new <- df_selected %>%
  mutate(col = recency * history)

# df_new
#   recency history  zip_code      channel spend     col
# 1       2 3345.93 Surburban        Phone     0 6691.86
# 2       1 3215.97     Urban Multichannel     0 3215.97
# 3       1 3003.48     Urban        Phone     0 3003.48
# 4       1 3040.20     Urban          Web     0 3040.20

重複削除

データフレームの重複を削除するコード。
ここでは、channel列の重複を削除しています。

Python

  • drop_duplicates()
df_new = df.channel.drop_duplicates()

# df_new
# 0            Phone
# 1              Web
# 12    Multichannel
# Name: channel, dtype: object

R

  • distinct()
df_new <- df %>%
  distinct(channel)

# df_new
#        channel
# 1        Phone
# 2          Web
# 3 Multichannel

列名変更

列名を変更するコード。
PythonとRでは、hogefugaの書く順番が違うため注意が必要です。

Python

  • rename(columns={"hoge" : "fuga"})
df_new = df.rename(columns={"recency":"RECENCY"})

# df_new.head()
#    RECENCY history_segment  history  mens  womens   zip_code  newbie channel        segment  visit  conversion  spend
# 0       10  2) $100 - $200   142.44     1       0  Surburban       0   Phone  Womens E-Mail      0           0    0.0
# 1        6  3) $200 - $350   329.08     1       1      Rural       1     Web      No E-Mail      0           0    0.0
# 2        7  2) $100 - $200   180.65     0       1  Surburban       1     Web  Womens E-Mail      0           0    0.0
# 3        9  5) $500 - $750   675.83     1       0      Rural       1     Web    Mens E-Mail      0           0    0.0
# 4        2    1) $0 - $100    45.34     1       0      Urban       0     Web  Womens E-Mail      0           0    0.0

R

  • rename(fuga = hoge)
df_new <- df %>%
  rename(RECENCY = recency)

# df_new %>% head()
#   RECENCY history_segment history mens womens  zip_code newbie channel       segment visit conversion spend
# 1      10  2) $100 - $200  142.44    1      0 Surburban      0   Phone Womens E-Mail     0          0     0
# 2       6  3) $200 - $350  329.08    1      1     Rural      1     Web     No E-Mail     0          0     0
# 3       7  2) $100 - $200  180.65    0      1 Surburban      1     Web Womens E-Mail     0          0     0
# 4       9  5) $500 - $750  675.83    1      0     Rural      1     Web   Mens E-Mail     0          0     0
# 5       2    1) $0 - $100   45.34    1      0     Urban      0     Web Womens E-Mail     0          0     0
# 6       6  2) $100 - $200  134.83    0      1 Surburban      0   Phone Womens E-Mail     1          0     0

データの列結合

データフレームを列どうしで結合するコード。
dfdf1df2に分割し、列結合をしています。

Python

  • concat([df1, df2], axis=1)
df1 = df[["recency", "history", "zip_code"]]
df2 = df[["channel", "spend"]]
df_new = pd.concat([df1, df2], axis=1)

# df_new.head()
#    recency  history   zip_code channel  spend
# 0       10   142.44  Surburban   Phone    0.0
# 1        6   329.08      Rural     Web    0.0
# 2        7   180.65  Surburban     Web    0.0
# 3        9   675.83      Rural     Web    0.0
# 4        2    45.34      Urban     Web    0.0

R

  • bind_cols(df1, df2)
df1 <- df %>%
  select(recency, history, zip_code)
df2 <- df %>%
  select(channel, spend)
df_new <- df1 %>%
  bind_cols(df2)

# tidyverseを利用しない場合
df_new <- cbind(df1, df2)

# df_new %>% head()
#   recency history  zip_code channel spend
# 1      10  142.44 Surburban   Phone     0
# 2       6  329.08     Rural     Web     0
# 3       7  180.65 Surburban     Web     0
# 4       9  675.83     Rural     Web     0
# 5       2   45.34     Urban     Web     0
# 6       6  134.83 Surburban   Phone     0

データの行結合

データフレームを行どうしで結合するコード。
dfdf1df2に分割し、行結合をしています。

Python

  • concat([df1, df2], axis=0)
df1 = df[df.history > 3000]
df2 = df[df.history == 30]
df_new = pd.concat([df1, df2], axis=0)

# df_new
#        recency history_segment  history  mens  womens   zip_code  newbie       channel        segment  visit  conversion  spend
# 4579         2     7) $1,000 +  3345.93     1       1  Surburban       1         Phone      No 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
# 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
# 8645         9    1) $0 - $100    30.00     1       0      Rural       0         Phone      No E-Mail      0           0    0.0
# 56175       10    1) $0 - $100    30.00     1       0  Surburban       0         Phone    Mens E-Mail      0           0    0.0

R

  • bind_rows(df1, df2)
df1 <- df %>%
  filter(history > 3000)
df2 <- df %>%
  filter(history == 30)
df_new <- df1 %>%
  bind_rows(df2)

# tidyverseを利用しない場合
df_new <- rbind(df1, df2)

# df_new
#   recency history_segment history mens womens  zip_code newbie      channel       segment visit conversion spend
# 1       2     7) $1,000 + 3345.93    1      1 Surburban      1        Phone     No E-Mail     0          0     0
# 2       1     7) $1,000 + 3215.97    1      1     Urban      1 Multichannel   Mens E-Mail     1          0     0
# 3       1     7) $1,000 + 3003.48    1      1     Urban      1        Phone     No E-Mail     0          0     0
# 4       1     7) $1,000 + 3040.20    0      1     Urban      1          Web Womens E-Mail     0          0     0
# 5       9    1) $0 - $100   30.00    1      0     Rural      0        Phone     No E-Mail     0          0     0
# 6      10    1) $0 - $100   30.00    1      0 Surburban      0        Phone   Mens E-Mail     0          0     0

列のマージ

データフレームを列でマージするコード。
dfdf1df2に分割し、indexをキーとして内部結合しています。

Python

  • pd.merge(df1, df2, on = "hoge", how = "inner")

how = "inner"の他にもleft, right, outerなどがあります。

df1 = df.reset_index().loc[df.history > 3000, 
                           ["index", "recency", "history", "zip_code"]]
df2 = df.reset_index().loc[:, ["index", "channel", "spend"]]
df_new = pd.merge(df1, df2, on = "index", how = "inner")

# df_new
#    index  recency  history   zip_code       channel  spend
# 0   4579        2  3345.93  Surburban         Phone    0.0
# 1  38680        1  3215.97      Urban  Multichannel    0.0
# 2  43060        1  3003.48      Urban         Phone    0.0
# 3  52860        1  3040.20      Urban           Web    0.0

R

  • inner_join(df1, df2, by = "hoge")

inner_joinの他にもleft_join, right_join, full_joinなどがあります。

df1 <- df %>%
  rowid_to_column("index") %>%
  filter(history > 3000) %>%
  select(index, recency, history, zip_code)
df2 <- df %>%
  rowid_to_column("index") %>%
  select(index, channel, spend)
df_new <- df1 %>%
  inner_join(df2, by = "index")

# df_new
#   index recency history  zip_code      channel spend
# 1  4580       2 3345.93 Surburban        Phone     0
# 2 38681       1 3215.97     Urban Multichannel     0
# 3 43061       1 3003.48     Urban        Phone     0
# 4 52861       1 3040.20     Urban          Web     0

データの集約

groupbyでデータを分けて全列の統計量を算出するコード。
ここでは、dfchannelでグルーピングして、各列の平均値を算出しています。

Python

  • groupby("hoge")
df_new = df.groupby("channel").mean()

# df_new
#                recency     history      mens    womens    newbie     visit  conversion     spend
# channel
# Multichannel  4.768488  520.970370  0.632698  0.624195  0.595723  0.171734    0.012626  1.401777
# Phone         5.897541  202.807184  0.539203  0.538953  0.493309  0.127155    0.007744  0.908892
# Web           5.904632  204.375017  0.540313  0.539923  0.485417  0.159407    0.009321  1.095420

R

  • group_by(hoge)
df_new <- df %>%
  group_by(channel) %>%
  summarise_all(funs(mean))

# df_new
# channel        recency history_segment history  mens womens zip_code newbie segment visit conversion spend
# <fct>            <dbl>           <dbl>   <dbl> <dbl>  <dbl>    <dbl>  <dbl>   <dbl> <dbl>      <dbl> <dbl>
# 1 Multichannel    4.77              NA    521. 0.633  0.624       NA  0.596      NA 0.172    0.0126  1.40
# 2 Phone           5.90              NA    203. 0.539  0.539       NA  0.493      NA 0.127    0.00774 0.909
# 3 Web             5.90              NA    204. 0.540  0.540       NA  0.485      NA 0.159    0.00932 1.10

連載