データサイエンス100本ノック(構造化データ加工編)- R Part 3 (Q41 to Q60)の解説です。
参照(Reference) : 「データサイエンティスト協会スキル定義委員」の「データサイエンス100本ノック(構造化データ加工編)」
The Data Scientist Society Github :
Data Science 100 Knocks (Structured Data Processing) URL :
はじめに
- 初めに以下のセルを実行してください
- 必要なライブラリのインポートとデータベース(PostgreSQL)からのデータ読み込みを行います
- 利用が想定されるライブラリは以下セルでインポートしています
- その他利用したいライブラリがあればinstall.packages()で適宜インストールしてください
- 名前、住所等はダミーデータであり、実在するものではありません
require("RPostgreSQL")
require("tidyr")
require("dplyr")
require("stringr")
require("caret")
require("lubridate")
require("rsample")
require("recipes")
require("themis")
host <- "db"
port <- Sys.getenv()["PG_PORT"]
dbname <- Sys.getenv()["PG_DATABASE"]
user <- Sys.getenv()["PG_USER"]
password <- Sys.getenv()["PG_PASSWORD"]
con <- dbConnect(PostgreSQL(), host=host, port=port, dbname=dbname, user=user, password=password)
df_customer <- dbGetQuery(con,"SELECT * FROM customer")
df_category <- dbGetQuery(con,"SELECT * FROM category")
df_product <- dbGetQuery(con,"SELECT * FROM product")
df_receipt <- dbGetQuery(con,"SELECT * FROM receipt")
df_store <- dbGetQuery(con,"SELECT * FROM store")
df_geocode <- dbGetQuery(con,"SELECT * FROM geocode")
Loading required package: RPostgreSQL Loading required package: DBI Loading required package: tidyr Loading required package: dplyr Attaching package: ‘dplyr’ The following objects are masked from ‘package:stats’: filter, lag The following objects are masked from ‘package:base’: intersect, setdiff, setequal, union Loading required package: stringr Loading required package: caret Loading required package: ggplot2 Loading required package: lattice Loading required package: lubridate Attaching package: ‘lubridate’ The following objects are masked from ‘package:base’: date, intersect, setdiff, union Loading required package: rsample Loading required package: recipes Attaching package: ‘recipes’ The following object is masked from ‘package:stringr’: fixed The following object is masked from ‘package:stats’: step Loading required package: themis Registered S3 methods overwritten by 'themis': method from bake.step_downsample recipes bake.step_upsample recipes prep.step_downsample recipes prep.step_upsample recipes tidy.step_downsample recipes tidy.step_upsample recipes tunable.step_downsample recipes tunable.step_upsample recipes Attaching package: ‘themis’ The following objects are masked from ‘package:recipes’: step_downsample, step_upsample
演習問題
R-041: レシート明細データ(df_receipt)の売上金額(amount)を日付(sales_ymd)ごとに集計し、前回売上があった日からの売上金額増減を計算せよ。そして結果を10件表示せよ。
df_receipt %>%
group_by(sales_ymd) %>%
summarise(sum_amount = sum(amount), .groups = "drop") %>%
mutate(lag_ymd = lag(sales_ymd), lag_amount = lag(sum_amount), diff_amount = sum_amount - lag_amount) %>%
slice(1:10)
sales_ymd | sum_amount | lag_ymd | lag_amount | diff_amount |
---|---|---|---|---|
<int> | <int> | <int> | <int> | <int> |
20170101 | 33723 | NA | NA | NA |
20170102 | 24165 | 20170101 | 33723 | -9558 |
20170103 | 27503 | 20170102 | 24165 | 3338 |
20170104 | 36165 | 20170103 | 27503 | 8662 |
20170105 | 37830 | 20170104 | 36165 | 1665 |
20170106 | 32387 | 20170105 | 37830 | -5443 |
20170107 | 23415 | 20170106 | 32387 | -8972 |
20170108 | 24737 | 20170107 | 23415 | 1322 |
20170109 | 26718 | 20170108 | 24737 | 1981 |
20170110 | 20143 | 20170109 | 26718 | -6575 |
解説:
このコードはRのdplyrパッケージを使って、df_receiptというデータフレームを操作しています。以下は、このコードが行っていることを段階的に説明したものである。
df_receipt %>% - これはRのパイプ演算子で、df_receiptデータフレームをパイプラインの次の関数に最初の引数として渡します。
group_by(sales_ymd) %>% - sales_ymd カラムによってデータフレームをグループ化します(これは日付または日付時間データを含みます)。演算子 %>% は、グループ化されたデータフレームをパイプラインの次の関数に送ります。
summarise(sum_amount = sum(amount), .groups = "drop") %>% - グループ化されたデータフレームを要約し、一意の sales_ymd グループごとに金額列の合計を計算します。結果のデータフレームは、sales_ymdとsum_amountの2つの列を持ちます。.groups = "drop "引数は、group_byで追加されたグループ化情報が不要になったので、それを削除するために使用されます。演算子%>%は、この要約されたデータフレームをパイプラインの次の関数に再びパイプします。
mutate(lag_ymd = lag(sales_ymd), lag_amount = lag(sum_amount), diff_amount = sum_amount - lag_amount) %>% - これは要約データフレームに新しい3列を追加します。最初の2列、lag_ymdとlag_amountは、それぞれsales_ymdとsum_amount列のラグ値を含んでいます。3列目のdiff_amountには、sum_amount列とlag_amount列の差分が格納されています。演算子 %>% は、この修正されたデータフレームをパイプラインの次の関数に送ります。
slice(1:10) - これは、修正されたデータフレームの最初の10行を選択するものです。結果として得られるデータフレームは、sales_ymd, sum_amount, lag_ymd, lag_amount, diff_amount という列を持ち、元の df_receipt データフレームをグループ化、要約、修正した後の最初の10行を含むことになる。
全体として、このコードは売上データに対してある種の時系列分析を行い、各日の売上の合計を計算し、各日の売上と前日の売上の差を計算します。結果として得られるデータフレームは、日々の売上高の経時的な傾向を把握することができるはずです。
R-042: レシート明細データ(df_receipt)の売上金額(amount)を日付(sales_ymd)ごとに集計し、各日付のデータに対し、前回、前々回、3回前に売上があった日のデータを結合せよ。そして結果を10件表示せよ。
# コード例1:縦持ちケース
df_sum <-df_receipt %>%
group_by(sales_ymd) %>%
summarise(sum_amount = sum(amount), .groups = "drop")
for (i in 1:3) {
df_tmp <- df_sum %>%
mutate(lag_ymd = lag(sales_ymd, n = i),
lag_amount = lag(sum_amount, n = i))
if (i == 1) {
df_lag <- df_tmp
}
else {
df_lag <- rbind(df_lag, df_tmp)
}
}
df_lag %>%
drop_na(everything()) %>%
arrange(sales_ymd, lag_ymd) %>%
slice(1:10)
sales_ymd | sum_amount | lag_ymd | lag_amount |
---|---|---|---|
<int> | <int> | <int> | <int> |
20170102 | 24165 | 20170101 | 33723 |
20170103 | 27503 | 20170101 | 33723 |
20170103 | 27503 | 20170102 | 24165 |
20170104 | 36165 | 20170101 | 33723 |
20170104 | 36165 | 20170102 | 24165 |
20170104 | 36165 | 20170103 | 27503 |
20170105 | 37830 | 20170102 | 24165 |
20170105 | 37830 | 20170103 | 27503 |
20170105 | 37830 | 20170104 | 36165 |
20170106 | 32387 | 20170103 | 27503 |
解説:
このコードもRのdplyrパッケージを使ってdf_receiptというデータフレームを操作しているものです。
df_sum <-df_receipt %>% group_by(sales_ymd) %>% summarise(sum_amount = sum(amount), .groups = "drop") - これは、前の例のコードの最初の行と同様で、df_receipt データフレームを sales_ymd 列でグループ化し、固有の sales_ymd グループごとに金額列の合計を計算して、結果のデータフレームを df_sum という新しい変数に格納しています。
for (i in 1:3) { - これは、3回反復するforループを開始します。
df_tmp <- df_sum %>% mutate(lag_ymd = lag(sales_ymd, n = i), lag_amount = lag(sum_amount, n = i)) - これは、df_sumデータフレームのsales_ymdとsum_amount列をdplyrのlag()を使用してi日間ラグらせた新しい一時データフレームdf_tmpを生成します。結果のデータフレームは、sales_ymd, sum_amount, lag_ymd, lag_amount の3つのカラムを持つことになります。
if (i == 1) { df_lag <- df_tmp }. - これがループの最初の反復である場合、df_lagデータフレームはdf_tmpと等しく設定される。
else { df_lag <- rbind(df_lag, df_tmp) }. - ループの最初の反復でない場合、df_tmp は dplyr の rbind() 関数を使用して既存の df_lag データフレームに追加されます。
} - forループはここで終了。
df_lag %>% drop_na(everything()) %>% arrange(sales_ymd, lag_ymd) %>% slice(1:10) - これはループ内で作成された df_lag データフレームを受け取り、 drop_na() でデータが欠損した行をすべて取り除き、 arrange() で sales_ymd と lag_ymd で行を並べ、 slice() で結果のデータフレームの最初の10行を選択します。
このコードの目的は、過去3日間の sales_ymd と sum_amount 列のラグ値を含む df_lag という新しいデータフレームを作成することです。このデータフレームは、過去3日間の売上動向を調べるために使用することができ、将来の売上を予測するのに役立つ可能性があります。最後のコードは、表示やさらなる分析のために、結果のデータフレームの最初の10行を選択するものです。
# コード例2:横持ちケース
df_sum <-df_receipt %>%
group_by(sales_ymd) %>%
summarise(sum_amount = sum(amount), .groups = "drop")
for (i in 1:3) {
col_name_1 <- paste("lag_ymd_", i , sep="_")
col_name_2 <- paste("lag_amount", i , sep="_")
df_tmp <- df_sum %>%
mutate(!!col_name_1 := lag(sales_ymd, n = i),
!!col_name_2 := lag(sum_amount, n = i))
if (i == 1) {
df_lag <- df_tmp
}
else {
df_lag <- cbind(df_lag, df_tmp[c(col_name_1, col_name_2)])
}
}
df_lag %>%
drop_na(everything()) %>%
arrange(sales_ymd) %>%
slice(1:10)
sales_ymd | sum_amount | lag_ymd__1 | lag_amount_1 | lag_ymd__2 | lag_amount_2 | lag_ymd__3 | lag_amount_3 |
---|---|---|---|---|---|---|---|
<int> | <int> | <int> | <int> | <int> | <int> | <int> | <int> |
20170104 | 36165 | 20170103 | 27503 | 20170102 | 24165 | 20170101 | 33723 |
20170105 | 37830 | 20170104 | 36165 | 20170103 | 27503 | 20170102 | 24165 |
20170106 | 32387 | 20170105 | 37830 | 20170104 | 36165 | 20170103 | 27503 |
20170107 | 23415 | 20170106 | 32387 | 20170105 | 37830 | 20170104 | 36165 |
20170108 | 24737 | 20170107 | 23415 | 20170106 | 32387 | 20170105 | 37830 |
20170109 | 26718 | 20170108 | 24737 | 20170107 | 23415 | 20170106 | 32387 |
20170110 | 20143 | 20170109 | 26718 | 20170108 | 24737 | 20170107 | 23415 |
20170111 | 24287 | 20170110 | 20143 | 20170109 | 26718 | 20170108 | 24737 |
20170112 | 23526 | 20170111 | 24287 | 20170110 | 20143 | 20170109 | 26718 |
20170113 | 28004 | 20170112 | 23526 | 20170111 | 24287 | 20170110 | 20143 |
解説:
このコードもRのdplyrパッケージを使ってdf_receiptというデータフレームを操作しているものです。
df_sum <-df_receipt %>% group_by(sales_ymd) %>% summarise(sum_amount = sum(amount), .groups = "drop") - これは、前の例のコードの最初の行と同様で、df_receipt データフレームを sales_ymd 列でグループ化し、固有の sales_ymd グループごとに金額列の合計を計算して、結果のデータフレームを df_sum という新しい変数に格納しています。
for (i in 1:3) { - これは、3回繰り返し実行するforループを開始します。
col_name_1 <- paste("lag_ymd_", i , sep="_") - これは、接頭辞 "lag_ymd_"と現在のiの値を連結したcol_name_1という新しい文字列変数を作成するものである。
col_name_2 <- paste("lag_amount", i , sep="_") - これは、接頭辞 "lag_amount_"と現在のiの値を連結するcol_name_2という新しい文字列変数を作成します。
df_tmp <- df_sum %>% mutate(!!col_name_1 := lag(sales_ymd, n = i), !!col_name_2 := lag(sum_amount, n = i)) - df_sum データフレームの sales_ymd と sum_amount 列を dplyr の lag() 関数を使用して i 日ラグさせて作成した新しい一時データフレームを作成する df_tmpという名前のデータです。しかし、前の例のように静的な名前で新しい列を追加するのではなく、このコードではステップ3と4で作成した文字列変数を使用して列名を動的に作成しています。変数名が列名として評価されるように、!!!演算子を用いて引用符を解除しています。
if (i == 1) { df_lag <- df_tmp }. - これがループの最初の反復である場合、df_lagデータフレームはdf_tmpと等しく設定される。
else { df_lag <- cbind(df_lag, df_tmp[c(col_name_1, col_name_2)]) } - これがループの最初の反復でない場合、df_tmp データフレームは cbind() を使って既存の df_lag データフレームに追加されます。しかし、このコードでは、前の例のように特定の列を選択するのではなく、名前が col_name_1 と col_name_2 の値に一致する列を動的に選択する。
} - ここでforループは終了します。
df_lag %>% drop_na(everything()) %>% arrange(sales_ymd) %>% slice(1:10) - これはループ内で作成されたdf_lagデータフレームを受け取り、drop_na()で欠損データのある行を削除し、arrange()で sales_ymd で行を整列し、slice()で結果のデータフレームの最初の10行を選択する。
このコードの目的は、df_lagという新しいデータフレームを作成し、sales_ymdとsum_amount列の過去3日間の各遅延値を格納するというものです。しかし、このコードでは、カラム名をハードコーディングする代わりに、動的にカラム名を生成します。
R-043: レシート明細データ(df_receipt)と顧客データ(df_customer)を結合し、性別コード(gender_cd)と年代(ageから計算)ごとに売上金額(amount)を合計した売上サマリデータを作成せよ。性別コードは0が男性、1が女性、9が不明を表すものとする。
ただし、項目構成は年代、女性の売上金額、男性の売上金額、性別不明の売上金額の4項目とすること(縦に年代、横に性別のクロス集計)。また、年代は10歳ごとの階級とすること。
df_sales_summary <- df_customer[c("customer_id", "gender_cd", "age")] %>%
mutate(era = trunc(age / 10) * 10) %>%
inner_join(df_receipt, by = "customer_id") %>%
group_by(gender_cd, era) %>%
summarise(sum_amount = sum(amount), .groups = "drop") %>%
spread( gender_cd, sum_amount, fill = 0) %>%
rename(male = "0", female = "1", unknown = "9")
df_sales_summary
era | male | female | unknown |
---|---|---|---|
<dbl> | <dbl> | <dbl> | <dbl> |
10 | 1591 | 149836 | 4317 |
20 | 72940 | 1363724 | 44328 |
30 | 177322 | 693047 | 50441 |
40 | 19355 | 9320791 | 483512 |
50 | 54320 | 6685192 | 342923 |
60 | 272469 | 987741 | 71418 |
70 | 13435 | 29764 | 2427 |
80 | 46360 | 262923 | 5111 |
90 | 0 | 6260 | 0 |
解説:
このコードは、df_customerとdf_receiptの2つのデータフレームからデータを使用して、顧客の性別と年齢時代(つまり10歳)別の売上金額のサマリーを作成します。
まず、df_customerから3つの列(customer_id、gender_cd、age)を選択し、ageを直近の10年に切り捨てて新しい変数eraを作成します。
次に、df_receiptデータフレームをcustomer_idで結合し、顧客の性別、年齢、販売金額に関する情報を持つ新しいデータフレームを作成します。
次に、このデータを gender_cd と era でグループ化し、summarise() を使用して総売上高を計算します。
spread() を使用してデータをピボット化し、各時代に 1 行、各性別の売上金額の合計を表す列を作成します。
最後に、rename() を使用して、男性、女性、性別不明を表す列に、より説明的な名前を付けます。
その結果、df_sales_summaryというデータフレームができ、性別と年代別の総売上高を要約しています。
R-044: 043で作成した売上サマリデータ(df_sales_summary)は性別の売上を横持ちさせたものであった。このデータから性別を縦持ちさせ、年代、性別コード、売上金額の3項目に変換せよ。ただし、性別コードは男性を”00″、女性を”01″、不明を”99″とする。
df_sales_summary %>%
gather(key = gender_cd, value = sum_amount, male, female, unknown) %>%
mutate(gender_cd = case_when(
gender_cd == "male" ~ "00",
gender_cd == "female" ~ "01",
gender_cd == "unknown" ~ "99",
))
era | gender_cd | sum_amount |
---|---|---|
<dbl> | <chr> | <dbl> |
10 | 00 | 1591 |
20 | 00 | 72940 |
30 | 00 | 177322 |
40 | 00 | 19355 |
50 | 00 | 54320 |
60 | 00 | 272469 |
70 | 00 | 13435 |
80 | 00 | 46360 |
90 | 00 | 0 |
10 | 01 | 149836 |
20 | 01 | 1363724 |
30 | 01 | 693047 |
40 | 01 | 9320791 |
50 | 01 | 6685192 |
60 | 01 | 987741 |
70 | 01 | 29764 |
80 | 01 | 262923 |
90 | 01 | 6260 |
10 | 99 | 4317 |
20 | 99 | 44328 |
30 | 99 | 50441 |
40 | 99 | 483512 |
50 | 99 | 342923 |
60 | 99 | 71418 |
70 | 99 | 2427 |
80 | 99 | 5111 |
90 | 99 | 0 |
解説:
このコードは、前のコードブロックで作成された df_sales_summary データフレームを修正します。
gather()関数は、male, female, unknown列をsum_amountという1つの列に重ね、gender_cdという新しい列を作成し、以前の列名(男性、女性、不明)を保存することによって、データをワイドフォーマットからロングフォーマットに変換するために使用されます。
mutate() 関数は、gender_cd カラムの値を再コード化するために使用されます。これは、case_when()関数を使用して、3つの可能な値を持つ新しい列を作成します。「00 "は男性、"01 "は女性、"99 "は性別不明です。
全体として、このコードはdf_sales_summaryデータフレームをロングフォーマットに整形し、性別の値を標準フォーマットに再コード化することで、他のデータフレームとマージするための準備をしています。
R-045: 顧客データ(df_customer)の生年月日(birth_day)は日付型でデータを保有している。これをYYYYMMDD形式の文字列に変換し、顧客ID(customer_id)とともに10件表示せよ。
df_tmp <- cbind(df_customer["customer_id"],
strftime(
df_customer$birth_day,
format = "%Y%m%d" ))
colnames(df_tmp) <- c("customer_id", "birth_day")
head(df_tmp,10)
customer_id | birth_day | |
---|---|---|
<chr> | <chr> | |
1 | CS021313000114 | 19810429 |
2 | CS037613000071 | 19520401 |
3 | CS031415000172 | 19761004 |
4 | CS028811000001 | 19330327 |
5 | CS001215000145 | 19950329 |
6 | CS020401000016 | 19740915 |
7 | CS015414000103 | 19770809 |
8 | CS029403000008 | 19730817 |
9 | CS015804000004 | 19310502 |
10 | CS033513000180 | 19620711 |
解説:
このコードは、df_customer データフレームから customer_id と birth_day 列を選択して、df_tmp という新しいデータフレームを作成します。
df_customer["customer_id"] 構文は customer_id 列をデータフレームとして(ベクトルとしてではなく)選択するもので、これは cbind() を使って別の列と結合するために必要です。
strftime() 関数は、birth_day カラムを %Y%m%d フォーマットの文字列としてフォーマットするために使用されます。
connames()関数は、データフレームに新しい列名を割り当てるために使用され、列の名前は "customer_id "と "birth_day "になっています。
最後に、head()関数を使用して、df_tmpデータフレームの最初の10行を表示します。
全体として、このコードでは、顧客IDや誕生日に基づいて他のデータフレームとマージするために使用できる2つの列を持つ新しいデータフレームを作成しています。
R-046: 顧客データ(df_customer)の申し込み日(application_date)はYYYYMMDD形式の文字列型でデータを保有している。これを日付型に変換し、顧客ID(customer_id)とともに10件表示せよ。
df_tmp <- cbind(df_customer["customer_id"],
strptime(df_customer$application_date, "%Y%m%d"))
colnames(df_tmp) <- c("customer_id", "application_date")
head(df_tmp, 10)
customer_id | application_date | |
---|---|---|
<chr> | <dttm> | |
1 | CS021313000114 | 2015-09-05 |
2 | CS037613000071 | 2015-04-14 |
3 | CS031415000172 | 2015-05-29 |
4 | CS028811000001 | 2016-01-15 |
5 | CS001215000145 | 2017-06-05 |
6 | CS020401000016 | 2015-02-25 |
7 | CS015414000103 | 2015-07-22 |
8 | CS029403000008 | 2015-05-15 |
9 | CS015804000004 | 2015-06-07 |
10 | CS033513000180 | 2015-07-28 |
解説:
このコードは、df_customer データフレームから customer_id と application_date 列を選択して、df_tmp という新しいデータフレームを作成します。
df_customer["customer_id"] 構文は、customer_id 列をデータフレームとして選択します(ベクトルとしてではなく)。
strptime() 関数は、application_date カラムを変換するために使用します。application_date カラムは、年、月、日を8桁の数字で表す %Y%m%d 形式の文字列で、R で日付と時間を表す POSIXlt クラスオブジェクトに変換します。
connames()関数は、データフレームに新しい列名を割り当てるために使用され、列の名前は「customer_id」と「application_date」となっています。
最後に、head()関数を使用して、df_tmpデータ・フレームの最初の10行を表示します。
全体として、このコードは2つの列を持つ新しいデータフレームを作成し、顧客IDや申込日に基づいて他のデータフレームと結合するために使用することができます。
R-047: レシート明細データ(df_receipt)の売上日(sales_ymd)はYYYYMMDD形式の数値型でデータを保有している。これを日付型に変換し、レシート番号(receipt_no)、レシートサブ番号(receipt_sub_no)とともに10件表示せよ。
df_tmp <- cbind(df_receipt[c("receipt_no", "receipt_sub_no")], strptime(as.character(df_receipt$sales_ymd), "%Y%m%d"))
colnames(df_tmp) <- c("receipt_no", "receipt_sub_no", "sales_ymd")
head(df_tmp, 10)
receipt_no | receipt_sub_no | sales_ymd | |
---|---|---|---|
<int> | <int> | <dttm> | |
1 | 112 | 1 | 2018-11-03 |
2 | 1132 | 2 | 2018-11-18 |
3 | 1102 | 1 | 2017-07-12 |
4 | 1132 | 1 | 2019-02-05 |
5 | 1102 | 2 | 2018-08-21 |
6 | 1112 | 1 | 2019-06-05 |
7 | 1102 | 2 | 2018-12-05 |
8 | 1102 | 1 | 2019-09-22 |
9 | 1112 | 2 | 2017-05-04 |
10 | 1102 | 1 | 2019-10-10 |
解説:
このコードは、df_receipt データフレームから receipt_no, receipt_sub_no, sales_ymd 列を選択し、sales_ymd 列を yyyymmdd 形式の整数から R の日付と時刻を表す POSIXlt クラスオブジェクトに変換して df_tmp という新しいデータフレームを作成します。
df_receipt[c("receipt_no", "receipt_sub_no")] 構文は、recipate_no と receipt_sub_no 列をデータフレームとして選択します(ベクトルとしてではなく)、これは cbind() で別の列と結合するために必要です。
as.character() は、sales_ymd カラムを strptime() 関数に渡す前に、整数から文字ベクトルに変換するために使用します。
strptime()関数は、文字ベクトルsales_ymdをRの日付と時刻を表すPOSIXltクラスオブジェクトに変換するために使用されます。
colnames()関数は、データフレームに新しい列名を割り当てるために使用され、列の名前は "recipate_no", "receipt_sub_no", "sales_ymd "となる。
最後に、head()関数を使用して、df_tmpデータフレームの最初の10行を表示します。
全体として、このコードは、領収書番号、領収書サブ番号、または販売日に基づいて他のデータフレームとマージするために使用できる、3つの列を持つ新しいデータフレームを作成します。
R-048: レシート明細データ(df_receipt)の売上エポック秒(sales_epoch)は数値型のUNIX秒でデータを保有している。これを日付型に変換し、レシート番号(receipt_no)、レシートサブ番号(receipt_sub_no)とともに10件表示せよ。
df_tmp <- cbind(df_receipt[c("receipt_no", "receipt_sub_no")], as.POSIXct(df_receipt$sales_epoch, origin="1970-01-01"))
colnames(df_tmp) <- c("receipt_no", "receipt_sub_no", "sales_ymd")
head(df_tmp, 10)
receipt_no | receipt_sub_no | sales_ymd | |
---|---|---|---|
<int> | <int> | <dttm> | |
1 | 112 | 1 | 2018-11-03 |
2 | 1132 | 2 | 2018-11-18 |
3 | 1102 | 1 | 2017-07-12 |
4 | 1132 | 1 | 2019-02-05 |
5 | 1102 | 2 | 2018-08-21 |
6 | 1112 | 1 | 2019-06-05 |
7 | 1102 | 2 | 2018-12-05 |
8 | 1102 | 1 | 2019-09-22 |
9 | 1112 | 2 | 2017-05-04 |
10 | 1102 | 1 | 2019-10-10 |
解説:
このコードは、元のデータフレームdf_receiptから選択した列を組み合わせて新しいデータフレームdf_tmpを作成します。具体的には、df_receiptから "recipate_no", "receipt_sub_no", "sales_epoch "列を選択し、cbind()という関数で結合しています。
3列目の "sales_epoch "には、Unixのエポックとして知られる1970年1月1日からの秒数が格納されています。次に、as.POSIXct()関数を用いて、この数値をRの日時を表すPOSIXctオブジェクトに変換します。引数のoriginには、エポックが1970年1月1日の0時から始まることを示す「1970-01-01」が設定されています。
最後に、関数colnames()を用いて、df_tmpの列の名前をそれぞれ "recipate_no", "receipt_sub_no", "sales_ymd "に変更した。
その結果、df_tmpのデータフレームには、df_receiptと同じ2つの列("recipate_no "と "recipate_sub_no")と、"sales_epoch "から変換した日付と時間の情報を持つ新しい列 "sales_ymd "が含まれます。df_tmpの最初の10行を表示するためにhead()関数が使用されています。
R-049: レシート明細データ(df_receipt)の売上エポック秒(sales_epoch)を日付型に変換し、「年」だけ取り出してレシート番号(receipt_no)、レシートサブ番号(receipt_sub_no)とともに10件表示せよ。
df_tmp <- cbind(df_receipt[c("receipt_no", "receipt_sub_no")], substring(as.POSIXct(df_receipt$sales_epoch, origin = "1970-01-01"), 1, 4))
colnames(df_tmp) <- c("receipt_no", "receipt_sub_no", "sales_year")
head(df_tmp, 10)
receipt_no | receipt_sub_no | sales_year | |
---|---|---|---|
<int> | <int> | <chr> | |
1 | 112 | 1 | 2018 |
2 | 1132 | 2 | 2018 |
3 | 1102 | 1 | 2017 |
4 | 1132 | 1 | 2019 |
5 | 1102 | 2 | 2018 |
6 | 1112 | 1 | 2019 |
7 | 1102 | 2 | 2018 |
8 | 1102 | 1 | 2019 |
9 | 1112 | 2 | 2017 |
10 | 1102 | 1 | 2019 |
解説:
このコードは、df_receiptデータフレームから3つの列を結合して、df_tmpという新しいデータフレームを作成しています。最初の2つの列、recipate_noとrecipate_sub_noは、df_receiptから直接選択されています。
3番目の列であるsales_yearは、sales_epoch列を日付/時刻形式に変換するために使用されているas.POSIXct()の出力から最初の4文字を抽出するsubstring()関数を使って作成されている。
as.POSIXct()関数は、originパラメータを使用して、Unixエポック時間(1970-01-01)が基準点として使用されることを指定しています。次に、substring()関数を使用して、この日付/時刻形式から年を抽出しています。
最後に、colnames()を使って、新しいデータフレームの対応する列に、"recipate_no", "receipt_sub_no", "sales_year" という列名を割り当てています。そして、head()関数を使用して、新しいデータフレームの最初の10行を表示します。
R-050: レシート明細データ(df_receipt)の売上エポック秒(sales_epoch)を日付型に変換し、「月」だけ取り出してレシート番号(receipt_no)、レシートサブ番号(receipt_sub_no)とともに10件表示せよ。なお、「月」は0埋め2桁で取り出すこと。
df_tmp <- cbind(df_receipt[c("receipt_no", "receipt_sub_no")], substring(as.POSIXct(df_receipt$sales_epoch, origin = "1970-01-01"), 6, 7))
colnames(df_tmp) <- c("receipt_no", "receipt_sub_no", "sales_ymd")
head(df_tmp, 10)
receipt_no | receipt_sub_no | sales_ymd | |
---|---|---|---|
<int> | <int> | <chr> | |
1 | 112 | 1 | 11 |
2 | 1132 | 2 | 11 |
3 | 1102 | 1 | 07 |
4 | 1132 | 1 | 02 |
5 | 1102 | 2 | 08 |
6 | 1112 | 1 | 06 |
7 | 1102 | 2 | 12 |
8 | 1102 | 1 | 09 |
9 | 1112 | 2 | 05 |
10 | 1102 | 1 | 10 |
解説:
このコードでは、"recipate_no", "receipt_sub_no", "sales_ymd" 列を含む新しいデータフレームdf_tmpを作成します。sales_ymd "カラムは、df_receiptの "sales_epoch "カラムから派生しています。
具体的には、substring()関数を用いて、開始位置を6(月成分の最初の文字に相当)、終了位置を7(月成分の最後の文字に相当)として、"sales_epoch "列から月成分を抽出する。この結果得られる文字列は、販売日の月を表しています。
新しいデータフレームdf_tmpは、df_receiptから選択した列を、cbind()関数を使用して抽出した月成分と結合して作成されます。最後に、df_tmpのカラム名が "recipate_no", "receipt_sub_no", "sales_ymd "に更新されています。
全体として、このコードは、df_receiptの "sales_epoch "列から各販売日の月を抽出し、新しいデータフレームdf_tmpに "sales_ymd "という名前の新しい列として追加します。
R-051: レシート明細データ(df_receipt)の売上エポック秒を日付型に変換し、「日」だけ取り出してレシート番号(receipt_no)、レシートサブ番号(receipt_sub_no)とともに10件表示せよ。なお、「日」は0埋め2桁で取り出すこと。
df_tmp <- cbind(df_receipt[c("receipt_no", "receipt_sub_no")], substring(as.POSIXct(df_receipt$sales_epoch, origin = "1970-01-01"), 9, 10))
colnames(df_tmp) <- c("receipt_no", "receipt_sub_no", "sales_day")
head(df_tmp, 10)
receipt_no | receipt_sub_no | sales_day | |
---|---|---|---|
<int> | <int> | <chr> | |
1 | 112 | 1 | 03 |
2 | 1132 | 2 | 18 |
3 | 1102 | 1 | 12 |
4 | 1132 | 1 | 05 |
5 | 1102 | 2 | 21 |
6 | 1112 | 1 | 05 |
7 | 1102 | 2 | 05 |
8 | 1102 | 1 | 22 |
9 | 1112 | 2 | 04 |
10 | 1102 | 1 | 10 |
解説:
このコードでは、"recipate_no"、"recipate_sub_no"、"sales_day "という3つの列を持つdf_tmpという新しいデータフレームを作成しています。recipate_no」と「recipate_sub_no」列は、「df_receipt」データフレームからコピーされたものです。sales_dayカラムは、df_receiptデータフレームのsales_epochカラムから日を抽出することにより作成される。
sales_epoch "カラムは、まずas.POSIXct関数を用いてPOSIXctオブジェクトに変換され、原点が "1970-01-01 "に設定されます。これは、Unixのタイムスタンプ(1970年1月1日からの秒単位)をRのdatetimeフォーマットに変換する一般的な方法です。次に、substring関数を使用して、datetimeオブジェクトのday部分を抽出します。substring関数は、入力文字列、部分文字列の開始位置、部分文字列の終了位置という3つの引数をとります。この場合、部分文字列は位置9(datetime文字列の曜日の部分)から始まり、位置10で終わります。
最後に、colnames関数を使用して、列名を「recipate_no」、「recipate_sub_no」、「sales_day」に更新しています。出来上がったデータフレームdf_tmpには、最初の10行のデータの "recipate_no", "receipt_sub_no", "sales_day "のカラムが表示されています。
R-052: レシート明細データ(df_receipt)の売上金額(amount)を顧客ID(customer_id)ごとに合計の上、売上金額合計に対して2,000円以下を0、2,000円より大きい金額を1に二値化し、顧客ID、売上金額合計とともに10件表示せよ。ただし、顧客IDが”Z”から始まるのものは非会員を表すため、除外して計算すること。
df_receipt %>%
filter(!grepl("^Z", customer_id)) %>%
group_by(customer_id) %>%
summarise(sum_amount=sum(amount), .groups = "drop") %>%
mutate(sales_flg = ifelse(sum_amount > 2000, 1, 0)) %>%
slice(1:10)
customer_id | sum_amount | sales_flg |
---|---|---|
<chr> | <int> | <dbl> |
CS001113000004 | 1298 | 0 |
CS001114000005 | 626 | 0 |
CS001115000010 | 3044 | 1 |
CS001205000004 | 1988 | 0 |
CS001205000006 | 3337 | 1 |
CS001211000025 | 456 | 0 |
CS001212000027 | 448 | 0 |
CS001212000031 | 296 | 0 |
CS001212000046 | 228 | 0 |
CS001212000070 | 456 | 0 |
解説:
このコードは、df_receiptデータフレームに対して以下の処理を実行します。
customer_idカラムが文字 "Z "で始まるすべての行をフィルタリングする.Groups
残りの行をcustomer_idで検索します。
各 customer_id の金額列の合計を計算する。
金額列の合計が2000より大きい場合は1、そうでない場合は0に設定される新しい列sales_flgを作成します。
結果のデータフレームから最初の10行を選択します。
演算子「%>%」を使用して処理を連鎖させることで、コードをより簡潔で読みやすくしています。
以下、ステップバイステップで説明します。
filter(!grepl("^Z", customer_id)): filter()関数は、customer_id列が文字「Z」で始まるすべての行を削除します。grepl()関数は、customer_id列の各要素がパターン^Zにマッチしないか(すなわち、文字「Z」で始まらないか)を示す論理ベクトルを返す。演算子! は論理ベクトルを否定するので、filter()はベクトルがTRUEである行だけを保持します。
group_by(customer_id): group_by()関数は、残りの行をcustomer_idでグループ化します。
summarise(sum_amount=sum(金額), .groups = "ドロップ"): summarise()関数は、各顧客IDのamount列の合計を計算し、その結果をsum_amountという新しい列に格納します。.groups = "drop "引数は、データを要約した後、グループ化構造を削除するようsummarise()に指示します。
mutate(sales_flg = ifelse(sum_amount > 2000, 1, 0)): mutate()関数は、sales_flgという新しい列を作成し、sum_amount列が2000より大きい場合は1、それ以外は0に設定されます。ifelse()関数は、標準的なif...else文のベクトル化バージョンで、条件ベクトルと同じ長さのベクトルを返し、条件ベクトルの要素がTRUEかFALSEかによって、yesまたはnoベクトルのいずれかから要素が選択されます。
slice(1:10) : slice()関数は、結果のデータフレームから最初の10行を選択する。
R-053: 顧客データ(df_customer)の郵便番号(postal_cd)に対し、東京(先頭3桁が100〜209のもの)を1、それ以外のものを0に二値化せよ。さらにレシート明細データ(df_receipt)と結合し、全期間において売上実績のある顧客数を、作成した二値ごとにカウントせよ。
df_customer[c("customer_id", "postal_cd")] %>%
mutate(postal_flg =
ifelse(100 <= as.integer(str_sub(postal_cd, start = 1, end = 3))
& as.integer(str_sub(postal_cd, start = 1, end = 3)) <= 209, 1, 0)) %>% inner_join(df_receipt, by = "customer_id") %>%
group_by(postal_flg) %>%
summarise(customer_cnt = n_distinct(customer_id), .groups = "drop")
postal_flg | customer_cnt |
---|---|
<dbl> | <int> |
0 | 3906 |
1 | 4400 |
解説:
このコードは、以下のタスクを実行します。
データフレームdf_customerから "customer_id "と "postal_cd "の列を選択する。
postal_cd "の最初の3文字が100から209の間の郵便番号を表す場合に1、それ以外の場合に0に設定される新しい列 "postal_flg "を作成する。このチェックにはifelse関数が使用されます。
修正されたdf_customerデータフレームとdf_receiptデータフレームを "customer_id "カラムで結合する。
結果のデータフレームを "postal_flg "カラムでグループ化する。
n_distinct関数を使用して、"postal_flg "の各値に対するユニークな顧客IDの数を計算します。
引数「.groups = 'drop'」でグループ化情報を削除する。
postal_flg "と "customer_cnt "の列を持つ結果のデータフレームを返す。
全体として、このコードはdf_receiptに含まれる郵便番号が100~209の範囲にある顧客の数を、その範囲に郵便番号があるかどうかでグループ化して計算しています。
R-054: 顧客データ(df_customer)の住所(address)は、埼玉県、千葉県、東京都、神奈川県のいずれかとなっている。都道府県毎にコード値を作成し、顧客ID、住所とともに10件表示せよ。値は埼玉県を11、千葉県を12、東京都を13、神奈川県を14とすること。
# コード例1(固定で切り出す)
df_customer %>%
mutate(prefecture_cd =
case_when(
str_sub(address, start = 1, end = 3) == "埼玉県" ~ "11",
str_sub(address, start = 1, end = 3) == "千葉県" ~ "12",
str_sub(address, start = 1, end = 3) == "東京都" ~ "13",
str_sub(address, start = 1, end = 3) == "神奈川" ~ "14")) %>% select(customer_id, address, prefecture_cd) %>%
slice(1:10)
customer_id | address | prefecture_cd |
---|---|---|
<chr> | <chr> | <chr> |
CS021313000114 | 神奈川県伊勢原市粟窪********** | 14 |
CS037613000071 | 東京都江東区南砂********** | 13 |
CS031415000172 | 東京都渋谷区代々木********** | 13 |
CS028811000001 | 神奈川県横浜市泉区和泉町********** | 14 |
CS001215000145 | 東京都大田区仲六郷********** | 13 |
CS020401000016 | 東京都板橋区若木********** | 13 |
CS015414000103 | 東京都江東区北砂********** | 13 |
CS029403000008 | 千葉県浦安市海楽********** | 12 |
CS015804000004 | 東京都江東区北砂********** | 13 |
CS033513000180 | 神奈川県横浜市旭区善部町********** | 14 |
解説:
このコードは、以下の動作を行います。
データフレームdf_customerを入力として受け取ります。
df_customerデータフレームに新しい列prefecture_cdを作成する。
prefecture_cd列の値は、address列の値に基づいている。
住所欄の最初の3文字が "埼玉県 "であれば、prefecture_cd欄の値は "11 "に設定される。
住所欄の最初の3文字が "千葉県 "の場合、prefecture_cd欄の値は "12 "に設定される。
住所欄の最初の3文字が "東京都 "の場合、prefecture_cd欄の値は "13 "に設定される。
住所カラムの最初の 3 文字が "神奈川 "の場合、prefecture_cd カラムの値は "14 "に設定される。
customer_id、address、prefecture_cdの各カラムが出力対象として選択される。
slice(1:10)関数は、結果のデータフレームの最初の10行を表示するために使用される。
# コード例2(正規表現を使う)
df_customer %>%
mutate(prefecture_cd =
case_when(
str_extract(address, pattern = "^.*?[都道府県]") == "埼玉県" ~ "11",
str_extract(address, pattern = "^.*?[都道府県]") == "千葉県" ~ "12",
str_extract(address, pattern = "^.*?[都道府県]") == "東京都" ~ "13",
str_extract(address, pattern = "^.*?[都道府県]") == "神奈川県" ~ "14")) %>% select(customer_id, address, prefecture_cd) %>%
slice(1:10)
customer_id | address | prefecture_cd |
---|---|---|
<chr> | <chr> | <chr> |
CS021313000114 | 神奈川県伊勢原市粟窪********** | 14 |
CS037613000071 | 東京都江東区南砂********** | 13 |
CS031415000172 | 東京都渋谷区代々木********** | 13 |
CS028811000001 | 神奈川県横浜市泉区和泉町********** | 14 |
CS001215000145 | 東京都大田区仲六郷********** | 13 |
CS020401000016 | 東京都板橋区若木********** | 13 |
CS015414000103 | 東京都江東区北砂********** | 13 |
CS029403000008 | 千葉県浦安市海楽********** | 12 |
CS015804000004 | 東京都江東区北砂********** | 13 |
CS033513000180 | 神奈川県横浜市旭区善部町********** | 14 |
解説:
このコードは、R の dplyr パッケージを使用して、df_customer データフレームに新しい列 prefecture_cd を作成し、日本の都道府県のサブセットについて都道府県名と都道府県コードを対応付ける。具体的には、データフレームに新しいカラムを追加する mutate() 関数と、正規表現のパターンマッチに基づいて都道府県名とコードを対応付ける case_when() 関数を使用しています。
以下、コードを一行一行で説明します。
df_customer %>% df_customer データフレームを入力としてパイプラインを開始します。
mutate(prefecture_cd = データフレームに新しい列 prefecture_cd を作成します。
case_when( 都道府県名とコードを対応付けるための一連の条件を開始します。
str_extract(address, pattern = "^.*?[都道府県]") == "埼玉県" ~ "11", 正規表現パターン "^.*?[都道府県]" にマッチして住所列から県名を抽出し、"埼玉県" を "11" と対応付ける。
str_extract(address, pattern = "^.*?[都道府県]") == "千葉県" ~ "12", "千葉県 "に対して同様の処理を行い、"12 "にマッピングする。
str_extract(address, pattern = "^.*?[都道府県]") == "東京都" ~ "13", "東京都 "と同じ処理を行い、"13 "に対応させる。
str_extract(address, pattern = "^.*?[都道府県]") == "神奈川県" ~ "14")) は、"神奈川県 "と同じように "14 "に対応させるために使用します。
select(customer_id, address, prefecture_cd) は、データフレームから customer_id, address, prefecture_cd の列のみを選択する。
slice(1:10) は、結果のデータフレームの最初の10行のみを選択する。
R-055: レシート明細(df_receipt)データの売上金額(amount)を顧客ID(customer_id)ごとに合計し、その合計金額の四分位点を求めよ。その上で、顧客ごとの売上金額合計に対して以下の基準でカテゴリ値を作成し、顧客ID、売上金額合計とともに10件表示せよ。カテゴリ値は順に1〜4とする。
- 最小値以上第1四分位未満 ・・・ 1を付与
- 第1四分位以上第2四分位未満 ・・・ 2を付与
- 第2四分位以上第3四分位未満 ・・・ 3を付与
- 第3四分位以上 ・・・ 4を付与
df_receipt %>%
group_by(customer_id) %>%
summarise(sum_amount = sum(amount), .groups = "drop") %>%
mutate(pct_group = case_when(
sum_amount < quantile(sum_amount)[2] ~ "1",
sum_amount < quantile(sum_amount)[3] ~ "2",
sum_amount < quantile(sum_amount)[4] ~ "3",
quantile(sum_amount)[4] <= sum_amount ~ "4"
)) %>%
slice(1:10)
customer_id | sum_amount | pct_group |
---|---|---|
<chr> | <int> | <chr> |
CS001113000004 | 1298 | 2 |
CS001114000005 | 626 | 2 |
CS001115000010 | 3044 | 3 |
CS001205000004 | 1988 | 3 |
CS001205000006 | 3337 | 3 |
CS001211000025 | 456 | 1 |
CS001212000027 | 448 | 1 |
CS001212000031 | 296 | 1 |
CS001212000046 | 228 | 1 |
CS001212000070 | 456 | 1 |
解説:
本コードは、以下の処理を行う。
データフレームdf_receiptを取得します。
df_receiptの行をcustomer_idでグループ化します。
同じcustomer_idを持つ行のグループごとに、sum()関数を使用して金額列の合計を計算し、その結果をsum_amountという新しい列に格納します。
.groups = "drop "を使用して、グループ化構造を削除します。
case_when()関数を使用して新しい列pct_groupを作成し、sum_amount列の値に基づいて各行を四分位数に割り当てる。四分位数はquantile()関数を用いて決定される。
最後に、結果のデータフレームの最初の10行を選択し、df_tmpという新しいデータフレームに格納します。
例えば、quantile(sum_amount)が[100, 200, 300, 400]を返す場合、結果のpct_group列は、sum_amountが200未満の行には「1」、200から299までの行には「2」、300から399までの行には「3」、400以上の行には「4」という値を持つことになります。
R-056: 顧客データ(df_customer)の年齢(age)をもとに10歳刻みで年代を算出し、顧客ID(customer_id)、生年月日(birth_day)とともに10件表示せよ。ただし、60歳以上は全て60歳代とすること。年代を表すカテゴリ名は任意とする。
# コード例1
df_customer[c("customer_id", "birth_day", "age")] %>%
mutate(era = trunc(age / 10) * 10) %>%
mutate(era = case_when( era < 60 ~ era, era >= 60 ~ 60 )) %>%
slice(1:10)
customer_id | birth_day | age | era |
---|---|---|---|
<chr> | <date> | <int> | <dbl> |
CS021313000114 | 1981-04-29 | 37 | 30 |
CS037613000071 | 1952-04-01 | 66 | 60 |
CS031415000172 | 1976-10-04 | 42 | 40 |
CS028811000001 | 1933-03-27 | 86 | 60 |
CS001215000145 | 1995-03-29 | 24 | 20 |
CS020401000016 | 1974-09-15 | 44 | 40 |
CS015414000103 | 1977-08-09 | 41 | 40 |
CS029403000008 | 1973-08-17 | 45 | 40 |
CS015804000004 | 1931-05-02 | 87 | 60 |
CS033513000180 | 1962-07-11 | 56 | 50 |
解説:
このコードは、顧客データを含むデータフレームdf_customerを操作する。このコードでは、customer_id、birth_day、ageの3つのカラムを選択します。そして、年齢に基づいて各顧客の年齢を計算します。
最初のmutate関数は、各顧客の年齢を10で割り、その結果を最も近い整数に切り捨て、その整数に10を乗じて年齢を計算する。たとえば、ある顧客が43歳であれば、その年齢は40歳になります。25歳の顧客は、年齢が20になります。
2番目のmutate関数は、60以上のeraの値を60に置き換えます。これは、60歳以上のすべての顧客を1つの時代カテゴリーにまとめるために行われます。
このデータフレームには、各顧客の顧客ID、生年月日、年齢、計算された年齢が含まれています。
# Code example 2 (when using min)
df_customer[c("customer_id", "birth_day","age")] %>%
mutate(era = sapply(1:nrow(df_customer),
function(x){
return (min(trunc(.[x, 3] / 10) * 10, 60))
})) %>%
slice(1:10)
customer_id | birth_day | age | era |
---|---|---|---|
<chr> | <date> | <int> | <dbl> |
CS021313000114 | 1981-04-29 | 37 | 30 |
CS037613000071 | 1952-04-01 | 66 | 60 |
CS031415000172 | 1976-10-04 | 42 | 40 |
CS028811000001 | 1933-03-27 | 86 | 60 |
CS001215000145 | 1995-03-29 | 24 | 20 |
CS020401000016 | 1974-09-15 | 44 | 40 |
CS015414000103 | 1977-08-09 | 41 | 40 |
CS029403000008 | 1973-08-17 | 45 | 40 |
CS015804000004 | 1931-05-02 | 87 | 60 |
CS033513000180 | 1962-07-11 | 56 | 50 |
解説:
このコードは、生年月日から各顧客の「時代」を計算するために使用されます。以下、各行の処理内容です。
df_customerデータフレームから "customer_id"、"birth_day"、"age "カラムのみを選択します。
mutate関数を使って、「era」という新しいカラムを追加する。このカラムの値は、"age "カラムを10で割って階数を取り(つまり、最も近い整数に切り捨て)、10を掛けることで計算されます。これにより、年齢が効果的に10の倍数に切り捨てられ、各顧客のおおよその「時代」が示されます。
再び「mutate」関数を使って、eraの値を調整します。eraが60未満の場合は、そのままにします。eraが60以上の場合は、60に設定されます。
slice関数で、出来上がったデータフレームの最初の10行を選択する。
このコードでは、データフレームの各行に計算を適用するために「sapply」関数を使用していることに注意してください。この方法は有効ですが、非常に大きなデータフレームの場合、時間がかかることがあります。より効率的な方法は、代わりにベクトル化された操作を使用することです。
R-057: 056の抽出結果と性別コード(gender_cd)により、新たに性別×年代の組み合わせを表すカテゴリデータを作成し、10件表示せよ。組み合わせを表すカテゴリの値は任意とする。
# 性別コード1桁と年代コード2桁を連結した性年代コードを生成する
df_customer[c("customer_id", "gender_cd", "birth_day", "age")] %>%
mutate(era = trunc(age / 10) * 10) %>%
mutate(era = case_when( era < 60 ~ formatC(era, width = 2,flag = "0"),
era >= 60 ~ formatC(60, width = 2,flag = "0"))) %>%
mutate(gender_era = paste(gender_cd, era, sep = "")) %>%
slice(1:10)
customer_id | gender_cd | birth_day | age | era | gender_era |
---|---|---|---|---|---|
<chr> | <chr> | <date> | <int> | <chr> | <chr> |
CS021313000114 | 1 | 1981-04-29 | 37 | 30 | 130 |
CS037613000071 | 9 | 1952-04-01 | 66 | 60 | 960 |
CS031415000172 | 1 | 1976-10-04 | 42 | 40 | 140 |
CS028811000001 | 1 | 1933-03-27 | 86 | 60 | 160 |
CS001215000145 | 1 | 1995-03-29 | 24 | 20 | 120 |
CS020401000016 | 0 | 1974-09-15 | 44 | 40 | 040 |
CS015414000103 | 1 | 1977-08-09 | 41 | 40 | 140 |
CS029403000008 | 0 | 1973-08-17 | 45 | 40 | 040 |
CS015804000004 | 0 | 1931-05-02 | 87 | 60 | 060 |
CS033513000180 | 1 | 1962-07-11 | 56 | 50 | 150 |
解説:
このコードは、df_customerというデータフレームを操作するもので、おそらく顧客に関する情報が含まれていると思われます。以下、各行の解説をします。
df_customer[c("customer_id", "gender_cd", "birth_day", "age")]: df_customer[c("customer_id", "gender_cd", "birth_day", "age")]: データフレームdf_customerから列 "customer_id", "gender_cd", "birth_day", "age "を選択しています。
%>%: データフレームを次の操作につなげるために使用するパイプ演算子です。
mutate(era = trunc(age / 10) * 10)。このカラムは、各顧客の年齢を10で割って小数点以下を切り捨て、10を掛けることで算出されます。これにより、顧客は10歳の範囲に分類されます。
mutate(era = case_when( era < 60 ~ formatC(era, width = 2,flag = "0"), era >= 60 ~ formatC(60, width = 2,flag = "0"))): case_when文を適用して「era」列を変更し、60未満の「era」値を先頭の0を含む2桁の文字列に変換し(例えば、10~19歳は「10」)、60以上の「era」値を「60」に設定します。
mutate(gender_era = paste(gender_cd, era, sep = "")): gender_era "という新しい列をデータフレームに追加する。"gender_cd "と "era "の列をpaste関数で連結して作成する。sep = ""引数は、2つの値の間にセパレータがないことを指定する。
slice(1:10) : データフレームの最初の10行を選択します。
R-058: 顧客データ(df_customer)の性別コード(gender_cd)をダミー変数化し、顧客ID(customer_id)とともに10件表示せよ。
# コード例1(すべてのコード値を項目化)
dummy_gender_model <- dummyVars(~gender_cd, data = df_customer, fullRank = FALSE)
dummy_gender <- predict(dummy_gender_model, df_customer)
head(cbind(df_customer["customer_id"], dummy_gender), 10)
customer_id | gender_cd0 | gender_cd1 | gender_cd9 | |
---|---|---|---|---|
<chr> | <dbl> | <dbl> | <dbl> | |
1 | CS021313000114 | 0 | 1 | 0 |
2 | CS037613000071 | 0 | 0 | 1 |
3 | CS031415000172 | 0 | 1 | 0 |
4 | CS028811000001 | 0 | 1 | 0 |
5 | CS001215000145 | 0 | 1 | 0 |
6 | CS020401000016 | 1 | 0 | 0 |
7 | CS015414000103 | 0 | 1 | 0 |
8 | CS029403000008 | 1 | 0 | 0 |
9 | CS015804000004 | 1 | 0 | 0 |
10 | CS033513000180 | 0 | 1 | 0 |
解説:
このコードは、データフレームdf_customerのカテゴリ変数 "gender_cd "に対してダミー変数を作成します。まず、"caret "パッケージのdummyVars関数を用いてモデルオブジェクトを作成します。fullRank引数はFALSEに設定され、カテゴリ変数の最初のレベル(他のレベルから推論できる)を削除しています。
2行目は、元のデータフレームdf_customerにモデルを適用して、ダミー変数を持つ新しいデータフレームを作成します。第1引数にdummy_gender_model、第2引数にdf_customerを持つpredict関数は、ダミー変数を作成するためにデータフレームにモデルを適用します。
最後に、cbind関数でdf_customerのcustomer_id列をダミー変数と結合し、head()で最初の10行を表示します。
# コード例2(fullRank=TRUEとすることで項目を一つ削ることができる)
dummy_gender_model <- dummyVars(~gender_cd, data = df_customer, fullRank = TRUE)
dummy_gender <- predict(dummy_gender_model, df_customer)
head(cbind(df_customer["customer_id"], dummy_gender), 3)
customer_id | gender_cd1 | gender_cd9 | |
---|---|---|---|
<chr> | <dbl> | <dbl> | |
1 | CS021313000114 | 1 | 0 |
2 | CS037613000071 | 0 | 1 |
3 | CS031415000172 | 1 | 0 |
解説:
このコードは、caret パッケージの dummyVars() と predict() 関数を使用して、df_customer dataframe の gender_cd 列のワンホットエンコーディングを実行します。
dummyVars() 関数は、数式で指定されたカテゴリ変数のカテゴリを表す、ダミー変数のセットを作成します。この場合、式 ~gender_cd は、ダミー変数を作成するために gender_cd 変数を使用するよう指定します。引数fullRank = TRUEは、ダミー変数のエンコードに参照レベルを含めることを指定する。
次に、predict() 関数を使用して、元の df_customer データフレームに符号化を適用します。その結果、エンコードされたデータフレームは dummy_gender 変数に格納される。
最後に、head()関数を使用して、dummy_gender dataframeの最初の3行と、cbind()を使用して追加された元のdf_customer dataframeのcustomer_idカラムを表示します。これにより、最初の数人の顧客のエンコーディングを確認することができます。各顧客のダミー変数のエンコーディングは、その性別に対応する列の1と、もう一方の性別に対応する列の0から構成されることになります。
R-059: レシート明細データ(df_receipt)の売上金額(amount)を顧客ID(customer_id)ごとに合計し、売上金額合計を平均0、標準偏差1に標準化して顧客ID、売上金額合計とともに10件表示せよ。標準化に使用する標準偏差は、分散の平方根、もしくは不偏分散の平方根のどちらでも良いものとする。ただし、顧客IDが”Z”から始まるのものは非会員を表すため、除外して計算すること。
# Rのscaleを使用しているため不偏分散の平方根による標準偏差で標準化される
df_receipt %>%
filter(!grepl("^Z", customer_id)) %>%
group_by(customer_id) %>%
summarise(sum_amount = sum(amount), .groups = "drop") %>%
mutate(std_amount = scale(sum_amount, center = TRUE, scale = TRUE)) %>%
slice(1:10)
customer_id | sum_amount | std_amount |
---|---|---|
<chr> | <int> | <dbl[,1]> |
CS001113000004 | 1298 | -0.4593502 |
CS001114000005 | 626 | -0.7063478 |
CS001115000010 | 3044 | 0.1824025 |
CS001205000004 | 1988 | -0.2057366 |
CS001205000006 | 3337 | 0.2900964 |
CS001211000025 | 456 | -0.7688324 |
CS001212000027 | 448 | -0.7717728 |
CS001212000031 | 296 | -0.8276413 |
CS001212000046 | 228 | -0.8526351 |
CS001212000070 | 456 | -0.7688324 |
解説:
このコードは、以下のステップを実行します。
df_receiptデータフレームで、customer_id列が文字 "Z "で始まらない行をフィルタリングします。これは、dplyrパッケージのfilter関数と、customer_id列に正規表現パターンをマッチさせるgrepl関数を使用して行われます。
group_by関数を使用して、結果の行をcustomer_idでグループ化する。
dplyrのsummarise関数を使用して、各グループの金額列の合計を計算する。
std_amount という新しい列を作成し、各顧客の金額の標準化された合計を格納します。scale関数を使用して、データを平均値でセンタリングし(center = TRUE)、標準偏差が1になるようにスケーリングして、金額の合計を標準化します(scale = TRUE)。
最後に、slice関数を使用して、customer_idとstd_amountの列を含む、結果のデータフレームの最初の10行を選択するために使用されます。
R-060: レシート明細データ(df_receipt)の売上金額(amount)を顧客ID(customer_id)ごとに合計し、売上金額合計を最小値0、最大値1に正規化して顧客ID、売上金額合計とともに10件表示せよ。ただし、顧客IDが”Z”から始まるのものは非会員を表すため、除外して計算すること。
df_receipt %>% filter(!grepl("^Z", customer_id)) %>%
group_by(customer_id) %>%
summarise(sum_amount = sum(amount), .groups = "drop") %>%
mutate( scale_amount = scale(sum_amount, center = min(sum_amount), scale = max(sum_amount) - min(sum_amount)))%>%
slice(1:10)
customer_id | sum_amount | scale_amount |
---|---|---|
<chr> | <int> | <dbl[,1]> |
CS001113000004 | 1298 | 0.053354188 |
CS001114000005 | 626 | 0.024157108 |
CS001115000010 | 3044 | 0.129214460 |
CS001205000004 | 1988 | 0.083333333 |
CS001205000006 | 3337 | 0.141944734 |
CS001211000025 | 456 | 0.016770942 |
CS001212000027 | 448 | 0.016423358 |
CS001212000031 | 296 | 0.009819256 |
CS001212000046 | 228 | 0.006864790 |
CS001212000070 | 456 | 0.016770942 |
解説:
このコードは、df_receiptデータフレームに対して、以下の処理を実行します。
customer_idが文字 "Z "で始まる行をフィルタリングする。
残りの行をcustomer_idでグループ化する。
customer_idの各グループの金額の合計を計算する。
出来上がったデータフレームを変異させ、sum_amountのスケーリングバージョンである新しい列scale_amountを追加します。scale関数は、sum_amountの最小値が0、最大値が1になるようにスケーリングします。
出来上がったデータフレームをスライスして、最初の10行だけを残します。
要約すると、このコードはdf_receiptの各顧客が使った金額の合計を計算し、それらの値を最小値が0、最大値が1になるようにスケーリングしています。結果として得られるscale_amount値は、異なる顧客の相対的な支出パターンを比較したい場合など、ある種の分析に有用です。
Comment