データサイエンス100本ノック（構造化データ加工編） - Julia/DataFramesMeta

データサイエンス100本ノック（構造化データ加工編） - Julia/DataFramesMeta

**別稿では Base の場合と Query を使う場合の解答例を示した。**
https://qiita.com/WolfMoon/items/dea015e37f83da32b0fa
**本稿では DataFramesMeta を使う場合の解答例を示す。**

# ファイル等の準備

https://github.com/The-Japan-DataScientist-Society/100knocks-preprocess
に書かれているように進めればよいが，デスクトップマシンに Python の実行環境がある場合には，そのページの中程の Requirement > Cloud Service > Colaboratory > Open in Colab をクリックし，最初のセルの

`import os`<br>
`import pandas as pd`<br>
  `:`<br>
`df_geocode = pd.read_csv("data/geocode.csv", dtype=dtype)`

を実行すれば，作業ディレクトリの下に `100knocks-preprocess/docker/work/data/` というディレクトリができ，これをベースディレクトリとして以下のように 6 個のデータファイルを読み込めば，始めることができる。

なお，それぞれのファイルの列を数値として読み込むか文字列として読み込むかはユーザが指定しなければならないことがある。入力結果を確認し，想定と違う場合には読み込み方を指示しなければならない。Julia では CSV.read() の types 引数で指定する。

```julia
#using DataFrames
using DataFramesMeta
using CSV
#using Query

basedirectory = "../Python/100knocks-preprocess/docker/work/data/"
df_customer = CSV.read(basedirectory * "customer.csv", DataFrame, types=Dict(3 => String, 10 => String));
df_category = CSV.read(basedirectory * "category.csv", DataFrame, types=String);
df_product = CSV.read(basedirectory * "product.csv", DataFrame, types=Dict(2 => String, 3 => String, 4 => String));
df_receipt = CSV.read(basedirectory * "receipt.csv", DataFrame);
df_store = CSV.read(basedirectory * "store.csv", DataFrame, types=Dict(3 => String));
df_geocode = CSV.read(basedirectory * "geocode.csv", DataFrame);

head(df, n=10) = begin println("size = $(size(df))"); println(first(df, n)) end;
```

はじめに
- ~~初めに以下のセルを実行してください~~
- ~~必要なライブラリのインポートとデータベース（PostgreSQL）からのデータ読み込みを行います~~
- ~~pandas等、利用が想定されるライブラリは以下セルでインポートしています~~
- ~~その他利用したいライブラリがあれば適宜インストールしてください（"!pip install ライブラリ名"でインストールも可能）~~
- 処理は複数回に分けても構いません
- 名前、住所等はダミーデータであり、実在するものではありません

# 入力したデータフレームの確認

**入力後のデータフレームの確認は，ここでは先頭 10 件を表示させ，目視で確認するということであるが，それぞれの列がどのように読み取られているかを確認すべきである。**

**実際には，各項目の度数分布を求めたり，ヒストグラムを描いたりして不適切なデータがあるかないかを確認すべきである。また，欠損値の有無と欠損値に特別の値が与えられているかどうかなどを確認しておく必要がある。**

## 1.　単純な目視確認

---
> J-001: レシート明細データ（df_receipt）から全項目の先頭10件を表示し、どのようなデータを保有しているか目視で確認せよ。

```julia
head(df_receipt)
```

    size = (104681, 9)
    [1m10×9 DataFrame[0m
    [1m Row [0m│[1m sales_ymd [0m[1m sales_epoch [0m[1m store_cd [0m[1m receipt_no [0m[1m receipt_sub_no [0m[1m customer_id    [0m[1m product_cd [0m[1m quantity [0m[1m amount [0m
         │[90m Int64     [0m[90m Int64       [0m[90m String7  [0m[90m Int64      [0m[90m Int64          [0m[90m String15       [0m[90m String15   [0m[90m Int64    [0m[90m Int64  [0m
    ─────┼────────────────────────────────────────────────────────────────────────────────────────────────────────────
       1 │  20181103   1541203200  S14006           112               1  CS006214000001  P070305012         1     158
       2 │  20181118   1542499200  S13008          1132               2  CS008415000097  P070701017         1      81
       3 │  20170712   1499817600  S14028          1102               1  CS028414000014  P060101005         1     170
       4 │  20190205   1549324800  S14042          1132               1  ZZ000000000000  P050301001         1      25
       5 │  20180821   1534809600  S14025          1102               2  CS025415000050  P060102007         1      90
       6 │  20190605   1559692800  S13003          1112               1  CS003515000195  P050102002         1     138
       7 │  20181205   1543968000  S14024          1102               2  CS024514000042  P080101005         1      30
       8 │  20190922   1569110400  S14040          1102               1  CS040415000178  P070501004         1     128
       9 │  20170504   1493856000  S13020          1112               2  ZZ000000000000  P071302010         1     770
      10 │  20191010   1570665600  S14027          1102               1  CS027514000015  P071101003         1     680

## 2.　必要な項目（列）のみを表示する

---
> J-002: レシート明細データ（df_receipt）から売上年月日（sales_ymd）、顧客ID（customer_id）、商品コード（product_cd）、売上金額（amount）の順に列を指定し、10件表示せよ。

```julia
@select(df_receipt, :sales_ymd, :customer_id, :product_cd, :amount) |> head
```

    size = (104681, 4)
    [1m10×4 DataFrame[0m
    [1m Row [0m│[1m sales_ymd [0m[1m customer_id    [0m[1m product_cd [0m[1m amount [0m
         │[90m Int64     [0m[90m String15       [0m[90m String15   [0m[90m Int64  [0m
    ─────┼───────────────────────────────────────────────
       1 │  20181103  CS006214000001  P070305012     158
       2 │  20181118  CS008415000097  P070701017      81
       3 │  20170712  CS028414000014  P060101005     170
       4 │  20190205  ZZ000000000000  P050301001      25
       5 │  20180821  CS025415000050  P060102007      90
       6 │  20190605  CS003515000195  P050102002     138
       7 │  20181205  CS024514000042  P080101005      30
       8 │  20190922  CS040415000178  P070501004     128
       9 │  20170504  ZZ000000000000  P071302010     770
      10 │  20191010  CS027514000015  P071101003     680

---
> J-003: レシート明細データ（df_receipt）から売上年月日（sales_ymd）、顧客ID（customer_id）、商品コード（product_cd）、売上金額（amount）の順に列を指定し、10件表示せよ。ただし、sales_ymdをsales_dateに項目名を変更しながら抽出すること。

```julia
@select(df_receipt, :sales_date=:sales_ymd, :customer_id, :product_cd, :amount) |> head
```

    size = (104681, 4)
    [1m10×4 DataFrame[0m
    [1m Row [0m│[1m sales_date [0m[1m customer_id    [0m[1m product_cd [0m[1m amount [0m
         │[90m Int64      [0m[90m String15       [0m[90m String15   [0m[90m Int64  [0m
    ─────┼────────────────────────────────────────────────
       1 │   20181103  CS006214000001  P070305012     158
       2 │   20181118  CS008415000097  P070701017      81
       3 │   20170712  CS028414000014  P060101005     170
       4 │   20190205  ZZ000000000000  P050301001      25
       5 │   20180821  CS025415000050  P060102007      90
       6 │   20190605  CS003515000195  P050102002     138
       7 │   20181205  CS024514000042  P080101005      30
       8 │   20190922  CS040415000178  P070501004     128
       9 │   20170504  ZZ000000000000  P071302010     770
      10 │   20191010  CS027514000015  P071101003     680

# 行と列の選択

## 3.　論理演算式による行の選択

Quiery の @filter() に比べて rsubset() の書き方はシンプルでわかりやすい。

---
> J-004: レシート明細データ（df_receipt）から売上日（sales_ymd）、顧客ID（customer_id）、商品コード（product_cd）、売上金額（amount）の順に列を指定し、以下の条件を満たすデータを抽出せよ。
> - 顧客ID（customer_id）が"CS018205000001"

```julia
df = @chain df_receipt begin
    @select(:sales_ymd, :customer_id, :product_cd, :amount)
    @rsubset(:customer_id == "CS018205000001")
end
println(df)
```

    [1m12×4 DataFrame[0m
    [1m Row [0m│[1m sales_ymd [0m[1m customer_id    [0m[1m product_cd [0m[1m amount [0m
         │[90m Int64     [0m[90m String15       [0m[90m String15   [0m[90m Int64  [0m
    ─────┼───────────────────────────────────────────────
       1 │  20180911  CS018205000001  P071401012    2200
       2 │  20180414  CS018205000001  P060104007     600
       3 │  20170614  CS018205000001  P050206001     990
       4 │  20170614  CS018205000001  P060702015     108
       5 │  20190216  CS018205000001  P071005024     102
       6 │  20180414  CS018205000001  P071101002     278
       7 │  20190226  CS018205000001  P070902035     168
       8 │  20190924  CS018205000001  P060805001     495
       9 │  20190226  CS018205000001  P071401020    2200
      10 │  20180911  CS018205000001  P071401005    1100
      11 │  20190216  CS018205000001  P040101002     218
      12 │  20190924  CS018205000001  P091503001     280

---
> J-005: レシート明細データ（df_receipt）から売上日（sales_ymd）、顧客ID（customer_id）、商品コード（product_cd）、売上金額（amount）の順に列を指定し、以下の全ての条件を満たすデータを抽出せよ。
> - 顧客ID（customer_id）が"CS018205000001"
> - 売上金額（amount）が1,000以上

```julia
df = @chain df_receipt begin
    @select(:sales_ymd, :customer_id, :product_cd, :amount)
    @rsubset(:customer_id == "CS018205000001")
    @rsubset(:amount >= 1000)
end
println(df)
```

    [1m3×4 DataFrame[0m
    [1m Row [0m│[1m sales_ymd [0m[1m customer_id    [0m[1m product_cd [0m[1m amount [0m
         │[90m Int64     [0m[90m String15       [0m[90m String15   [0m[90m Int64  [0m
    ─────┼───────────────────────────────────────────────
       1 │  20180911  CS018205000001  P071401012    2200
       2 │  20190226  CS018205000001  P071401020    2200
       3 │  20180911  CS018205000001  P071401005    1100

複数個の論理演算式は一つにまとめることができる。

```julia
df = @chain df_receipt begin
    @select(:sales_ymd, :customer_id, :product_cd, :amount)
    @rsubset(:customer_id == "CS018205000001" && :amount >= 1000)
end
println(df)
```

    [1m3×4 DataFrame[0m
    [1m Row [0m│[1m sales_ymd [0m[1m customer_id    [0m[1m product_cd [0m[1m amount [0m
         │[90m Int64     [0m[90m String15       [0m[90m String15   [0m[90m Int64  [0m
    ─────┼───────────────────────────────────────────────
       1 │  20180911  CS018205000001  P071401012    2200
       2 │  20190226  CS018205000001  P071401020    2200
       3 │  20180911  CS018205000001  P071401005    1100

---
> J-006: レシート明細データ（df_receipt）から売上日（sales_ymd）、顧客ID（customer_id）、商品コード（product_cd）、売上数量（quantity）、売上金額（amount）の順に列を指定し、以下の全ての条件を満たすデータを抽出せよ。
> - 顧客ID（customer_id）が"CS018205000001"
> - 売上金額（amount）が1,000以上または売上数量（quantity）が5以上

```julia
df = @chain df_receipt begin
    @select(:sales_ymd, :customer_id, :product_cd, :quantity, :amount)
    @rsubset(:customer_id == "CS018205000001" && (:amount >= 1000 || :quantity >= 5))
end
println(df)
```

    [1m5×5 DataFrame[0m
    [1m Row [0m│[1m sales_ymd [0m[1m customer_id    [0m[1m product_cd [0m[1m quantity [0m[1m amount [0m
         │[90m Int64     [0m[90m String15       [0m[90m String15   [0m[90m Int64    [0m[90m Int64  [0m
    ─────┼─────────────────────────────────────────────────────────
       1 │  20180911  CS018205000001  P071401012         1    2200
       2 │  20180414  CS018205000001  P060104007         6     600
       3 │  20170614  CS018205000001  P050206001         5     990
       4 │  20190226  CS018205000001  P071401020         1    2200
       5 │  20180911  CS018205000001  P071401005         1    1100

---
> J-007: レシート明細データ（df_receipt）から売上日（sales_ymd）、顧客ID（customer_id）、商品コード（product_cd）、売上金額（amount）の順に列を指定し、以下の全ての条件を満たすデータを抽出せよ。
> - 顧客ID（customer_id）が"CS018205000001"
> - 売上金額（amount）が1,000以上2,000以下

```julia
df = @chain df_receipt begin
    @select(:sales_ymd, :customer_id, :product_cd, :amount)
    @rsubset(:customer_id == "CS018205000001" && 1000 <= :amount <= 2000)
end
println(df)
```

    [1m1×4 DataFrame[0m
    [1m Row [0m│[1m sales_ymd [0m[1m customer_id    [0m[1m product_cd [0m[1m amount [0m
         │[90m Int64     [0m[90m String15       [0m[90m String15   [0m[90m Int64  [0m
    ─────┼───────────────────────────────────────────────
       1 │  20180911  CS018205000001  P071401005    1100

---
> J-008: レシート明細データ（df_receipt）から売上日（sales_ymd）、顧客ID（customer_id）、商品コード（product_cd）、売上金額（amount）の順に列を指定し、以下の全ての条件を満たすデータを抽出せよ。
> - 顧客ID（customer_id）が"CS018205000001"
> - 商品コード（product_cd）が"P071401019"以外

```julia
df = @chain df_receipt begin
    @select(:sales_ymd, :customer_id, :product_cd, :amount)
    @rsubset(:customer_id == "CS018205000001" && :product_cd != "P071401019")
end
println(df)
```

    [1m12×4 DataFrame[0m
    [1m Row [0m│[1m sales_ymd [0m[1m customer_id    [0m[1m product_cd [0m[1m amount [0m
         │[90m Int64     [0m[90m String15       [0m[90m String15   [0m[90m Int64  [0m
    ─────┼───────────────────────────────────────────────
       1 │  20180911  CS018205000001  P071401012    2200
       2 │  20180414  CS018205000001  P060104007     600
       3 │  20170614  CS018205000001  P050206001     990
       4 │  20170614  CS018205000001  P060702015     108
       5 │  20190216  CS018205000001  P071005024     102
       6 │  20180414  CS018205000001  P071101002     278
       7 │  20190226  CS018205000001  P070902035     168
       8 │  20190924  CS018205000001  P060805001     495
       9 │  20190226  CS018205000001  P071401020    2200
      10 │  20180911  CS018205000001  P071401005    1100
      11 │  20190216  CS018205000001  P040101002     218
      12 │  20190924  CS018205000001  P091503001     280

---
> J-009: 以下の処理において、出力結果を変えずにORをANDに書き換えよ。
> 
> `df_store.query('not(prefecture_cd == "13" | floor_area > 900)')`

```julia
df = @rsubset(df_store, !(:prefecture_cd == "13" || :floor_area > 900))
println(df)
```

    [1m3×10 DataFrame[0m
    [1m Row [0m│[1m store_cd [0m[1m store_name [0m[1m prefecture_cd [0m[1m prefecture [0m[1m address                           [0m[1m address_kana                      [0m[1m tel_no       [0m[1m longitude [0m[1m latitude [0m[1m floor_area [0m
         │[90m String7  [0m[90m String31   [0m[90m String        [0m[90m String15   [0m[90m String                            [0m[90m String                            [0m[90m String15     [0m[90m Float64   [0m[90m Float64  [0m[90m Float64    [0m
    ─────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       1 │ S14046    北山田店    14             神奈川県    神奈川県横浜市都筑区北山田一丁目   カナガワケンヨコハマシツヅキクキ…  045-123-4049    139.592   35.5619       831.0
       2 │ S14011    日吉本町店  14             神奈川県    神奈川県横浜市港北区日吉本町四丁…  カナガワケンヨコハマシコウホクク…  045-123-4033    139.632   35.5466       890.0
       3 │ S12013    習志野店    12             千葉県      千葉県習志野市芝園一丁目           チバケンナラシノシシバゾノイッチ…  047-123-4002    140.022   35.6612       808.0

```julia
df = @rsubset(df_store, :prefecture_cd != "13" && :floor_area <= 900)
println(df)
```

    [1m3×10 DataFrame[0m
    [1m Row [0m│[1m store_cd [0m[1m store_name [0m[1m prefecture_cd [0m[1m prefecture [0m[1m address                           [0m[1m address_kana                      [0m[1m tel_no       [0m[1m longitude [0m[1m latitude [0m[1m floor_area [0m
         │[90m String7  [0m[90m String31   [0m[90m String        [0m[90m String15   [0m[90m String                            [0m[90m String                            [0m[90m String15     [0m[90m Float64   [0m[90m Float64  [0m[90m Float64    [0m
    ─────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       1 │ S14046    北山田店    14             神奈川県    神奈川県横浜市都筑区北山田一丁目   カナガワケンヨコハマシツヅキクキ…  045-123-4049    139.592   35.5619       831.0
       2 │ S14011    日吉本町店  14             神奈川県    神奈川県横浜市港北区日吉本町四丁…  カナガワケンヨコハマシコウホクク…  045-123-4033    139.632   35.5466       890.0
       3 │ S12013    習志野店    12             千葉県      千葉県習志野市芝園一丁目           チバケンナラシノシシバゾノイッチ…  047-123-4002    140.022   35.6612       808.0

## 4.　部分文字列の有無による選択（正規表現）

---
> J-010: 店舗データ（df_store）から、店舗コード（store_cd）が"S14"で始まるものだけ全項目抽出し、10件表示せよ。

```julia
df = @rsubset(df_store, occursin(r"^S14", :store_cd))
head(df)
```

    size = (23, 10)
    [1m10×10 DataFrame[0m
    [1m Row [0m│[1m store_cd [0m[1m store_name   [0m[1m prefecture_cd [0m[1m prefecture [0m[1m address                           [0m[1m address_kana                      [0m[1m tel_no       [0m[1m longitude [0m[1m latitude [0m[1m floor_area [0m
         │[90m String7  [0m[90m String31     [0m[90m String        [0m[90m String15   [0m[90m String                            [0m[90m String                            [0m[90m String15     [0m[90m Float64   [0m[90m Float64  [0m[90m Float64    [0m
    ─────┼────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       1 │ S14010    菊名店        14             神奈川県    神奈川県横浜市港北区菊名一丁目     カナガワケンヨコハマシコウホクク…  045-123-4032    139.633   35.5005      1732.0
       2 │ S14033    阿久和店      14             神奈川県    神奈川県横浜市瀬谷区阿久和西一丁…  カナガワケンヨコハマシセヤクアク…  045-123-4043    139.496   35.4592      1495.0
       3 │ S14036    相模原中央店  14             神奈川県    神奈川県相模原市中央二丁目         カナガワケンサガミハラシチュウオ…  042-123-4045    139.372   35.5733      1679.0
       4 │ S14040    長津田店      14             神奈川県    神奈川県横浜市緑区長津田みなみ台…  カナガワケンヨコハマシミドリクナ…  045-123-4046    139.499   35.524       1548.0
       5 │ S14050    阿久和西店    14             神奈川県    神奈川県横浜市瀬谷区阿久和西一丁…  カナガワケンヨコハマシセヤクアク…  045-123-4053    139.496   35.4592      1830.0
       6 │ S14028    二ツ橋店      14             神奈川県    神奈川県横浜市瀬谷区二ツ橋町       カナガワケンヨコハマシセヤクフタ…  045-123-4042    139.496   35.463       1574.0
       7 │ S14012    本牧和田店    14             神奈川県    神奈川県横浜市中区本牧和田         カナガワケンヨコハマシナカクホン…  045-123-4034    139.658   35.4216      1341.0
       8 │ S14046    北山田店      14             神奈川県    神奈川県横浜市都筑区北山田一丁目   カナガワケンヨコハマシツヅキクキ…  045-123-4049    139.592   35.5619       831.0
       9 │ S14022    逗子店        14             神奈川県    神奈川県逗子市逗子一丁目           カナガワケンズシシズシイッチョウ…  046-123-4036    139.579   35.2964      1838.0
      10 │ S14011    日吉本町店    14             神奈川県    神奈川県横浜市港北区日吉本町四丁…  カナガワケンヨコハマシコウホクク…  045-123-4033    139.632   35.5466       890.0

---
> J-011: 顧客データ（df_customer）から顧客ID（customer_id）の末尾が1のものだけ全項目抽出し、10件表示せよ。

```julia
df = @rsubset(df_customer, occursin(r"1$", :customer_id))
head(df)
```

    size = (2369, 11)
    [1m10×11 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m customer_name [0m[1m gender_cd [0m[1m gender  [0m[1m birth_day  [0m[1m age   [0m[1m postal_cd [0m[1m address                           [0m[1m application_store_cd [0m[1m application_date [0m[1m status_cd    [0m
         │[90m String15       [0m[90m String31      [0m[90m String    [0m[90m String7 [0m[90m Date       [0m[90m Int64 [0m[90m String15  [0m[90m String                            [0m[90m String7              [0m[90m String           [0m[90m String15     [0m
    ─────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       1 │ CS037613000071  六角 雅彦      9          不明     1952-04-01     66  136-0076   東京都江東区南砂**********         S13037                20150414          0-00000000-0
       2 │ CS028811000001  堀井 かおり    1          女性     1933-03-27     86  245-0016   神奈川県横浜市泉区和泉町********…  S14028                20160115          0-00000000-0
       3 │ CS040412000191  川井 郁恵      1          女性     1977-01-05     42  226-0021   神奈川県横浜市緑区北八朔町******…  S14040                20151101          1-20091025-4
       4 │ CS028314000011  小菅 あおい    1          女性     1983-11-26     35  246-0038   神奈川県横浜市瀬谷区宮沢********…  S14028                20151123          1-20080426-5
       5 │ CS039212000051  藤島 恵梨香    1          女性     1997-02-03     22  166-0001   東京都杉並区阿佐谷北**********     S13039                20171121          1-20100215-4
       6 │ CS015412000111  松居 奈月      1          女性     1972-10-04     46  136-0071   東京都江東区亀戸**********         S13015                20150629          0-00000000-0
       7 │ CS004702000041  野島 洋        0          男性     1943-08-24     75  176-0022   東京都練馬区向山**********         S13004                20170218          0-00000000-0
       8 │ CS041515000001  栗田 千夏      1          女性     1967-01-02     52  206-0001   東京都多摩市和田**********         S13041                20160422          E-20100803-F
       9 │ CS029313000221  北条 ひかり    1          女性     1987-06-19     31  279-0011   千葉県浦安市美浜**********         S12029                20180810          0-00000000-0
      10 │ CS034312000071  望月 奈央      1          女性     1980-09-20     38  213-0026   神奈川県川崎市高津区久末********…  S14034                20160106          0-00000000-0

---
> J-012: 店舗データ（df_store）から、住所 (address) に"横浜市"が含まれるものだけ全項目表示せよ。

```julia
df = @rsubset(df_store, occursin(r"横浜市", :address))
println(df)
```

    [1m11×10 DataFrame[0m
    [1m Row [0m│[1m store_cd [0m[1m store_name [0m[1m prefecture_cd [0m[1m prefecture [0m[1m address                           [0m[1m address_kana                      [0m[1m tel_no       [0m[1m longitude [0m[1m latitude [0m[1m floor_area [0m
         │[90m String7  [0m[90m String31   [0m[90m String        [0m[90m String15   [0m[90m String                            [0m[90m String                            [0m[90m String15     [0m[90m Float64   [0m[90m Float64  [0m[90m Float64    [0m
    ─────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       1 │ S14010    菊名店      14             神奈川県    神奈川県横浜市港北区菊名一丁目     カナガワケンヨコハマシコウホクク…  045-123-4032    139.633   35.5005      1732.0
       2 │ S14033    阿久和店    14             神奈川県    神奈川県横浜市瀬谷区阿久和西一丁…  カナガワケンヨコハマシセヤクアク…  045-123-4043    139.496   35.4592      1495.0
       3 │ S14040    長津田店    14             神奈川県    神奈川県横浜市緑区長津田みなみ台…  カナガワケンヨコハマシミドリクナ…  045-123-4046    139.499   35.524       1548.0
       4 │ S14050    阿久和西店  14             神奈川県    神奈川県横浜市瀬谷区阿久和西一丁…  カナガワケンヨコハマシセヤクアク…  045-123-4053    139.496   35.4592      1830.0
       5 │ S14028    二ツ橋店    14             神奈川県    神奈川県横浜市瀬谷区二ツ橋町       カナガワケンヨコハマシセヤクフタ…  045-123-4042    139.496   35.463       1574.0
       6 │ S14012    本牧和田店  14             神奈川県    神奈川県横浜市中区本牧和田         カナガワケンヨコハマシナカクホン…  045-123-4034    139.658   35.4216      1341.0
       7 │ S14046    北山田店    14             神奈川県    神奈川県横浜市都筑区北山田一丁目   カナガワケンヨコハマシツヅキクキ…  045-123-4049    139.592   35.5619       831.0
       8 │ S14011    日吉本町店  14             神奈川県    神奈川県横浜市港北区日吉本町四丁…  カナガワケンヨコハマシコウホクク…  045-123-4033    139.632   35.5466       890.0
       9 │ S14048    中川中央店  14             神奈川県    神奈川県横浜市都筑区中川中央二丁…  カナガワケンヨコハマシツヅキクナ…  045-123-4051    139.576   35.5491      1657.0
      10 │ S14042    新山下店    14             神奈川県    神奈川県横浜市中区新山下二丁目     カナガワケンヨコハマシナカクシン…  045-123-4047    139.659   35.4389      1044.0
      11 │ S14006    葛が谷店    14             神奈川県    神奈川県横浜市都筑区葛が谷         カナガワケンヨコハマシツヅキクク…  045-123-4031    139.563   35.5357      1886.0

---
> J-013: 顧客データ（df_customer）から、ステータスコード（status_cd）の先頭がアルファベットのA〜Fで始まるデータを全項目抽出し、10件表示せよ。

```julia
df = @rsubset(df_customer, occursin(r"^[A-F]", :status_cd))
head(df)
```

    size = (3401, 11)
    [1m10×11 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m customer_name [0m[1m gender_cd [0m[1m gender  [0m[1m birth_day  [0m[1m age   [0m[1m postal_cd [0m[1m address                           [0m[1m application_store_cd [0m[1m application_date [0m[1m status_cd    [0m
         │[90m String15       [0m[90m String31      [0m[90m String    [0m[90m String7 [0m[90m Date       [0m[90m Int64 [0m[90m String15  [0m[90m String                            [0m[90m String7              [0m[90m String           [0m[90m String15     [0m
    ─────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       1 │ CS031415000172  宇多田 貴美子  1          女性     1976-10-04     42  151-0053   東京都渋谷区代々木**********       S13031                20150529          D-20100325-C
       2 │ CS015414000103  奥野 陽子      1          女性     1977-08-09     41  136-0073   東京都江東区北砂**********         S13015                20150722          B-20100609-B
       3 │ CS011215000048  芦田 沙耶      1          女性     1992-02-01     27  223-0062   神奈川県横浜市港北区日吉本町****…  S14011                20150228          C-20100421-9
       4 │ CS029415000023  梅田 里穂      1          女性     1976-01-17     43  279-0043   千葉県浦安市富士見**********       S12029                20150610          D-20100918-E
       5 │ CS035415000029  寺沢 真希      9          不明     1977-09-27     41  158-0096   東京都世田谷区玉川台**********     S13035                20141220          F-20101029-F
       6 │ CS031415000106  宇野 由美子    1          女性     1970-02-26     49  151-0053   東京都渋谷区代々木**********       S13031                20150201          F-20100511-E
       7 │ CS029215000025  石倉 美帆      1          女性     1993-09-28     25  279-0022   千葉県浦安市今川**********         S12029                20150708          B-20100820-C
       8 │ CS033605000005  猪股 雄太      0          男性     1955-12-05     63  246-0031   神奈川県横浜市瀬谷区瀬谷********…  S14033                20150425          F-20100917-E
       9 │ CS033415000229  板垣 菜々美    1          女性     1977-11-07     41  246-0021   神奈川県横浜市瀬谷区二ツ橋町****…  S14033                20150712          F-20100326-E
      10 │ CS008415000145  黒谷 麻緒      1          女性     1977-06-27     41  157-0067   東京都世田谷区喜多見**********     S13008                20150829          F-20100622-F

---
> J-014: 顧客データ（df_customer）から、ステータスコード（status_cd）の末尾が数字の1〜9で終わるデータを全項目抽出し、10件表示せよ。

```julia
df = @rsubset(df_customer, occursin(r"[1-9]$", :status_cd))
head(df)
```

    size = (4970, 11)
    [1m10×11 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m customer_name [0m[1m gender_cd [0m[1m gender  [0m[1m birth_day  [0m[1m age   [0m[1m postal_cd [0m[1m address                           [0m[1m application_store_cd [0m[1m application_date [0m[1m status_cd    [0m
         │[90m String15       [0m[90m String31      [0m[90m String    [0m[90m String7 [0m[90m Date       [0m[90m Int64 [0m[90m String15  [0m[90m String                            [0m[90m String7              [0m[90m String           [0m[90m String15     [0m
    ─────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       1 │ CS001215000145  田崎 美紀      1          女性     1995-03-29     24  144-0055   東京都大田区仲六郷**********       S13001                20170605          6-20090929-2
       2 │ CS033513000180  安斎 遥        1          女性     1962-07-11     56  241-0823   神奈川県横浜市旭区善部町********…  S14033                20150728          6-20080506-5
       3 │ CS011215000048  芦田 沙耶      1          女性     1992-02-01     27  223-0062   神奈川県横浜市港北区日吉本町****…  S14011                20150228          C-20100421-9
       4 │ CS040412000191  川井 郁恵      1          女性     1977-01-05     42  226-0021   神奈川県横浜市緑区北八朔町******…  S14040                20151101          1-20091025-4
       5 │ CS009315000023  皆川 文世      1          女性     1980-04-15     38  154-0012   東京都世田谷区駒沢**********       S13009                20150319          5-20080322-1
       6 │ CS015315000033  福士 璃奈子    1          女性     1983-03-17     36  135-0043   東京都江東区塩浜**********         S13015                20141024          4-20080219-3
       7 │ CS023513000066  神戸 そら      1          女性     1961-12-17     57  210-0005   神奈川県川崎市川崎区東田町******…  S14023                20150915          5-20100524-9
       8 │ CS035513000134  市川 美帆      1          女性     1960-03-27     59  156-0053   東京都世田谷区桜**********         S13035                20150227          8-20100711-9
       9 │ CS001515000263  高松 夏空      1          女性     1962-11-09     56  144-0051   東京都大田区西蒲田**********       S13001                20160812          1-20100804-1
      10 │ CS040314000027  鶴田 きみまろ  9          不明     1986-03-26     33  226-0027   神奈川県横浜市緑区長津田********…  S14040                20150122          2-20080426-4

---
> J-015: 顧客データ（df_customer）から、ステータスコード（status_cd）の先頭がアルファベットのA〜Fで始まり、末尾が数字の1〜9で終わるデータを全項目抽出し、10件表示せよ。

```julia
df = @rsubset(df_customer, occursin(r"^[A-F].*[1-9]$", :status_cd))
head(df)
```

    size = (620, 11)
    [1m10×11 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m customer_name [0m[1m gender_cd [0m[1m gender  [0m[1m birth_day  [0m[1m age   [0m[1m postal_cd [0m[1m address                           [0m[1m application_store_cd [0m[1m application_date [0m[1m status_cd    [0m
         │[90m String15       [0m[90m String31      [0m[90m String    [0m[90m String7 [0m[90m Date       [0m[90m Int64 [0m[90m String15  [0m[90m String                            [0m[90m String7              [0m[90m String           [0m[90m String15     [0m
    ─────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       1 │ CS011215000048  芦田 沙耶      1          女性     1992-02-01     27  223-0062   神奈川県横浜市港北区日吉本町****…  S14011                20150228          C-20100421-9
       2 │ CS022513000105  島村 貴美子    1          女性     1962-03-12     57  249-0002   神奈川県逗子市山の根**********     S14022                20150320          A-20091115-7
       3 │ CS001515000096  水野 陽子      9          不明     1960-11-29     58  144-0053   東京都大田区蒲田本町**********     S13001                20150614          A-20100724-7
       4 │ CS013615000053  西脇 季衣      1          女性     1953-10-18     65  261-0026   千葉県千葉市美浜区幕張西********…  S12013                20150128          B-20100329-6
       5 │ CS020412000161  小宮 薫        1          女性     1974-05-21     44  174-0042   東京都板橋区東坂下**********       S13020                20150822          B-20081021-3
       6 │ CS001215000097  竹中 あさみ    1          女性     1990-07-25     28  146-0095   東京都大田区多摩川**********       S13001                20170315          A-20100211-2
       7 │ CS035212000007  内村 恵梨香    1          女性     1990-12-04     28  152-0023   東京都目黒区八雲**********         S13035                20151013          B-20101018-6
       8 │ CS002515000386  野田 コウ      1          女性     1963-05-30     55  185-0013   東京都国分寺市西恋ケ窪**********   S13002                20160410          C-20100127-8
       9 │ CS001615000372  稲垣 寿々花    1          女性     1956-10-29     62  144-0035   東京都大田区南蒲田**********       S13001                20170403          A-20100104-1
      10 │ CS032512000121  松井 知世      1          女性     1962-09-04     56  210-0011   神奈川県川崎市川崎区富士見******…  S13032                20150727          A-20100103-5

---
> J-016: 店舗データ（df_store）から、電話番号（tel_no）が3桁-3桁-4桁のデータを全項目表示せよ。

```julia
df = @rsubset(df_customer, occursin(r"[0-9]{3}-[0-9]{3}-[0-9]{4}", :status_cd))
head(df)
```

    size = (0, 11)
    [1m0×11 DataFrame[0m
    [1m Row [0m│[1m customer_id [0m[1m customer_name [0m[1m gender_cd [0m[1m gender  [0m[1m birth_day [0m[1m age   [0m[1m postal_cd [0m[1m address [0m[1m application_store_cd [0m[1m application_date [0m[1m status_cd [0m
         │[90m String15    [0m[90m String31      [0m[90m String    [0m[90m String7 [0m[90m Date      [0m[90m Int64 [0m[90m String15  [0m[90m String  [0m[90m String7              [0m[90m String           [0m[90m String15  [0m
    ─────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

```julia
df = df_store[occursin.(r"[0-9]{3}-[0-9]{3}-[0-9]{4}", df_store.tel_no), :]
println(df)
```

    [1m34×10 DataFrame[0m
    [1m Row [0m│[1m store_cd [0m[1m store_name   [0m[1m prefecture_cd [0m[1m prefecture [0m[1m address                           [0m[1m address_kana                      [0m[1m tel_no       [0m[1m longitude [0m[1m latitude [0m[1m floor_area [0m
         │[90m String7  [0m[90m String31     [0m[90m String        [0m[90m String15   [0m[90m String                            [0m[90m String                            [0m[90m String15     [0m[90m Float64   [0m[90m Float64  [0m[90m Float64    [0m
    ─────┼────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       1 │ S12014    千草台店      12             千葉県      千葉県千葉市稲毛区千草台一丁目     チバケンチバシイナゲクチグサダイ…  043-123-4003    140.118   35.6356      1698.0
       2 │ S13002    国分寺店      13             東京都      東京都国分寺市本多二丁目           トウキョウトコクブンジシホンダニ…  042-123-4008    139.48    35.7057      1735.0
       3 │ S14010    菊名店        14             神奈川県    神奈川県横浜市港北区菊名一丁目     カナガワケンヨコハマシコウホクク…  045-123-4032    139.633   35.5005      1732.0
       4 │ S14033    阿久和店      14             神奈川県    神奈川県横浜市瀬谷区阿久和西一丁…  カナガワケンヨコハマシセヤクアク…  045-123-4043    139.496   35.4592      1495.0
       5 │ S14036    相模原中央店  14             神奈川県    神奈川県相模原市中央二丁目         カナガワケンサガミハラシチュウオ…  042-123-4045    139.372   35.5733      1679.0
       6 │ S14040    長津田店      14             神奈川県    神奈川県横浜市緑区長津田みなみ台…  カナガワケンヨコハマシミドリクナ…  045-123-4046    139.499   35.524       1548.0
       7 │ S14050    阿久和西店    14             神奈川県    神奈川県横浜市瀬谷区阿久和西一丁…  カナガワケンヨコハマシセヤクアク…  045-123-4053    139.496   35.4592      1830.0
       8 │ S13052    森野店        13             東京都      東京都町田市森野三丁目             トウキョウトマチダシモリノサンチ…  042-123-4030    139.438   35.5529      1087.0
       9 │ S14028    二ツ橋店      14             神奈川県    神奈川県横浜市瀬谷区二ツ橋町       カナガワケンヨコハマシセヤクフタ…  045-123-4042    139.496   35.463       1574.0
      10 │ S14012    本牧和田店    14             神奈川県    神奈川県横浜市中区本牧和田         カナガワケンヨコハマシナカクホン…  045-123-4034    139.658   35.4216      1341.0
      11 │ S14046    北山田店      14             神奈川県    神奈川県横浜市都筑区北山田一丁目   カナガワケンヨコハマシツヅキクキ…  045-123-4049    139.592   35.5619       831.0
      12 │ S14022    逗子店        14             神奈川県    神奈川県逗子市逗子一丁目           カナガワケンズシシズシイッチョウ…  046-123-4036    139.579   35.2964      1838.0
      13 │ S14011    日吉本町店    14             神奈川県    神奈川県横浜市港北区日吉本町四丁…  カナガワケンヨコハマシコウホクク…  045-123-4033    139.632   35.5466       890.0
      14 │ S13016    小金井店      13             東京都      東京都小金井市本町一丁目           トウキョウトコガネイシホンチョウ…  042-123-4015    139.509   35.7002      1399.0
      15 │ S14034    川崎野川店    14             神奈川県    神奈川県川崎市宮前区野川           カナガワケンカワサキシミヤマエク…  044-123-4044    139.6     35.5769      1318.0
      16 │ S14048    中川中央店    14             神奈川県    神奈川県横浜市都筑区中川中央二丁…  カナガワケンヨコハマシツヅキクナ…  045-123-4051    139.576   35.5491      1657.0
      17 │ S12007    佐倉店        12             千葉県      千葉県佐倉市上志津                 チバケンサクラシカミシヅ           043-123-4001    140.145   35.7187      1895.0
      18 │ S14026    辻堂西海岸店  14             神奈川県    神奈川県藤沢市辻堂西海岸二丁目     カナガワケンフジサワシツジドウニ…  046-123-4040    139.447   35.3246      1732.0
      19 │ S13041    八王子店      13             東京都      東京都八王子市大塚                 トウキョウトハチオウジシオオツカ   042-123-4026    139.423   35.6379       810.0
      20 │ S14049    川崎大師店    14             神奈川県    神奈川県川崎市川崎区中瀬三丁目     カナガワケンカワサキシカワサキク…  044-123-4052    139.733   35.5376       962.0
      21 │ S14023    川崎店        14             神奈川県    神奈川県川崎市川崎区本町二丁目     カナガワケンカワサキシカワサキク…  044-123-4037    139.703   35.536       1804.0
      22 │ S13018    清瀬店        13             東京都      東京都清瀬市松山一丁目             トウキョウトキヨセシマツヤマイッ…  042-123-4017    139.518   35.7689      1220.0
      23 │ S14027    南藤沢店      14             神奈川県    神奈川県藤沢市南藤沢               カナガワケンフジサワシミナミフジ…  046-123-4041    139.49    35.3376      1521.0
      24 │ S14021    伊勢原店      14             神奈川県    神奈川県伊勢原市伊勢原四丁目       カナガワケンイセハラシイセハラヨ…  046-123-4035    139.313   35.4017       962.0
      25 │ S14047    相模原店      14             神奈川県    神奈川県相模原市千代田六丁目       カナガワケンサガミハラシチヨダロ…  042-123-4050    139.375   35.5596      1047.0
      26 │ S12013    習志野店      12             千葉県      千葉県習志野市芝園一丁目           チバケンナラシノシシバゾノイッチ…  047-123-4002    140.022   35.6612       808.0
      27 │ S14042    新山下店      14             神奈川県    神奈川県横浜市中区新山下二丁目     カナガワケンヨコハマシナカクシン…  045-123-4047    139.659   35.4389      1044.0
      28 │ S12030    八幡店        12             千葉県      千葉県市川市八幡三丁目             チバケンイチカワシヤワタサンチョ…  047-123-4005    139.924   35.7232      1162.0
      29 │ S14025    大和店        14             神奈川県    神奈川県大和市下和田               カナガワケンヤマトシシモワダ       046-123-4039    139.468   35.4341      1011.0
      30 │ S14045    厚木店        14             神奈川県    神奈川県厚木市中町二丁目           カナガワケンアツギシナカチョウニ…  046-123-4048    139.365   35.4418       980.0
      31 │ S12029    東野店        12             千葉県      千葉県浦安市東野一丁目             チバケンウラヤスシヒガシノイッチ…  047-123-4004    139.897   35.6509      1101.0
      32 │ S12053    高洲店        12             千葉県      千葉県浦安市高洲五丁目             チバケンウラヤスシタカスゴチョウ…  047-123-4006    139.918   35.6375      1555.0
      33 │ S14024    三田店        14             神奈川県    神奈川県川崎市多摩区三田四丁目     カナガワケンカワサキシタマクミタ…  044-123-4038    139.542   35.6077       972.0
      34 │ S14006    葛が谷店      14             神奈川県    神奈川県横浜市都筑区葛が谷         カナガワケンヨコハマシツヅキクク…  045-123-4031    139.563   35.5357      1886.0

# データフレームの並べ替え(sort)

## 5.　列でソート

---
> J-017: 顧客データ（df_customer）を生年月日（birth_day）で高齢順にソートし、先頭から全項目を10件表示せよ。

```julia
df = @orderby(df_customer, :birth_day)
head(df)
```

    size = (21971, 11)
    [1m10×11 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m customer_name [0m[1m gender_cd [0m[1m gender  [0m[1m birth_day  [0m[1m age   [0m[1m postal_cd [0m[1m address                           [0m[1m application_store_cd [0m[1m application_date [0m[1m status_cd    [0m
         │[90m String15       [0m[90m String31      [0m[90m String    [0m[90m String7 [0m[90m Date       [0m[90m Int64 [0m[90m String15  [0m[90m String                            [0m[90m String7              [0m[90m String           [0m[90m String15     [0m
    ─────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       1 │ CS003813000014  村山 菜々美    1          女性     1928-11-26     90  182-0007   東京都調布市菊野台**********       S13003                20160214          0-00000000-0
       2 │ CS026813000004  吉村 朝陽      1          女性     1928-12-14     90  251-0043   神奈川県藤沢市辻堂元町**********   S14026                20150723          0-00000000-0
       3 │ CS018811000003  熊沢 美里      1          女性     1929-01-07     90  204-0004   東京都清瀬市野塩**********         S13018                20150403          0-00000000-0
       4 │ CS027803000004  内村 拓郎      0          男性     1929-01-12     90  251-0031   神奈川県藤沢市鵠沼藤が谷********…  S14027                20151227          0-00000000-0
       5 │ CS013801000003  天野 拓郎      0          男性     1929-01-15     90  274-0824   千葉県船橋市前原東**********       S12013                20160120          0-00000000-0
       6 │ CS001814000022  鶴田 里穂      1          女性     1929-01-28     90  144-0045   東京都大田区南六郷**********       S13001                20161012          A-20090415-7
       7 │ CS016815000002  山元 美紀      1          女性     1929-02-22     90  184-0005   東京都小金井市桜町**********       S13016                20150629          C-20090923-C
       8 │ CS009815000003  中田 里穂      1          女性     1929-04-08     89  154-0014   東京都世田谷区新町**********       S13009                20150421          D-20091021-E
       9 │ CS012813000013  宇野 南朋      1          女性     1929-04-09     89  231-0806   神奈川県横浜市中区本牧町********…  S14012                20150712          0-00000000-0
      10 │ CS005813000015  金谷 恵梨香    1          女性     1929-04-09     89  165-0032   東京都中野区鷺宮**********         S13005                20150506          0-00000000-0

---
> J-018: 顧客データ（df_customer）を生年月日（birth_day）で若い順にソートし、先頭から全項目を10件表示せよ。

**文字列データの降順ソートはちょっとまだるっこしい。これは，`@orderby()` の作りが悪いからであろう。**

```julia
df = @orderby(df_customer, invperm(sortperm(:birth_day, rev=true)))
head(df)
```

    size = (21971, 11)
    [1m10×11 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m customer_name [0m[1m gender_cd [0m[1m gender  [0m[1m birth_day  [0m[1m age   [0m[1m postal_cd [0m[1m address                           [0m[1m application_store_cd [0m[1m application_date [0m[1m status_cd    [0m
         │[90m String15       [0m[90m String31      [0m[90m String    [0m[90m String7 [0m[90m Date       [0m[90m Int64 [0m[90m String15  [0m[90m String                            [0m[90m String7              [0m[90m String           [0m[90m String15     [0m
    ─────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       1 │ CS035114000004  大村 美里      1          女性     2007-11-25     11  156-0053   東京都世田谷区桜**********         S13035                20150619          6-20091205-6
       2 │ CS022103000002  福山 はじめ    9          不明     2007-10-02     11  249-0006   神奈川県逗子市逗子**********       S14022                20160909          0-00000000-0
       3 │ CS002113000009  柴田 真悠子    1          女性     2007-09-17     11  184-0014   東京都小金井市貫井南町**********   S13002                20160304          0-00000000-0
       4 │ CS004115000014  松井 京子      1          女性     2007-08-09     11  165-0031   東京都中野区上鷺宮**********       S13004                20161120          1-20081231-1
       5 │ CS002114000010  山内 遥        1          女性     2007-06-03     11  184-0015   東京都小金井市貫井北町**********   S13002                20160920          6-20100510-1
       6 │ CS025115000002  小柳 夏希      1          女性     2007-04-18     11  245-0018   神奈川県横浜市泉区上飯田町******…  S14025                20160116          D-20100913-D
       7 │ CS002113000025  広末 まなみ    1          女性     2007-03-30     12  184-0015   東京都小金井市貫井北町**********   S13002                20171030          0-00000000-0
       8 │ CS033112000003  長野 美紀      1          女性     2007-03-22     12  245-0051   神奈川県横浜市戸塚区名瀬町******…  S14033                20150606          0-00000000-0
       9 │ CS007115000006  福岡 瞬        1          女性     2007-03-10     12  285-0845   千葉県佐倉市西志津**********       S12007                20151118          F-20101016-F
      10 │ CS014113000008  矢口 莉緒      1          女性     2007-03-05     12  260-0041   千葉県千葉市中央区東千葉********…  S12014                20150622          3-20091108-6

**Base.sort のほうがわかりやすい。**

```julia
df = sort(df_customer, :birth_day, rev=true)
head(df)
```

    size = (21971, 11)
    [1m10×11 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m customer_name [0m[1m gender_cd [0m[1m gender  [0m[1m birth_day  [0m[1m age   [0m[1m postal_cd [0m[1m address                           [0m[1m application_store_cd [0m[1m application_date [0m[1m status_cd    [0m
         │[90m String15       [0m[90m String31      [0m[90m String    [0m[90m String7 [0m[90m Date       [0m[90m Int64 [0m[90m String15  [0m[90m String                            [0m[90m String7              [0m[90m String           [0m[90m String15     [0m
    ─────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       1 │ CS035114000004  大村 美里      1          女性     2007-11-25     11  156-0053   東京都世田谷区桜**********         S13035                20150619          6-20091205-6
       2 │ CS022103000002  福山 はじめ    9          不明     2007-10-02     11  249-0006   神奈川県逗子市逗子**********       S14022                20160909          0-00000000-0
       3 │ CS002113000009  柴田 真悠子    1          女性     2007-09-17     11  184-0014   東京都小金井市貫井南町**********   S13002                20160304          0-00000000-0
       4 │ CS004115000014  松井 京子      1          女性     2007-08-09     11  165-0031   東京都中野区上鷺宮**********       S13004                20161120          1-20081231-1
       5 │ CS002114000010  山内 遥        1          女性     2007-06-03     11  184-0015   東京都小金井市貫井北町**********   S13002                20160920          6-20100510-1
       6 │ CS025115000002  小柳 夏希      1          女性     2007-04-18     11  245-0018   神奈川県横浜市泉区上飯田町******…  S14025                20160116          D-20100913-D
       7 │ CS002113000025  広末 まなみ    1          女性     2007-03-30     12  184-0015   東京都小金井市貫井北町**********   S13002                20171030          0-00000000-0
       8 │ CS033112000003  長野 美紀      1          女性     2007-03-22     12  245-0051   神奈川県横浜市戸塚区名瀬町******…  S14033                20150606          0-00000000-0
       9 │ CS007115000006  福岡 瞬        1          女性     2007-03-10     12  285-0845   千葉県佐倉市西志津**********       S12007                20151118          F-20101016-F
      10 │ CS014113000008  矢口 莉緒      1          女性     2007-03-05     12  260-0041   千葉県千葉市中央区東千葉********…  S12014                20150622          3-20091108-6

## 6.　順位付けをしてソート

**順位付けは StatsBase パッケージに `tiedrank()`, `competerank()`, `ordinalrank()` がある。**

---
> J-019: レシート明細データ（df_receipt）に対し、1件あたりの売上金額（amount）が高い順にランクを付与し、先頭から10件表示せよ。項目は顧客ID（customer_id）、売上金額（amount）、付与したランクを表示させること。なお、売上金額（amount）が等しい場合は同一順位を付与するものとする。

```julia
using StatsBase
df = @chain df_receipt begin
    @select(:customer_id, :amount, :rank = competerank(:amount, rev=true))
    @orderby(:rank)
end
head(df)
```

    size = (104681, 3)
    [1m10×3 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m amount [0m[1m rank  [0m
         │[90m String15       [0m[90m Int64  [0m[90m Int64 [0m
    ─────┼───────────────────────────────
       1 │ CS011415000006   10925      1
       2 │ ZZ000000000000    6800      2
       3 │ CS028605000002    5780      3
       4 │ CS015515000034    5480      4
       5 │ ZZ000000000000    5480      4
       6 │ ZZ000000000000    5480      4
       7 │ ZZ000000000000    5440      7
       8 │ CS021515000089    5440      7
       9 │ CS015515000083    5280      9
      10 │ CS017414000114    5280      9

---
> J-020: レシート明細データ（df_receipt）に対し、1件あたりの売上金額（amount）が高い順にランクを付与し、先頭から10件表示せよ。項目は顧客ID（customer_id）、売上金額（amount）、付与したランクを表示させること。なお、売上金額（amount）が等しい場合でも別順位を付与すること。

```julia
using StatsBase
df = @chain df_receipt begin
    @select(:customer_id, :amount, :rank = ordinalrank(:amount, rev=true))
    @orderby(:rank)
end
head(df)
```

    size = (104681, 3)
    [1m10×3 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m amount [0m[1m rank  [0m
         │[90m String15       [0m[90m Int64  [0m[90m Int64 [0m
    ─────┼───────────────────────────────
       1 │ CS011415000006   10925      1
       2 │ ZZ000000000000    6800      2
       3 │ CS028605000002    5780      3
       4 │ CS015515000034    5480      4
       5 │ ZZ000000000000    5480      5
       6 │ ZZ000000000000    5480      6
       7 │ ZZ000000000000    5440      7
       8 │ CS021515000089    5440      8
       9 │ CS015515000083    5280      9
      10 │ CS017414000114    5280     10

# 件数（行数）の確認

---
> J-021: レシート明細データ（df_receipt）に対し、件数をカウントせよ。

```julia
nrow(df_receipt)
```

    104681

---
> J-022: レシート明細データ（df_receipt）の顧客ID（customer_id）に対し、ユニーク件数をカウントせよ。

**ユニークなデータ，データフレームを作るには `unique()` を使う。**

```julia
length(unique(df_receipt.customer_id))
```

    8307

```julia
@chain df_receipt begin
    unique(:customer_id)
    nrow
end
```

    8307

```julia
@chain df_receipt begin
    @distinct(:customer_id)
    nrow
end

```

    8307

# グループ化して処理

**groupby() によってグループ化して，それぞれのグループ化データフレームを `combine()` によって処理して，その結果をデータフレームとして生成する。**

## 7.　合計

---
> J-023: レシート明細データ（df_receipt）に対し、店舗コード（store_cd）ごとに売上金額（amount）と売上数量（quantity）を合計せよ。

```julia
df = @chain df_receipt begin
    groupby(:store_cd)
    @combine(:amount = sum(:amount), :quantity = sum(:quantity))
    @orderby(:store_cd)
end
println(df)
```

    [1m52×3 DataFrame[0m
    [1m Row [0m│[1m store_cd [0m[1m amount [0m[1m quantity [0m
         │[90m String7  [0m[90m Int64  [0m[90m Int64    [0m
    ─────┼────────────────────────────
       1 │ S12007    638761      2099
       2 │ S12013    787513      2425
       3 │ S12014    725167      2358
       4 │ S12029    794741      2555
       5 │ S12030    684402      2403
       6 │ S13001    811936      2347
       7 │ S13002    727821      2340
       8 │ S13003    764294      2197
       9 │ S13004    779373      2390
      10 │ S13005    629876      2004
      11 │ S13008    809288      2491
      12 │ S13009    808870      2486
      13 │ S13015    780873      2248
      14 │ S13016    793773      2432
      15 │ S13017    748221      2376
      16 │ S13018    790535      2562
      17 │ S13019    827833      2541
      18 │ S13020    796383      2383
      19 │ S13031    705968      2336
      20 │ S13032    790501      2491
      21 │ S13035    715869      2219
      22 │ S13037    693087      2344
      23 │ S13038    708884      2337
      24 │ S13039    611888      1981
      25 │ S13041    728266      2233
      26 │ S13043    587895      1881
      27 │ S13044    520764      1729
      28 │ S13051    107452       354
      29 │ S13052    100314       250
      30 │ S14006    712839      2284
      31 │ S14010    790361      2290
      32 │ S14011    805724      2434
      33 │ S14012    720600      2412
      34 │ S14021    699511      2231
      35 │ S14022    651328      2047
      36 │ S14023    727630      2258
      37 │ S14024    736323      2417
      38 │ S14025    755581      2394
      39 │ S14026    824537      2503
      40 │ S14027    714550      2303
      41 │ S14028    786145      2458
      42 │ S14033    725318      2282
      43 │ S14034    653681      2024
      44 │ S14036    203694       635
      45 │ S14040    701858      2233
      46 │ S14042    534689      1935
      47 │ S14045    458484      1398
      48 │ S14046    412646      1354
      49 │ S14047    338329      1041
      50 │ S14048    234276       769
      51 │ S14049    230808       788
      52 │ S14050    167090       580

## 8.　最大値

---
> J-024: レシート明細データ（df_receipt）に対し、顧客ID（customer_id）ごとに最も新しい売上年月日（sales_ymd）を求め、10件表示せよ。

```julia
df = @chain df_receipt begin
    groupby(:customer_id)
    @combine(:sales_ymd_maximum = maximum(:sales_ymd))
    @orderby(:customer_id)  # ソートする必要がある場合
end
head(df)
```

    size = (8307, 2)
    [1m10×2 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m sales_ymd_maximum [0m
         │[90m String15       [0m[90m Int64             [0m
    ─────┼───────────────────────────────────
       1 │ CS001113000004           20190308
       2 │ CS001114000005           20190731
       3 │ CS001115000010           20190405
       4 │ CS001205000004           20190625
       5 │ CS001205000006           20190224
       6 │ CS001211000025           20190322
       7 │ CS001212000027           20170127
       8 │ CS001212000031           20180906
       9 │ CS001212000046           20170811
      10 │ CS001212000070           20191018

## 9.　最小値

---
> J-025: レシート明細データ（df_receipt）に対し、顧客ID（customer_id）ごとに最も古い売上年月日（sales_ymd）を求め、10件表示せよ。

```julia
df = @chain df_receipt begin
    groupby(:customer_id)
    @combine(:sales_ymd_minimum = minimum(:sales_ymd))
    @orderby(:customer_id)  # ソートする必要がある場合
end
head(df)
```

    size = (8307, 2)
    [1m10×2 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m sales_ymd_minimum [0m
         │[90m String15       [0m[90m Int64             [0m
    ─────┼───────────────────────────────────
       1 │ CS001113000004           20190308
       2 │ CS001114000005           20180503
       3 │ CS001115000010           20171228
       4 │ CS001205000004           20170914
       5 │ CS001205000006           20180207
       6 │ CS001211000025           20190322
       7 │ CS001212000027           20170127
       8 │ CS001212000031           20180906
       9 │ CS001212000046           20170811
      10 │ CS001212000070           20191018

## 10.　比較演算，論理演算

---
> J-026: レシート明細データ（df_receipt）に対し、顧客ID（customer_id）ごとに最も新しい売上年月日（sales_ymd）と古い売上年月日を求め、両者が異なるデータを10件表示せよ。

```julia
df = @chain df_receipt begin
    groupby(:customer_id)
    @combine(:max_ymd = maximum(:sales_ymd), :min_ymd = minimum(:sales_ymd))
    @rsubset(:min_ymd != :max_ymd)
    @orderby(:customer_id)  # ソートする必要がある場合
end
head(df)
```

    size = (5546, 3)
    [1m10×3 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m max_ymd  [0m[1m min_ymd  [0m
         │[90m String15       [0m[90m Int64    [0m[90m Int64    [0m
    ─────┼────────────────────────────────────
       1 │ CS001114000005  20190731  20180503
       2 │ CS001115000010  20190405  20171228
       3 │ CS001205000004  20190625  20170914
       4 │ CS001205000006  20190224  20180207
       5 │ CS001214000009  20190902  20170306
       6 │ CS001214000017  20191006  20180828
       7 │ CS001214000048  20190929  20171109
       8 │ CS001214000052  20190617  20180208
       9 │ CS001215000005  20181021  20170206
      10 │ CS001215000040  20171022  20170214

## 11.　平均値

---
> J-027: レシート明細データ（df_receipt）に対し、店舗コード（store_cd）ごとに売上金額（amount）の平均を計算し、降順でTOP5を表示せよ。

```julia
using StatsBase

df = @chain df_receipt begin
    groupby(:store_cd)
    @combine(:mean_amount = mean(:amount))
    @orderby(-:mean_amount)
end
head(df, 5)
```

    size = (52, 2)
    [1m5×2 DataFrame[0m
    [1m Row [0m│[1m store_cd [0m[1m mean_amount [0m
         │[90m String7  [0m[90m Float64     [0m
    ─────┼───────────────────────
       1 │ S13052        402.867
       2 │ S13015        351.112
       3 │ S13003        350.916
       4 │ S14010        348.791
       5 │ S13001        348.47

## 12.　中央値

---
> J-028: レシート明細データ（df_receipt）に対し、店舗コード（store_cd）ごとに売上金額（amount）の中央値を計算し、降順でTOP5を表示せよ。

```julia
df = @chain df_receipt begin
    groupby(:store_cd)
    @combine(:median_amount = median(:amount))
    @orderby(-:median_amount)
end
head(df, 5)
```

    size = (52, 2)
    [1m5×2 DataFrame[0m
    [1m Row [0m│[1m store_cd [0m[1m median_amount [0m
         │[90m String7  [0m[90m Float64       [0m
    ─────┼─────────────────────────
       1 │ S13052            190.0
       2 │ S14010            188.0
       3 │ S14050            185.0
       4 │ S13003            180.0
       5 │ S14040            180.0

## 13.　最頻値

---
> J-029: レシート明細データ（df_receipt）に対し、店舗コード（store_cd）ごとに商品コード（product_cd）の最頻値を求め、10件表示させよ。

```julia
using FreqTables
df = @chain df_receipt begin
    groupby(:store_cd)
    @combine(:product_cd_mode = mode(:product_cd), :cnt = maximum(freqtable(:product_cd)))
    @orderby(:store_cd)
end
head(df)
```

    size = (52, 3)
    [1m10×3 DataFrame[0m
    [1m Row [0m│[1m store_cd [0m[1m product_cd_mode [0m[1m cnt   [0m
         │[90m String7  [0m[90m String15        [0m[90m Int64 [0m
    ─────┼──────────────────────────────────
       1 │ S12007    P060303001          72
       2 │ S12013    P060303001         107
       3 │ S12014    P060303001          65
       4 │ S12029    P060303001          92
       5 │ S12030    P060303001         115
       6 │ S13001    P060303001          67
       7 │ S13002    P060303001          78
       8 │ S13003    P071401001          65
       9 │ S13004    P060303001          88
      10 │ S13005    P040503001          36

## 14.　分散

---
> J-030: レシート明細データ（df_receipt）に対し、店舗コード（store_cd）ごとに売上金額（amount）の分散を計算し、降順で5件表示せよ。

```julia
df = @chain df_receipt begin
    groupby(:store_cd)
    @combine(:var_amount = var(:amount, corrected=false))
    @orderby(-:var_amount)
end
head(df, 5)
```

    size = (52, 2)
    [1m5×2 DataFrame[0m
    [1m Row [0m│[1m store_cd [0m[1m var_amount     [0m
         │[90m String7  [0m[90m Float64        [0m
    ─────┼──────────────────────────
       1 │ S13052         4.40089e5
       2 │ S14011         3.06315e5
       3 │ S14034         2.9692e5
       4 │ S13001    295432.0
       5 │ S13015         2.95294e5

## 15.　標準偏差

---
> J-031: レシート明細データ（df_receipt）に対し、店舗コード（store_cd）ごとに売上金額（amount）の標準偏差を計算し、降順で5件表示せよ。

```julia
df = @chain df_receipt begin
    groupby(:store_cd)
    @combine(:std_amount = std(:amount, corrected=false))
    @orderby(-:std_amount)
end
head(df, 5)
```

    size = (52, 2)
    [1m5×2 DataFrame[0m
    [1m Row [0m│[1m store_cd [0m[1m std_amount [0m
         │[90m String7  [0m[90m Float64    [0m
    ─────┼──────────────────────
       1 │ S13052       663.392
       2 │ S14011       553.457
       3 │ S14034       544.904
       4 │ S13001       543.537
       5 │ S13015       543.41

## 16.　パーセンタイル値（第1四分位数，中央値，第3四分位数）

---
> J-032: レシート明細データ（df_receipt）の売上金額（amount）について、25％刻みでパーセンタイル値を求めよ。

```julia
df = @combine(df_receipt, :quantile = quantile(:amount, 0.25:0.25:1))
println(df)
```

    [1m4×1 DataFrame[0m
    [1m Row [0m│[1m quantile [0m
         │[90m Float64  [0m
    ─────┼──────────
       1 │    102.0
       2 │    170.0
       3 │    288.0
       4 │  10925.0

## 17.　比較演算，論理演算

---
> J-033: レシート明細データ（df_receipt）に対し、店舗コード（store_cd）ごとに売上金額（amount）の平均を計算し、330以上のものを抽出せよ。

```julia
df = @chain df_receipt begin
    groupby(:store_cd)
    @combine(:mean_amount = mean(:amount))
    @rsubset(:mean_amount >= 330)
    @orderby(:store_cd)
end
println(df)
```

    [1m13×2 DataFrame[0m
    [1m Row [0m│[1m store_cd [0m[1m mean_amount [0m
         │[90m String7  [0m[90m Float64     [0m
    ─────┼───────────────────────
       1 │ S12013        330.194
       2 │ S13001        348.47
       3 │ S13003        350.916
       4 │ S13004        330.944
       5 │ S13015        351.112
       6 │ S13019        330.209
       7 │ S13020        337.88
       8 │ S13052        402.867
       9 │ S14010        348.791
      10 │ S14011        335.718
      11 │ S14026        332.341
      12 │ S14045        330.082
      13 │ S14047        330.077

## 18.　グループ化されたデータフレームごとの集計結果との比較

---
> J-034: レシート明細データ（df_receipt）に対し、顧客ID（customer_id）ごとに売上金額（amount）を合計して全顧客の平均を求めよ。ただし、顧客IDが"Z"から始まるものは非会員を表すため、除外して計算すること。

```julia
df = @chain df_receipt begin
    @rsubset(!occursin(r"^Z", :customer_id))
    groupby(:customer_id)
    @combine(:sum_amount = sum(:amount))
end
mean(df.sum_amount)
```

    2547.742234529256

---
> J-035: レシート明細データ（df_receipt）に対し、顧客ID（customer_id）ごとに売上金額（amount）を合計して全顧客の平均を求め、平均以上に買い物をしている顧客を抽出し、10件表示せよ。ただし、顧客IDが"Z"から始まるものは非会員を表すため、除外して計算すること。

```julia
df = @chain df_receipt begin
    @rsubset(!occursin(r"^Z", :customer_id))
    groupby(:customer_id)
    @combine(:sum_amount = sum(:amount))
end
# mean(df.sum_amount)
df2 = @chain df begin
    @rsubset(:sum_amount > mean(df.sum_amount))
    @orderby(:customer_id)
end
head(df2)
```

    size = (2996, 2)
    [1m10×2 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m sum_amount [0m
         │[90m String15       [0m[90m Int64      [0m
    ─────┼────────────────────────────
       1 │ CS001115000010        3044
       2 │ CS001205000006        3337
       3 │ CS001214000009        4685
       4 │ CS001214000017        4132
       5 │ CS001214000052        5639
       6 │ CS001215000040        3496
       7 │ CS001304000006        3726
       8 │ CS001305000005        3485
       9 │ CS001305000011        4370
      10 │ CS001315000180        3300

# データフレームの結合(join)

## 19.　`innnerjoin()`

---
> J-036: レシート明細データ（df_receipt）と店舗データ（df_store）を内部結合し、レシート明細データの全項目と店舗データの店舗名（store_name）を10件表示せよ。

```julia
df = innerjoin(df_receipt, df_store[!, [:store_cd, :store_name]], on=:store_cd)
head(df)
```

    size = (104681, 10)
    [1m10×10 DataFrame[0m
    [1m Row [0m│[1m sales_ymd [0m[1m sales_epoch [0m[1m store_cd [0m[1m receipt_no [0m[1m receipt_sub_no [0m[1m customer_id    [0m[1m product_cd [0m[1m quantity [0m[1m amount [0m[1m store_name [0m
         │[90m Int64     [0m[90m Int64       [0m[90m String7  [0m[90m Int64      [0m[90m Int64          [0m[90m String15       [0m[90m String15   [0m[90m Int64    [0m[90m Int64  [0m[90m String31   [0m
    ─────┼────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       1 │  20181103   1541203200  S14006           112               1  CS006214000001  P070305012         1     158  葛が谷店
       2 │  20181118   1542499200  S13008          1132               2  CS008415000097  P070701017         1      81  成城店
       3 │  20170712   1499817600  S14028          1102               1  CS028414000014  P060101005         1     170  二ツ橋店
       4 │  20190205   1549324800  S14042          1132               1  ZZ000000000000  P050301001         1      25  新山下店
       5 │  20180821   1534809600  S14025          1102               2  CS025415000050  P060102007         1      90  大和店
       6 │  20190605   1559692800  S13003          1112               1  CS003515000195  P050102002         1     138  狛江店
       7 │  20181205   1543968000  S14024          1102               2  CS024514000042  P080101005         1      30  三田店
       8 │  20190922   1569110400  S14040          1102               1  CS040415000178  P070501004         1     128  長津田店
       9 │  20170504   1493856000  S13020          1112               2  ZZ000000000000  P071302010         1     770  十条仲原店
      10 │  20191010   1570665600  S14027          1102               1  CS027514000015  P071101003         1     680  南藤沢店

---
> J-037: 商品データ（df_product）とカテゴリデータ（df_category）を内部結合し、商品データの全項目とカテゴリデータのカテゴリ小区分名（category_small_name）を10件表示せよ。

```julia
df = innerjoin(df_product, df_category[!, [:category_small_name, :category_small_cd]], on=:category_small_cd)
head(df)
```

    size = (10030, 7)
    [1m10×7 DataFrame[0m
    [1m Row [0m│[1m product_cd [0m[1m category_major_cd [0m[1m category_medium_cd [0m[1m category_small_cd [0m[1m unit_price [0m[1m unit_cost [0m[1m category_small_name [0m
         │[90m String15   [0m[90m String            [0m[90m String             [0m[90m String            [0m[90m Int64?     [0m[90m Int64?    [0m[90m String              [0m
    ─────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       1 │ P040101001  04                 0401                040101                    198        149  弁当類
       2 │ P040101002  04                 0401                040101                    218        164  弁当類
       3 │ P040101003  04                 0401                040101                    230        173  弁当類
       4 │ P040101004  04                 0401                040101                    248        186  弁当類
       5 │ P040101005  04                 0401                040101                    268        201  弁当類
       6 │ P040101006  04                 0401                040101                    298        224  弁当類
       7 │ P040101007  04                 0401                040101                    338        254  弁当類
       8 │ P040101008  04                 0401                040101                    420        315  弁当類
       9 │ P040101009  04                 0401                040101                    498        374  弁当類
      10 │ P040101010  04                 0401                040101                    580        435  弁当類

## 20.　`leftjoin()`

---
> J-038: 顧客データ（df_customer）とレシート明細データ（df_receipt）から、顧客ごとの売上金額合計を求め、10件表示せよ。ただし、売上実績がない顧客については売上金額を0として表示させること。また、顧客は性別コード（gender_cd）が女性（1）であるものを対象とし、非会員（顧客IDが"Z"から始まるもの）は除外すること。

```julia
df = leftjoin(df_customer[!, [:customer_id, :gender_cd]], df_receipt[!, [:customer_id, :amount]], on=:customer_id)
df2 = @chain df begin
    @rsubset(!occursin(r"^Z", :customer_id) && :gender_cd == "1")
    groupby(:customer_id)
    @combine(:sum_amount = sum(:amount))
    #@rtransform(:sum_amount = ismissing(:sum_amount) ? 0 : :sum_amount)  # coalesce() は missing の置換に使える
    @rtransform(:sum_amount = coalesce(:sum_amount, 0))
    @orderby(:customer_id)
end
head(df2)
```

    size = (17918, 2)
    [1m10×2 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m sum_amount [0m
         │[90m String15       [0m[90m Int64      [0m
    ─────┼────────────────────────────
       1 │ CS001112000009           0
       2 │ CS001112000019           0
       3 │ CS001112000021           0
       4 │ CS001112000023           0
       5 │ CS001112000024           0
       6 │ CS001112000029           0
       7 │ CS001112000030           0
       8 │ CS001113000004        1298
       9 │ CS001113000010           0
      10 │ CS001114000005         626

## 21.　`outerjoin()`

---
> J-039: レシート明細データ（df_receipt）から、売上日数の多い顧客の上位20件を抽出したデータと、売上金額合計の多い顧客の上位20件を抽出したデータをそれぞれ作成し、さらにその2つを完全外部結合せよ。ただし、非会員（顧客IDが"Z"から始まるもの）は除外すること。

```julia
df = @chain df_receipt begin
    @rsubset(!occursin(r"^Z", :customer_id))
    groupby(:customer_id)
end

df2 = @chain df begin
    @combine(:come_days = length(unique(:sales_ymd)))
    @orderby(-:come_days)
end

df3 = @chain df begin
    @combine(:sum_amount = sum(:amount))
    @orderby(-:sum_amount)
end

df4 = outerjoin(df2[1:20, :], df3[1:20, :], on=:customer_id)
println(df4)
```

    [1m34×3 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m come_days [0m[1m sum_amount [0m
         │[90m String15       [0m[90m Int64?    [0m[90m Int64?     [0m
    ─────┼───────────────────────────────────────
       1 │ CS017415000097         20       23086
       2 │ CS015415000185         22       20153
       3 │ CS031414000051         19       19202
       4 │ CS028415000007         21       19127
       5 │ CS010214000010         22       18585
       6 │ CS016415000141         20       18372
       7 │ CS040214000008         23 [90m    missing [0m
       8 │ CS010214000002         21 [90m    missing [0m
       9 │ CS039414000052         19 [90m    missing [0m
      10 │ CS021514000045         19 [90m    missing [0m
      11 │ CS022515000226         19 [90m    missing [0m
      12 │ CS014214000023         19 [90m    missing [0m
      13 │ CS021515000172         19 [90m    missing [0m
      14 │ CS014415000077         18 [90m    missing [0m
      15 │ CS030214000008         18 [90m    missing [0m
      16 │ CS021515000056         18 [90m    missing [0m
      17 │ CS031414000073         18 [90m    missing [0m
      18 │ CS032415000209         18 [90m    missing [0m
      19 │ CS007515000107         18 [90m    missing [0m
      20 │ CS021515000211         18 [90m    missing [0m
      21 │ CS001605000009 [90m   missing [0m      18925
      22 │ CS006515000023 [90m   missing [0m      18372
      23 │ CS011414000106 [90m   missing [0m      18338
      24 │ CS038415000104 [90m   missing [0m      17847
      25 │ CS035414000024 [90m   missing [0m      17615
      26 │ CS021515000089 [90m   missing [0m      17580
      27 │ CS032414000072 [90m   missing [0m      16563
      28 │ CS016415000101 [90m   missing [0m      16348
      29 │ CS011415000006 [90m   missing [0m      16094
      30 │ CS034415000047 [90m   missing [0m      16083
      31 │ CS007514000094 [90m   missing [0m      15735
      32 │ CS009414000059 [90m   missing [0m      15492
      33 │ CS030415000034 [90m   missing [0m      15468
      34 │ CS015515000034 [90m   missing [0m      15300

---
> J-040: 全ての店舗と全ての商品を組み合わせたデータを作成したい。店舗データ（df_store）と商品データ（df_product）を直積し、件数を計算せよ。

件数を計算するだけなら，それぞれの件数の積である。

```julia
nrow(df_store) * nrow(df_product)
```

    531590

```julia
# 別解
df_store2 = copy(df_store);
insertcols!(df_store2, 1, :key => 0);

df_product2 = copy(df_product);
insertcols!(df_product2, 1, :key => 0);

outerjoin(df_store2, df_product2, on=:key) |>
    nrow
```

    531590

# 差分

---
> J-041: レシート明細データ（df_receipt）の売上金額（amount）を日付（sales_ymd）ごとに集計し、前回売上があった日からの売上金額増減を計算せよ。そして結果を10件表示せよ。

```julia
df = @chain df_receipt begin
    groupby(:sales_ymd)
    @combine(:amount_sum = sum(:amount))
    @orderby(:sales_ymd)
    @transform(:lag_sales_ymd = vcat(missing, :sales_ymd[1:end-1]))
    @transform(:lag_amount_sum = vcat(missing, :amount_sum[1:end-1]))
    @transform(:diff_amount = :amount_sum - :lag_amount_sum)
end
head(df)
```

    size = (1034, 5)
    [1m10×5 DataFrame[0m
    [1m Row [0m│[1m sales_ymd [0m[1m amount_sum [0m[1m lag_sales_ymd [0m[1m lag_amount_sum [0m[1m diff_amount [0m
         │[90m Int64     [0m[90m Int64      [0m[90m Int64?        [0m[90m Int64?         [0m[90m Int64?      [0m
    ─────┼───────────────────────────────────────────────────────────────────
       1 │  20170101       33723 [90m       missing [0m[90m        missing [0m[90m     missing [0m
       2 │  20170102       24165       20170101           33723        -9558
       3 │  20170103       27503       20170102           24165         3338
       4 │  20170104       36165       20170103           27503         8662
       5 │  20170105       37830       20170104           36165         1665
       6 │  20170106       32387       20170105           37830        -5443
       7 │  20170107       23415       20170106           32387        -8972
       8 │  20170108       24737       20170107           23415         1322
       9 │  20170109       26718       20170108           24737         1981
      10 │  20170110       20143       20170109           26718        -6575

---
> J-042: レシート明細データ（df_receipt）の売上金額（amount）を日付（sales_ymd）ごとに集計し、各日付のデータに対し、前回、前々回、3回前に売上があった日のデータを結合せよ。そして結果を10件表示せよ。

```julia
df = @chain df_receipt begin
    groupby(:sales_ymd)
    @combine(:amount = sum(:amount))
    @orderby(:sales_ymd)
end

df2 = DataFrame()  # sales_ymd = 0, amount = 0, lag_ymd = 0, lag_amount = 0)
for i = 2:nrow(df)
    i >= 4 && append!(df2, DataFrame(sales_ymd = df[i, 1], amount = df[i, 2], lag_ymd = df[i-3, 1], lag_amount = df[i-3, 2]))
    i >= 3 && append!(df2, DataFrame(sales_ymd = df[i, 1], amount = df[i, 2], lag_ymd = df[i-2, 1], lag_amount = df[i-2, 2]))
              append!(df2, DataFrame(sales_ymd = df[i, 1], amount = df[i, 2], lag_ymd = df[i-1, 1], lag_amount = df[i-1, 2]))
end

head(df2)
```

    size = (3096, 4)
    [1m10×4 DataFrame[0m
    [1m Row [0m│[1m sales_ymd [0m[1m amount [0m[1m lag_ymd  [0m[1m lag_amount [0m
         │[90m Int64     [0m[90m Int64  [0m[90m Int64    [0m[90m Int64      [0m
    ─────┼─────────────────────────────────────────
       1 │  20170102   24165  20170101       33723
       2 │  20170103   27503  20170101       33723
       3 │  20170103   27503  20170102       24165
       4 │  20170104   36165  20170101       33723
       5 │  20170104   36165  20170102       24165
       6 │  20170104   36165  20170103       27503
       7 │  20170105   37830  20170102       24165
       8 │  20170105   37830  20170103       27503
       9 │  20170105   37830  20170104       36165
      10 │  20170106   32387  20170103       27503

```julia
# 別解
df = @chain df_receipt begin
    groupby(:sales_ymd)
    @combine(:amount = sum(:amount))
    @orderby(:sales_ymd)
end

df3 = DataFrame()  # sales_ymd = 0, amount = 0,
        # lag_ymd_1 = 0, lag_amount_1 = 0,
        # lag_ymd_2 = 0, lag_amount_2 = 0,
        # lag_ymd_3 = 0, lag_amount_3 = 0)
for i = 4:nrow(df)
    append!(df3,
        DataFrame(sales_ymd = df[i, 1], amount = df[i, 2],
            lag_ymd_1 = df[i-1, 1], lag_amount_1 = df[i-1, 2],
            lag_ymd_2 = df[i-2, 1], lag_amount_2 = df[i-2, 2],
            lag_ymd_3 = df[i-3, 1], lag_amount_3 = df[i-3, 2]
            ))
end
head(df3)
```

    size = (1031, 8)
    [1m10×8 DataFrame[0m
    [1m Row [0m│[1m sales_ymd [0m[1m amount [0m[1m lag_ymd_1 [0m[1m lag_amount_1 [0m[1m lag_ymd_2 [0m[1m lag_amount_2 [0m[1m lag_ymd_3 [0m[1m lag_amount_3 [0m
         │[90m Int64     [0m[90m Int64  [0m[90m Int64     [0m[90m Int64        [0m[90m Int64     [0m[90m Int64        [0m[90m Int64     [0m[90m Int64        [0m
    ─────┼──────────────────────────────────────────────────────────────────────────────────────────────
       1 │  20170104   36165   20170103         27503   20170102         24165   20170101         33723
       2 │  20170105   37830   20170104         36165   20170103         27503   20170102         24165
       3 │  20170106   32387   20170105         37830   20170104         36165   20170103         27503
       4 │  20170107   23415   20170106         32387   20170105         37830   20170104         36165
       5 │  20170108   24737   20170107         23415   20170106         32387   20170105         37830
       6 │  20170109   26718   20170108         24737   20170107         23415   20170106         32387
       7 │  20170110   20143   20170109         26718   20170108         24737   20170107         23415
       8 │  20170111   24287   20170110         20143   20170109         26718   20170108         24737
       9 │  20170112   23526   20170111         24287   20170110         20143   20170109         26718
      10 │  20170113   28004   20170112         23526   20170111         24287   20170110         20143

# クロス集計

---
> J-043： レシート明細データ（df_receipt）と顧客データ（df_customer）を結合し、性別コード（gender_cd）と年代（ageから計算）ごとに売上金額（amount）を合計した売上サマリデータを作成せよ。性別コードは0が男性、1が女性、9が不明を表すものとする。
>
> ただし、項目構成は年代、女性の売上金額、男性の売上金額、性別不明の売上金額の4項目とすること（縦に年代、横に性別のクロス集計）。また、年代は10歳ごとの階級とすること。

```julia
df = @chain innerjoin(df_receipt[!, [:customer_id, :amount]],
        df_customer[!, [:customer_id, :gender_cd, :age]],
        on=:customer_id, makeunique=true) begin
    @rtransform(:age = div(:age, 10) * 10)
    groupby([:age, :gender_cd])
    @combine(:amount_sum = sum(:amount))
end
head(df)
```

    size = (25, 3)
    [1m10×3 DataFrame[0m
    [1m Row [0m│[1m age   [0m[1m gender_cd [0m[1m amount_sum [0m
         │[90m Int64 [0m[90m String    [0m[90m Int64      [0m
    ─────┼──────────────────────────────
       1 │    10  1              149836
       2 │    10  9                4317
       3 │    10  0                1591
       4 │    20  1             1363724
       5 │    20  9               44328
       6 │    20  0               72940
       7 │    30  1              693047
       8 │    30  9               50441
       9 │    30  0              177322
      10 │    40  1             9320791

```julia
using NamedArrays
using FreqTables
table = freqtable(df.age, df.gender_cd, weights=df.amount_sum)
df043 = hcat(10:10:90, DataFrame(Matrix(table), :auto), makeunique=true)
println(rename!(df043, [:era, :male, :female, :unknown]))
```

    [1m9×4 DataFrame[0m
    [1m Row [0m│[1m era   [0m[1m male   [0m[1m female  [0m[1m unknown [0m
         │[90m Int64 [0m[90m Int64  [0m[90m Int64   [0m[90m Int64   [0m
    ─────┼─────────────────────────────────
       1 │    10    1591   149836     4317
       2 │    20   72940  1363724    44328
       3 │    30  177322   693047    50441
       4 │    40   19355  9320791   483512
       5 │    50   54320  6685192   342923
       6 │    60  272469   987741    71418
       7 │    70   13435    29764     2427
       8 │    80   46360   262923     5111
       9 │    90       0     6260        0

```julia
# 別解
df0 = @chain innerjoin(df_receipt[!, [:customer_id, :amount]],
        df_customer[!, [:customer_id, :gender_cd, :age]],
        on=:customer_id, makeunique=true) begin
    @rtransform(:age = div(:age, 10) * 10)
end
freqtable(df0.age, df0.gender_cd, weights=df0.amount)
```

    9×3 Named Matrix{Int64}
    Dim1 ╲ Dim2 │       0        1        9
    ────────────┼──────────────────────────
    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

# 縦長フォーマット

---
> J-044： 043で作成した売上サマリデータ（df_sales_summary）は性別の売上を横持ちさせたものであった。このデータから性別を縦持ちさせ、年代、性別コード、売上金額の3項目に変換せよ。ただし、性別コードは男性を"00"、女性を"01"、不明を"99"とする。

```julia
df2 = stack(df043, 2:4)
df2[df2[:, 2] .== "male", 2] .= "00"
df2[df2[:, 2] .== "female", 2] .= "01"
df2[df2[:, 2] .== "unknown", 2] .= "99";
```

```julia
println(df2)
```

    [1m27×3 DataFrame[0m
    [1m Row [0m│[1m era   [0m[1m variable [0m[1m value   [0m
         │[90m Int64 [0m[90m String   [0m[90m Int64   [0m
    ─────┼──────────────────────────
       1 │    10  00           1591
       2 │    20  00          72940
       3 │    30  00         177322
       4 │    40  00          19355
       5 │    50  00          54320
       6 │    60  00         272469
       7 │    70  00          13435
       8 │    80  00          46360
       9 │    90  00              0
      10 │    10  01         149836
      11 │    20  01        1363724
      12 │    30  01         693047
      13 │    40  01        9320791
      14 │    50  01        6685192
      15 │    60  01         987741
      16 │    70  01          29764
      17 │    80  01         262923
      18 │    90  01           6260
      19 │    10  99           4317
      20 │    20  99          44328
      21 │    30  99          50441
      22 │    40  99         483512
      23 │    50  99         342923
      24 │    60  99          71418
      25 │    70  99           2427
      26 │    80  99           5111
      27 │    90  99              0

# 日付データ

## 22.　日付型を文字列に変換

---
> J-045: 顧客データ（df_customer）の生年月日（birth_day）は日付型でデータを保有している。これをYYYYMMDD形式の文字列に変換し、顧客ID（customer_id）とともに10件表示せよ。

```julia
using Dates
df = @chain df_customer begin
    @rtransform(:birthday = Dates.format(:birth_day, "yyyymmdd"))
    @select(:customer_id, :birthday)
end
head(df)
```

    size = (21971, 2)
    [1m10×2 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m birthday [0m
         │[90m String15       [0m[90m String   [0m
    ─────┼──────────────────────────
       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

## 23.　文字列を日付型に変換

---
> J-046: 顧客データ（df_customer）の申し込み日（application_date）はYYYYMMDD形式の文字列型でデータを保有している。これを日付型に変換し、顧客ID（customer_id）とともに10件表示せよ。

```julia
df = @chain df_customer begin
    @transform(:yyyy = [parse(Int, s[1:4]) for s in :application_date])
    @transform(:mm = [parse(Int, s[5:6]) for s in :application_date])
    @transform(:dd = [parse(Int, s[7:8]) for s in :application_date])
    @rtransform(:application_date = Date(:yyyy, :mm, :dd))
    @select(:customer_id, :application_date)
end
head(df)
```

    size = (21971, 2)
    [1m10×2 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m application_date [0m
         │[90m String15       [0m[90m Date             [0m
    ─────┼──────────────────────────────────
       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

---
> J-047: レシート明細データ（df_receipt）の売上日（sales_ymd）はYYYYMMDD形式の数値型でデータを保有している。これを日付型に変換し、レシート番号（receipt_no）、レシートサブ番号（receipt_sub_no）とともに10件表示せよ。

```julia
df = @chain df_receipt begin
    @rtransform(:yyyy = :sales_ymd ÷ 10000)
    @rtransform(:mm = :sales_ymd % 10000 ÷ 100)
    @rtransform(:dd = :sales_ymd % 100)
    @rtransform(:sales_ymd = Date(:yyyy, :mm, :dd))
    @select(:receipt_no, :receipt_sub_no, :sales_ymd)
end
head(df)
```

    size = (104681, 3)
    [1m10×3 DataFrame[0m
    [1m Row [0m│[1m receipt_no [0m[1m receipt_sub_no [0m[1m sales_ymd  [0m
         │[90m Int64      [0m[90m Int64          [0m[90m Date       [0m
    ─────┼────────────────────────────────────────
       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

---
> J-048: レシート明細データ（df_receipt）の売上エポック秒（sales_epoch）は数値型のUNIX秒でデータを保有している。これを日付型に変換し、レシート番号(receipt_no)、レシートサブ番号（receipt_sub_no）とともに10件表示せよ。

```julia
df = @chain df_receipt begin
    @rtransform(:sales_ymd = Date(unix2datetime(:sales_epoch)))
    @select(:receipt_no, :receipt_sub_no, :sales_ymd)
end
head(df)
```

    size = (104681, 3)
    [1m10×3 DataFrame[0m
    [1m Row [0m│[1m receipt_no [0m[1m receipt_sub_no [0m[1m sales_ymd  [0m
         │[90m Int64      [0m[90m Int64          [0m[90m Date       [0m
    ─────┼────────────────────────────────────────
       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

## 24.　エポック秒を日付型に変換

---
> J-049: レシート明細データ（df_receipt）の売上エポック秒（sales_epoch）を日付型に変換し、「年」だけ取り出してレシート番号(receipt_no)、レシートサブ番号（receipt_sub_no）とともに10件表示せよ。

```julia
df = @chain df_receipt begin
    @rtransform(:salse_year = year(unix2datetime(:sales_epoch)))
    @select(:receipt_no, :receipt_sub_no, :salse_year)
end
head(df)
```

    size = (104681, 3)
    [1m10×3 DataFrame[0m
    [1m Row [0m│[1m receipt_no [0m[1m receipt_sub_no [0m[1m salse_year [0m
         │[90m Int64      [0m[90m Int64          [0m[90m Int64      [0m
    ─────┼────────────────────────────────────────
       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

---
> J-050: レシート明細データ（df_receipt）の売上エポック秒（sales_epoch）を日付型に変換し、「月」だけ取り出してレシート番号(receipt_no)、レシートサブ番号（receipt_sub_no）とともに10件表示せよ。なお、「月」は0埋め2桁で取り出すこと。

```julia
df = @chain df_receipt begin
    @rtransform(:salse_month = string(month(unix2datetime(:sales_epoch)), pad=2))
    @select(:receipt_no, :receipt_sub_no, :salse_month)
end
head(df)
```

    size = (104681, 3)
    [1m10×3 DataFrame[0m
    [1m Row [0m│[1m receipt_no [0m[1m receipt_sub_no [0m[1m salse_month [0m
         │[90m Int64      [0m[90m Int64          [0m[90m String      [0m
    ─────┼─────────────────────────────────────────
       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

---
> J-051: レシート明細データ（df_receipt）の売上エポック秒を日付型に変換し、「日」だけ取り出してレシート番号(receipt_no)、レシートサブ番号（receipt_sub_no）とともに10件表示せよ。なお、「日」は0埋め2桁で取り出すこと。

```julia
df = @chain df_receipt begin
    @rtransform(:salse_day = string(day(unix2datetime(:sales_epoch)), pad=2))
    @select(:receipt_no, :receipt_sub_no, :salse_day)
end
head(df)
```

    size = (104681, 3)
    [1m10×3 DataFrame[0m
    [1m Row [0m│[1m receipt_no [0m[1m receipt_sub_no [0m[1m salse_day [0m
         │[90m Int64      [0m[90m Int64          [0m[90m String    [0m
    ─────┼───────────────────────────────────────
       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

# 変数の作成と数式変形

---
> J-052: レシート明細データ（df_receipt）の売上金額（amount）を顧客ID（customer_id）ごとに合計の上、売上金額合計に対して2,000円以下を0、2,000円より大きい金額を1に二値化し、顧客ID、売上金額合計とともに10件表示せよ。ただし、顧客IDが"Z"から始まるのものは非会員を表すため、除外して計算すること。

```julia
df = @chain df_receipt begin
    @rsubset(!occursin(r"^Z", :customer_id))
    groupby(:customer_id)
    @combine(:sum_amount = sum(:amount))
    @rtransform(:sale_flag = (:sum_amount >= 2000) + 0)
    @orderby(:customer_id)
end
head(df)
```

    size = (8306, 3)
    [1m10×3 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m sum_amount [0m[1m sale_flag [0m
         │[90m String15       [0m[90m Int64      [0m[90m Int64     [0m
    ─────┼───────────────────────────────────────
       1 │ CS001113000004        1298          0
       2 │ CS001114000005         626          0
       3 │ CS001115000010        3044          1
       4 │ CS001205000004        1988          0
       5 │ CS001205000006        3337          1
       6 │ CS001211000025         456          0
       7 │ CS001212000027         448          0
       8 │ CS001212000031         296          0
       9 │ CS001212000046         228          0
      10 │ CS001212000070         456          0

---
> J-053: 顧客データ（df_customer）の郵便番号（postal_cd）に対し、東京（先頭3桁が100〜209のもの）を1、それ以外のものを0に二値化せよ。さらにレシート明細データ（df_receipt）と結合し、全期間において売上実績のある顧客数を、作成した二値ごとにカウントせよ。

```julia
df  = @rtransform(df_customer, :postal_flg = (100 <= parse(Int, SubString(:postal_cd, 1, 3)) <= 209) + 0)
df2 = @select(df_receipt, :customer_id)
df3 = @chain unique(innerjoin(df, df2, on=:customer_id)) begin
    unique()
    # @select(:customer_id, :postal_flg)
    groupby(:postal_flg)
    @combine(:n = length(:postal_flg))
end
head(df3)
```

    size = (2, 2)
    [1m2×2 DataFrame[0m
    [1m Row [0m│[1m postal_flg [0m[1m n     [0m
         │[90m Int64      [0m[90m Int64 [0m
    ─────┼───────────────────
       1 │          0   3906
       2 │          1   4400

---
> J-054: 顧客データ（df_customer）の住所（address）は、埼玉県、千葉県、東京都、神奈川県のいずれかとなっている。都道府県毎にコード値を作成し、顧客ID、住所とともに10件表示せよ。値は埼玉県を11、千葉県を12、東京都を13、神奈川県を14とすること。

```julia
df = @chain df_customer begin
    @select(:customer_id, :address)
    @transform(:prefecture_cd = [indexin(s[1], ['埼', '千', '東', '神'])[1]+10 for s in :address])
end
head(df)
```

    size = (21971, 3)
    [1m10×3 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m address                           [0m[1m prefecture_cd [0m
         │[90m String15       [0m[90m String                            [0m[90m Int64         [0m
    ─────┼──────────────────────────────────────────────────────────────────
       1 │ CS021313000114  神奈川県伊勢原市粟窪**********                14
       2 │ CS037613000071  東京都江東区南砂**********                    13
       3 │ CS031415000172  東京都渋谷区代々木**********                  13
       4 │ CS028811000001  神奈川県横浜市泉区和泉町********…             14
       5 │ CS001215000145  東京都大田区仲六郷**********                  13
       6 │ CS020401000016  東京都板橋区若木**********                    13
       7 │ CS015414000103  東京都江東区北砂**********                    13
       8 │ CS029403000008  千葉県浦安市海楽**********                    12
       9 │ CS015804000004  東京都江東区北砂**********                    13
      10 │ CS033513000180  神奈川県横浜市旭区善部町********…             14

---
> J-055: レシート明細（df_receipt）データの売上金額（amount）を顧客ID（customer_id）ごとに合計し、その合計金額の四分位点を求めよ。その上で、顧客ごとの売上金額合計に対して以下の基準でカテゴリ値を作成し、顧客ID、売上金額合計とともに10件表示せよ。カテゴリ値は順に1〜4とする。
>
> - 最小値以上第1四分位未満 ・・・ 1を付与
> - 第1四分位以上第2四分位未満 ・・・ 2を付与
> - 第2四分位以上第3四分位未満 ・・・ 3を付与
> - 第3四分位以上 ・・・ 4を付与

```julia
using StatsBase
function encode(df)
    result = fill(4, length(df))
    for (i, x) in enumerate(df)
        if x < Q[1]
            result[i] = 1
        elseif x < Q[2]
            result[i] = 2
        elseif x < Q[3]
            result[i] = 3
        end
    end
    return result
end

df = @chain df_receipt begin
    @select(:amount, :customer_id)
    groupby(:customer_id)
    @combine(:amount_sum = sum(:amount))
    @orderby(:customer_id)
end

Q = quantile(df.amount_sum, [0.25, 0.50, 0.75]);
println(Q)

df2 = @transform(df, :pct_group = encode(:amount_sum))

head(df2);
```

    [548.5, 1478.0, 3651.0]
    size = (8307, 3)
    [1m10×3 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m amount_sum [0m[1m pct_group [0m
         │[90m String15       [0m[90m Int64      [0m[90m Int64     [0m
    ─────┼───────────────────────────────────────
       1 │ CS001113000004        1298          2
       2 │ CS001114000005         626          2
       3 │ CS001115000010        3044          3
       4 │ CS001205000004        1988          3
       5 │ CS001205000006        3337          3
       6 │ CS001211000025         456          1
       7 │ CS001212000027         448          1
       8 │ CS001212000031         296          1
       9 │ CS001212000046         228          1
      10 │ CS001212000070         456          1

---
> J-056: 顧客データ（df_customer）の年齢（age）をもとに10歳刻みで年代を算出し、顧客ID（customer_id）、生年月日（birth_day）とともに10件表示せよ。ただし、60歳以上は全て60歳代とすること。年代を表すカテゴリ名は任意とする。

```julia
df = @chain df_customer begin
    @select(:customer_id, :birth_day, :age)
    @rtransform(:era = min((:age ÷ 10) * 10, 60))
end
head(df)
```

    size = (21971, 4)
    [1m10×4 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m birth_day  [0m[1m age   [0m[1m era   [0m
         │[90m String15       [0m[90m Date       [0m[90m Int64 [0m[90m Int64 [0m
    ─────┼──────────────────────────────────────────
       1 │ CS021313000114  1981-04-29     37     30
       2 │ CS037613000071  1952-04-01     66     60
       3 │ CS031415000172  1976-10-04     42     40
       4 │ CS028811000001  1933-03-27     86     60
       5 │ CS001215000145  1995-03-29     24     20
       6 │ CS020401000016  1974-09-15     44     40
       7 │ CS015414000103  1977-08-09     41     40
       8 │ CS029403000008  1973-08-17     45     40
       9 │ CS015804000004  1931-05-02     87     60
      10 │ CS033513000180  1962-07-11     56     50

---
> J-057: 056の抽出結果と性別コード（gender_cd）により、新たに性別×年代の組み合わせを表すカテゴリデータを作成し、10件表示せよ。組み合わせを表すカテゴリの値は任意とする。

```julia
df = @chain df_customer begin
    @select(:customer_id, :gender_cd, :birth_day, :age)
    @rtransform(:era = min((:age ÷ 10) * 10, 60))
    @rtransform(:gender_era = string(:gender_cd) * string(:era))
end
head(df)
```

    size = (21971, 6)
    [1m10×6 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m gender_cd [0m[1m birth_day  [0m[1m age   [0m[1m era   [0m[1m gender_era [0m
         │[90m String15       [0m[90m String    [0m[90m Date       [0m[90m Int64 [0m[90m Int64 [0m[90m String     [0m
    ─────┼─────────────────────────────────────────────────────────────────
       1 │ CS021313000114  1          1981-04-29     37     30  130
       2 │ CS037613000071  9          1952-04-01     66     60  960
       3 │ CS031415000172  1          1976-10-04     42     40  140
       4 │ CS028811000001  1          1933-03-27     86     60  160
       5 │ CS001215000145  1          1995-03-29     24     20  120
       6 │ CS020401000016  0          1974-09-15     44     40  040
       7 │ CS015414000103  1          1977-08-09     41     40  140
       8 │ CS029403000008  0          1973-08-17     45     40  040
       9 │ CS015804000004  0          1931-05-02     87     60  060
      10 │ CS033513000180  1          1962-07-11     56     50  150

## 25.　ダミー変数の作成

---
> J-058: 顧客データ（df_customer）の性別コード（gender_cd）をダミー変数化し、顧客ID（customer_id）とともに10件表示せよ。

```julia
# トリッキー（スマート）な方法
dummydatamatrix = Matrix(permutedims(["0", "1", "9"]) .== df_customer.gender_cd) .+ 0
df = hcat(df_customer[!, [:customer_id]], DataFrame(dummydatamatrix, :auto))
rename!(df, [:customer_id, :gender_cd0, :gender_cd1, :gender_cd9])
head(df)
```

    size = (21971, 4)
    [1m10×4 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m gender_cd0 [0m[1m gender_cd1 [0m[1m gender_cd9 [0m
         │[90m String15       [0m[90m Int64      [0m[90m Int64      [0m[90m Int64      [0m
    ─────┼────────────────────────────────────────────────────
       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

```julia
# 地道な方法
df = @chain df_customer begin
    @select(:customer_id, :gender_cd)
    @rtransform begin
        :gender_cd0 = (:gender_cd == "0") + 0
        :gender_cd1 = (:gender_cd == "1") + 0
        :gender_cd9 = (:gender_cd == "9") + 0
    end
    @select($(Not(:gender_cd)))
end
head(df)
```

    size = (21971, 4)
    [1m10×4 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m gender_cd0 [0m[1m gender_cd1 [0m[1m gender_cd9 [0m
         │[90m String15       [0m[90m Int64      [0m[90m Int64      [0m[90m Int64      [0m
    ─────┼────────────────────────────────────────────────────
       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

## 26.　標準化

---
> J-059: レシート明細データ（df_receipt）の売上金額（amount）を顧客ID（customer_id）ごとに合計し、売上金額合計を平均0、標準偏差1に標準化して顧客ID、売上金額合計とともに10件表示せよ。標準化に使用する標準偏差は、分散の平方根、もしくは不偏分散の平方根のどちらでも良いものとする。ただし、顧客IDが"Z"から始まるのものは非会員を表すため、除外して計算すること。

~~TIPS:
~- query()の引数engineで'python'か'numexpr'かを選択でき、デフォルトはインストールされていればnumexprが、無ければpythonが使われます。さらに、文字列メソッドはengine='python'でないとquery()内で使えません。~~

```julia
df = @chain df_receipt begin
    @rsubset(!occursin(r"^Z", :customer_id))
    groupby(:customer_id)
    @combine(:sum_amount = sum(:amount))
end

df2 = @chain df begin
    @rtransform(:standardized_amount = (:sum_amount - mean(df.sum_amount)) / std(df.sum_amount))
    @orderby(:customer_id)
end
head(df2)
```

    size = (8306, 3)
    [1m10×3 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m sum_amount [0m[1m standardized_amount [0m
         │[90m String15       [0m[90m Int64      [0m[90m Float64             [0m
    ─────┼─────────────────────────────────────────────────
       1 │ CS001113000004        1298            -0.45935
       2 │ CS001114000005         626            -0.706348
       3 │ CS001115000010        3044             0.182403
       4 │ CS001205000004        1988            -0.205737
       5 │ CS001205000006        3337             0.290096
       6 │ CS001211000025         456            -0.768832
       7 │ CS001212000027         448            -0.771773
       8 │ CS001212000031         296            -0.827641
       9 │ CS001212000046         228            -0.852635
      10 │ CS001212000070         456            -0.768832

---
> J-060: レシート明細データ（df_receipt）の売上金額（amount）を顧客ID（customer_id）ごとに合計し、売上金額合計を最小値0、最大値1に正規化して顧客ID、売上金額合計とともに10件表示せよ。ただし、顧客IDが"Z"から始まるのものは非会員を表すため、除外して計算すること。

```julia
df = @chain df_receipt begin
    @rsubset(!occursin(r"^Z", :customer_id))
    groupby(:customer_id)
    @combine(:sum_amount = sum(:amount))
end

df2 = @chain df begin
    @rtransform(:standardized_amount = (:sum_amount - minimum(df.sum_amount)) / (maximum(df.sum_amount) - minimum(df.sum_amount)))
    @orderby(:customer_id)
end
head(df2)
```

    size = (8306, 3)
    [1m10×3 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m sum_amount [0m[1m standardized_amount [0m
         │[90m String15       [0m[90m Int64      [0m[90m Float64             [0m
    ─────┼─────────────────────────────────────────────────
       1 │ CS001113000004        1298           0.0533542
       2 │ CS001114000005         626           0.0241571
       3 │ CS001115000010        3044           0.129214
       4 │ CS001205000004        1988           0.0833333
       5 │ CS001205000006        3337           0.141945
       6 │ CS001211000025         456           0.0167709
       7 │ CS001212000027         448           0.0164234
       8 │ CS001212000031         296           0.00981926
       9 │ CS001212000046         228           0.00686479
      10 │ CS001212000070         456           0.0167709

## 27.　対数変換

---
> J-061: レシート明細データ（df_receipt）の売上金額（amount）を顧客ID（customer_id）ごとに合計し、売上金額合計を常用対数化（底10）して顧客ID、売上金額合計とともに10件表示せよ。ただし、顧客IDが"Z"から始まるのものは非会員を表すため、除外して計算すること。

```julia
df = @chain df_receipt begin
    @rsubset(!occursin(r"^Z", :customer_id))
    groupby(:customer_id)
    @combine(:sum_amount = sum(:amount))
    @rtransform(:log_amount = log10(:sum_amount))
    @orderby(:customer_id)
end
head(df)
```

    size = (8306, 3)
    [1m10×3 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m sum_amount [0m[1m log_amount [0m
         │[90m String15       [0m[90m Int64      [0m[90m Float64    [0m
    ─────┼────────────────────────────────────────
       1 │ CS001113000004        1298     3.11327
       2 │ CS001114000005         626     2.79657
       3 │ CS001115000010        3044     3.48344
       4 │ CS001205000004        1988     3.29842
       5 │ CS001205000006        3337     3.52336
       6 │ CS001211000025         456     2.65896
       7 │ CS001212000027         448     2.65128
       8 │ CS001212000031         296     2.47129
       9 │ CS001212000046         228     2.35793
      10 │ CS001212000070         456     2.65896

---
> J-062: レシート明細データ（df_receipt）の売上金額（amount）を顧客ID（customer_id）ごとに合計し、売上金額合計を自然対数化（底e）して顧客ID、売上金額合計とともに10件表示せよ。ただし、顧客IDが"Z"から始まるのものは非会員を表すため、除外して計算すること。

```julia
df = @chain df_receipt begin
    @rsubset(!occursin(r"^Z", :customer_id))
    groupby(:customer_id)
    @combine(:sum_amount = sum(:amount))
    @rtransform(:log_amount = log(:sum_amount))
    @orderby(:customer_id)
end
head(df)
```

    size = (8306, 3)
    [1m10×3 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m sum_amount [0m[1m log_amount [0m
         │[90m String15       [0m[90m Int64      [0m[90m Float64    [0m
    ─────┼────────────────────────────────────────
       1 │ CS001113000004        1298     7.16858
       2 │ CS001114000005         626     6.43935
       3 │ CS001115000010        3044     8.02093
       4 │ CS001205000004        1988     7.59488
       5 │ CS001205000006        3337     8.11283
       6 │ CS001211000025         456     6.12249
       7 │ CS001212000027         448     6.10479
       8 │ CS001212000031         296     5.69036
       9 │ CS001212000046         228     5.42935
      10 │ CS001212000070         456     6.12249

# 欠損値処理

---
> J-063: 商品データ（df_product）の単価（unit_price）と原価（unit_cost）から各商品の利益額を算出し、結果を10件表示せよ。

```julia
df = @transform(df_product, :unit_profit = :unit_price - :unit_cost)
head(df)
```

    size = (10030, 7)
    [1m10×7 DataFrame[0m
    [1m Row [0m│[1m product_cd [0m[1m category_major_cd [0m[1m category_medium_cd [0m[1m category_small_cd [0m[1m unit_price [0m[1m unit_cost [0m[1m unit_profit [0m
         │[90m String15   [0m[90m String            [0m[90m String             [0m[90m String            [0m[90m Int64?     [0m[90m Int64?    [0m[90m Int64?      [0m
    ─────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────
       1 │ P040101001  04                 0401                040101                    198        149           49
       2 │ P040101002  04                 0401                040101                    218        164           54
       3 │ P040101003  04                 0401                040101                    230        173           57
       4 │ P040101004  04                 0401                040101                    248        186           62
       5 │ P040101005  04                 0401                040101                    268        201           67
       6 │ P040101006  04                 0401                040101                    298        224           74
       7 │ P040101007  04                 0401                040101                    338        254           84
       8 │ P040101008  04                 0401                040101                    420        315          105
       9 │ P040101009  04                 0401                040101                    498        374          124
      10 │ P040101010  04                 0401                040101                    580        435          145

---
> J-064: 商品データ（df_product）の単価（unit_price）と原価（unit_cost）から、各商品の利益率の全体平均を算出せよ。ただし、単価と原価には欠損が生じていることに注意せよ。

```julia
mean(skipmissing(1 .- df_product.unit_cost ./ df_product.unit_price))
```

    0.24911389885176904

```julia
df = @rtransform(df_product, :unit_profit_rate = 1 - :unit_cost / :unit_price)
mean(skipmissing(df.unit_profit_rate))
```

    0.24911389885176904

---
> J-065: 商品データ（df_product）の各商品について、利益率が30%となる新たな単価を求めよ。ただし、1円未満は切り捨てること。そして結果を10件表示させ、利益率がおよそ30％付近であることを確認せよ。ただし、単価（unit_price）と原価（unit_cost）には欠損が生じていることに注意せよ。

```julia
df = @chain df_product begin 
    @rtransform @passmissing :new_price = trunc(Int, :unit_cost / 0.7)
    @rtransform @passmissing :new_profit_rate = 1 - :unit_cost / :new_price
    @select(:product_cd, :unit_price, :unit_cost, :new_price, :new_profit_rate)
end
head(df)
```

    size = (10030, 5)
    [1m10×5 DataFrame[0m
    [1m Row [0m│[1m product_cd [0m[1m unit_price [0m[1m unit_cost [0m[1m new_price [0m[1m new_profit_rate [0m
         │[90m String15   [0m[90m Int64?     [0m[90m Int64?    [0m[90m Int64?    [0m[90m Float64?        [0m
    ─────┼───────────────────────────────────────────────────────────────
       1 │ P040101001         198        149        212         0.29717
       2 │ P040101002         218        164        234         0.299145
       3 │ P040101003         230        173        247         0.299595
       4 │ P040101004         248        186        265         0.298113
       5 │ P040101005         268        201        287         0.299652
       6 │ P040101006         298        224        320         0.3
       7 │ P040101007         338        254        362         0.298343
       8 │ P040101008         420        315        450         0.3
       9 │ P040101009         498        374        534         0.299625
      10 │ P040101010         580        435        621         0.299517

---
> J-066: 商品データ（df_product）の各商品について、利益率が30%となる新たな単価を求めよ。今回は、1円未満を丸めること（四捨五入または偶数への丸めで良い）。そして結果を10件表示させ、利益率がおよそ30％付近であることを確認せよ。ただし、単価（unit_price）と原価（unit_cost）には欠損が生じていることに注意せよ。

```julia
df = @chain df_product begin 
    @rtransform @passmissing :new_price = round(Int, :unit_cost / 0.7)
    @rtransform @passmissing :new_profit_rate = 1 - :unit_cost / :new_price
    @select(:product_cd, :unit_price, :unit_cost, :new_price, :new_profit_rate)
end
head(df)
```

    size = (10030, 5)
    [1m10×5 DataFrame[0m
    [1m Row [0m│[1m product_cd [0m[1m unit_price [0m[1m unit_cost [0m[1m new_price [0m[1m new_profit_rate [0m
         │[90m String15   [0m[90m Int64?     [0m[90m Int64?    [0m[90m Int64?    [0m[90m Float64?        [0m
    ─────┼───────────────────────────────────────────────────────────────
       1 │ P040101001         198        149        213         0.300469
       2 │ P040101002         218        164        234         0.299145
       3 │ P040101003         230        173        247         0.299595
       4 │ P040101004         248        186        266         0.300752
       5 │ P040101005         268        201        287         0.299652
       6 │ P040101006         298        224        320         0.3
       7 │ P040101007         338        254        363         0.300275
       8 │ P040101008         420        315        450         0.3
       9 │ P040101009         498        374        534         0.299625
      10 │ P040101010         580        435        621         0.299517

---
> J-067: 商品データ（df_product）の各商品について、利益率が30%となる新たな単価を求めよ。今回は、1円未満を切り上げること。そして結果を10件表示させ、利益率がおよそ30％付近であることを確認せよ。ただし、単価（unit_price）と原価（unit_cost）には欠損が生じていることに注意せよ。

```julia
df = @chain df_product begin 
    @rtransform @passmissing :new_price = ceil(Int, :unit_cost / 0.7)
    @rtransform @passmissing :new_profit_rate = 1 - :unit_cost / :new_price
    @select(:product_cd, :unit_price, :unit_cost, :new_price, :new_profit_rate)
end
head(df)
```

    size = (10030, 5)
    [1m10×5 DataFrame[0m
    [1m Row [0m│[1m product_cd [0m[1m unit_price [0m[1m unit_cost [0m[1m new_price [0m[1m new_profit_rate [0m
         │[90m String15   [0m[90m Int64?     [0m[90m Int64?    [0m[90m Int64?    [0m[90m Float64?        [0m
    ─────┼───────────────────────────────────────────────────────────────
       1 │ P040101001         198        149        213         0.300469
       2 │ P040101002         218        164        235         0.302128
       3 │ P040101003         230        173        248         0.302419
       4 │ P040101004         248        186        266         0.300752
       5 │ P040101005         268        201        288         0.302083
       6 │ P040101006         298        224        320         0.3
       7 │ P040101007         338        254        363         0.300275
       8 │ P040101008         420        315        451         0.301552
       9 │ P040101009         498        374        535         0.300935
      10 │ P040101010         580        435        622         0.300643

---
> J-068: 商品データ（df_product）の各商品について、消費税率10％の税込み金額を求めよ。1円未満の端数は切り捨てとし、結果を10件表示せよ。ただし、単価（unit_price）には欠損が生じていることに注意せよ。

```julia
df = @chain df_product begin 
    @rtransform @passmissing :tax_price = trunc(Int, :unit_price * 1.1)
    @select(:product_cd, :unit_price, :tax_price)
end
head(df)
```

    size = (10030, 3)
    [1m10×3 DataFrame[0m
    [1m Row [0m│[1m product_cd [0m[1m unit_price [0m[1m tax_price [0m
         │[90m String15   [0m[90m Int64?     [0m[90m Int64?    [0m
    ─────┼───────────────────────────────────
       1 │ P040101001         198        217
       2 │ P040101002         218        239
       3 │ P040101003         230        253
       4 │ P040101004         248        272
       5 │ P040101005         268        294
       6 │ P040101006         298        327
       7 │ P040101007         338        371
       8 │ P040101008         420        462
       9 │ P040101009         498        547
      10 │ P040101010         580        638

---
> J-069: レシート明細データ（df_receipt）と商品データ（df_product）を結合し、顧客毎に全商品の売上金額合計と、カテゴリ大区分コード（category_major_cd）が"07"（瓶詰缶詰）の売上金額合計を計算の上、両者の比率を求めよ。抽出対象はカテゴリ大区分コード"07"（瓶詰缶詰）の売上実績がある顧客のみとし、結果を10件表示せよ。

```julia
df = innerjoin(df_receipt[!, [:customer_id, :product_cd, :amount]],
               df_product[!, [:category_major_cd, :product_cd]],
               on=:product_cd);

df1 = @chain df begin
    groupby(:customer_id)
    @combine(:sum_all = sum(:amount))
end

df2 = @chain df begin
    @rsubset(:category_major_cd == "07")
    groupby(:customer_id)
    @combine(:sum_07 = sum(:amount))
end

df3 = @chain innerjoin(df1, df2, on=:customer_id) begin
    @rtransform(:sales_rate = :sum_07 / :sum_all)
    @orderby(:customer_id)
end
head(df3)
```

    size = (6865, 4)
    [1m10×4 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m sum_all [0m[1m sum_07 [0m[1m sales_rate [0m
         │[90m String15       [0m[90m Int64   [0m[90m Int64  [0m[90m Float64    [0m
    ─────┼─────────────────────────────────────────────
       1 │ CS001113000004     1298    1298    1.0
       2 │ CS001114000005      626     486    0.776358
       3 │ CS001115000010     3044    2694    0.88502
       4 │ CS001205000004     1988     346    0.174044
       5 │ CS001205000006     3337    2004    0.600539
       6 │ CS001212000027      448     200    0.446429
       7 │ CS001212000031      296     296    1.0
       8 │ CS001212000046      228     108    0.473684
       9 │ CS001212000070      456     308    0.675439
      10 │ CS001213000018      243     145    0.596708

# 経過日数，月数，年数

---
> J-070: レシート明細データ（df_receipt）の売上日（sales_ymd）に対し、顧客データ（df_customer）の会員申込日（application_date）からの経過日数を計算し、顧客ID（customer_id）、売上日、会員申込日とともに10件表示せよ（sales_ymdは数値、application_dateは文字列でデータを保持している点に注意）。

```julia
using Dates
date(d) = @. Date(d ÷ 10000, (d % 10000) ÷ 100, d % 100)
df = @rtransform(innerjoin(df_receipt[!, [:customer_id, :sales_ymd]],
                           df_customer[!, [:customer_id, :application_date]],
                           on=:customer_id),
        :elapsed_days = Dates.value(date(:sales_ymd) - date(parse(Int, :application_date))))
head(df, 10)
```

    size = (65682, 4)
    [1m10×4 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m sales_ymd [0m[1m application_date [0m[1m elapsed_days [0m
         │[90m String15       [0m[90m Int64     [0m[90m String           [0m[90m Int64        [0m
    ─────┼───────────────────────────────────────────────────────────
       1 │ CS006214000001   20181103  20150201                  1371
       2 │ CS008415000097   20181118  20150322                  1337
       3 │ CS028414000014   20170712  20150711                   732
       4 │ CS025415000050   20180821  20160131                   933
       5 │ CS003515000195   20190605  20150306                  1552
       6 │ CS024514000042   20181205  20151010                  1152
       7 │ CS040415000178   20190922  20150627                  1548
       8 │ CS027514000015   20191010  20151101                  1439
       9 │ CS025415000134   20190918  20150720                  1521
      10 │ CS021515000126   20171010  20150508                   886

---
> J-071: レシート明細データ（df_receipt）の売上日（sales_ymd）に対し、顧客データ（df_customer）の会員申込日（application_date）からの経過月数を計算し、顧客ID（customer_id）、売上日、会員申込日とともに10件表示せよ（sales_ymdは数値、application_dateは文字列でデータを保持している点に注意）。1ヶ月未満は切り捨てること。

```julia
using Dates
date2(d) = @. (d ÷ 10000, (d % 10000) ÷ 100, d % 100)
function date3(d_begin, d_end)
    y, m, d = date2(d_end) .- date2(parse(Int, d_begin))
    12y + m + (d < 0)*(-1)
end;
```

```julia
df = @rtransform(innerjoin(df_receipt[!, [:customer_id, :sales_ymd]],
                           df_customer[!, [:customer_id, :application_date]],
                           on=:customer_id),
        :elapsed_months = date3(:application_date, :sales_ymd))
head(df)
```

    size = (65682, 4)
    [1m10×4 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m sales_ymd [0m[1m application_date [0m[1m elapsed_months [0m
         │[90m String15       [0m[90m Int64     [0m[90m String           [0m[90m Int64          [0m
    ─────┼─────────────────────────────────────────────────────────────
       1 │ CS006214000001   20181103  20150201                      45
       2 │ CS008415000097   20181118  20150322                      43
       3 │ CS028414000014   20170712  20150711                      24
       4 │ CS025415000050   20180821  20160131                      30
       5 │ CS003515000195   20190605  20150306                      50
       6 │ CS024514000042   20181205  20151010                      37
       7 │ CS040415000178   20190922  20150627                      50
       8 │ CS027514000015   20191010  20151101                      47
       9 │ CS025415000134   20190918  20150720                      49
      10 │ CS021515000126   20171010  20150508                      29

---
> J-072: レシート明細データ（df_receipt）の売上日（df_customer）に対し、顧客データ（df_customer）の会員申込日（application_date）からの経過年数を計算し、顧客ID（customer_id）、売上日、会員申込日とともに10件表示せよ（sales_ymdは数値、application_dateは文字列でデータを保持している点に注意）。1年未満は切り捨てること。

```julia
year2(ymd, ymd2) = (ymd2 .- ymd) .÷ 10000
df = @rtransform(innerjoin(df_receipt[!, [:customer_id, :sales_ymd]],
                           df_customer[!, [:customer_id, :application_date]],
                           on=:customer_id),
        :elapsed_years = year2(parse(Int, :application_date), :sales_ymd))
head(df)
```

    size = (65682, 4)
    [1m10×4 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m sales_ymd [0m[1m application_date [0m[1m elapsed_years [0m
         │[90m String15       [0m[90m Int64     [0m[90m String           [0m[90m Int64         [0m
    ─────┼────────────────────────────────────────────────────────────
       1 │ CS006214000001   20181103  20150201                      3
       2 │ CS008415000097   20181118  20150322                      3
       3 │ CS028414000014   20170712  20150711                      2
       4 │ CS025415000050   20180821  20160131                      2
       5 │ CS003515000195   20190605  20150306                      4
       6 │ CS024514000042   20181205  20151010                      3
       7 │ CS040415000178   20190922  20150627                      4
       8 │ CS027514000015   20191010  20151101                      3
       9 │ CS025415000134   20190918  20150720                      4
      10 │ CS021515000126   20171010  20150508                      2

---
> J-073: レシート明細データ（df_receipt）の売上日（sales_ymd）に対し、顧客データ（df_customer）の会員申込日（application_date）からのエポック秒による経過時間を計算し、顧客ID（customer_id）、売上日、会員申込日とともに10件表示せよ（なお、sales_ymdは数値、application_dateは文字列でデータを保持している点に注意）。なお、時間情報は保有していないため各日付は0時0分0秒を表すものとする。

```julia
datetime(d) = DateTime(d ÷ 10000, (d % 10000) ÷ 100, d % 100, 0, 0, 0)

df = @chain innerjoin(df_receipt[!, [:customer_id, :sales_ymd]],
          df_customer[!, [:customer_id, :application_date]],
          on=:customer_id) begin
    @rtransform(:elapsed_epoch = convert(Int64, Dates.value(datetime(:sales_ymd) - datetime(parse(Int, :application_date)))/1000))
end
head(df)
```

    size = (65682, 4)
    [1m10×4 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m sales_ymd [0m[1m application_date [0m[1m elapsed_epoch [0m
         │[90m String15       [0m[90m Int64     [0m[90m String           [0m[90m Int64         [0m
    ─────┼────────────────────────────────────────────────────────────
       1 │ CS006214000001   20181103  20150201              118454400
       2 │ CS008415000097   20181118  20150322              115516800
       3 │ CS028414000014   20170712  20150711               63244800
       4 │ CS025415000050   20180821  20160131               80611200
       5 │ CS003515000195   20190605  20150306              134092800
       6 │ CS024514000042   20181205  20151010               99532800
       7 │ CS040415000178   20190922  20150627              133747200
       8 │ CS027514000015   20191010  20151101              124329600
       9 │ CS025415000134   20190918  20150720              131414400
      10 │ CS021515000126   20171010  20150508               76550400

---
> J-074: レシート明細データ（df_receipt）の売上日（sales_ymd）に対し、当該週の月曜日からの経過日数を計算し、売上日、直前の月曜日付とともに10件表示せよ（sales_ymdは数値でデータを保持している点に注意）。

```julia
date(d) = Date(d ÷ 10000, (d % 10000) ÷ 100, d % 100)
df = @chain df_receipt begin
    @rtransform(:monday = firstdayofweek(date(:sales_ymd)))
    @rtransform(:elapsed_days = Dates.value(date(:sales_ymd) - :monday))
    @select(:sales_ymd, :elapsed_days, :monday)
end
head(df)
```

    size = (104681, 3)
    [1m10×3 DataFrame[0m
    [1m Row [0m│[1m sales_ymd [0m[1m elapsed_days [0m[1m monday     [0m
         │[90m Int64     [0m[90m Int64        [0m[90m Date       [0m
    ─────┼─────────────────────────────────────
       1 │  20181103             5  2018-10-29
       2 │  20181118             6  2018-11-12
       3 │  20170712             2  2017-07-10
       4 │  20190205             1  2019-02-04
       5 │  20180821             1  2018-08-20
       6 │  20190605             2  2019-06-03
       7 │  20181205             2  2018-12-03
       8 │  20190922             6  2019-09-16
       9 │  20170504             3  2017-05-01
      10 │  20191010             3  2019-10-07

# 無作為抽出

---
> J-075: 顧客データ（df_customer）からランダムに1%のデータを抽出し、先頭から10件表示せよ。

```julia
using StatsBase
n = nrow(df_customer)
df = df_customer[sample(1:n, floor(Int, n*0.01), replace=false), :]
head(df)
```

    size = (219, 11)
    [1m10×11 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m customer_name [0m[1m gender_cd [0m[1m gender  [0m[1m birth_day  [0m[1m age   [0m[1m postal_cd [0m[1m address                        [0m[1m application_store_cd [0m[1m application_date [0m[1m status_cd    [0m
         │[90m String15       [0m[90m String31      [0m[90m String    [0m[90m String7 [0m[90m Date       [0m[90m Int64 [0m[90m String15  [0m[90m String                         [0m[90m String7              [0m[90m String           [0m[90m String15     [0m
    ─────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       1 │ CS025414000027  川本 美紀      1          女性     1970-12-12     48  242-0024   神奈川県大和市福田**********    S14025                20151010          F-20100721-D
       2 │ CS037602000013  伊集院 竜也    0          男性     1952-03-25     67  135-0021   東京都江東区白河**********      S13037                20151128          0-00000000-0
       3 │ CS002314000261  伴 茜          9          不明     1980-07-08     38  185-0011   東京都国分寺市本多**********    S13002                20180112          0-00000000-0
       4 │ CS031515000016  板垣 結衣      1          女性     1962-03-25     57  151-0072   東京都渋谷区幡ヶ谷**********    S13031                20150518          C-20100707-D
       5 │ CS008414000039  土屋 りえ      1          女性     1971-07-11     47  157-0067   東京都世田谷区喜多見**********  S13008                20151127          F-20090822-D
       6 │ CS020713000052  矢口 陽子      1          女性     1944-12-21     74  173-0005   東京都板橋区仲宿**********      S13020                20150204          0-00000000-0
       7 │ CS004315000319  金田 陽子      1          女性     1984-02-23     35  176-0025   東京都練馬区中村南**********    S13004                20160410          2-20090925-1
       8 │ CS007412000215  三好 愛        1          女性     1969-02-08     50  276-0022   千葉県八千代市上高野**********  S12007                20150510          0-00000000-0
       9 │ CS001613000230  長澤 千夏      1          女性     1949-07-20     69  144-0046   東京都大田区東六郷**********    S13001                20160505          0-00000000-0
      10 │ CS020315000061  藤島 華子      1          女性     1986-09-19     32  115-0055   東京都北区赤羽西**********      S13020                20150724          6-20090925-7

**以下の方法は，各行に [0,1) の一様乱数を与え，その値が希望する抽出率の数値未満の行を抽出するというものである。実際に抽出される件数が若干の幅を持つが，実用上の問題はないであろう。**

```julia
# 別解
using StatsBase
df = @chain df_customer begin
    @rtransform(:uniformrandom = rand(1)[1])
    @rsubset(:uniformrandom < 0.01)
    @select($(Not(:uniformrandom)))  # 乱数列は用済みなので削除
end
println("df_customer の件数 = $(nrow(df_customer)), 抽出された件数 = $(nrow(df)), 抽出率 = $(100nrow(df)/nrow(df_customer)) %")
head(df)
```

    df_customer の件数 = 21971, 抽出された件数 = 218, 抽出率 = 0.9922170133357607 %
    size = (218, 11)
    [1m10×11 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m customer_name [0m[1m gender_cd [0m[1m gender  [0m[1m birth_day  [0m[1m age   [0m[1m postal_cd [0m[1m address                           [0m[1m application_store_cd [0m[1m application_date [0m[1m status_cd    [0m
         │[90m String15       [0m[90m String31      [0m[90m String    [0m[90m String7 [0m[90m Date       [0m[90m Int64 [0m[90m String15  [0m[90m String                            [0m[90m String7              [0m[90m String           [0m[90m String15     [0m
    ─────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       1 │ CS001215000145  田崎 美紀      1          女性     1995-03-29     24  144-0055   東京都大田区仲六郷**********       S13001                20170605          6-20090929-2
       2 │ CS035513000025  松永 桃子      1          女性     1964-08-10     54  154-0015   東京都世田谷区桜新町**********     S13035                20150401          9-20091109-A
       3 │ CS004514000338  島崎 貴美子    1          女性     1964-12-07     54  165-0033   東京都中野区若宮**********         S13004                20170301          0-00000000-0
       4 │ CS011515000108  久野 幸子      1          女性     1965-04-11     53  223-0064   神奈川県横浜市港北区下田町******…  S14011                20150315          C-20100828-C
       5 │ CS004413000614  藤村 朝陽      1          女性     1977-06-25     41  165-0033   東京都中野区若宮**********         S13004                20170603          0-00000000-0
       6 │ CS011612000101  渡部 あや子    1          女性     1949-04-11     69  211-0025   神奈川県川崎市中原区木月********…  S14011                20150711          0-00000000-0
       7 │ CS008313000158  入江 真悠子    1          女性     1986-02-08     33  182-0004   東京都調布市入間町**********       S13008                20150511          0-00000000-0
       8 │ CS024615000029  水谷 由美子    1          女性     1957-12-24     61  214-0034   神奈川県川崎市多摩区三田********…  S14024                20150414          9-20091207-6
       9 │ CS004504000015  小幡 良介      0          男性     1961-07-01     57  176-0024   東京都練馬区中村**********         S13004                20160906          0-00000000-0
      10 │ CS013213000006  野田 文世      1          女性     1991-12-01     27  261-0026   千葉県千葉市美浜区幕張西********…  S12013                20160102          0-00000000-0

# 層化抽出

---
> J-076: 顧客データ（df_customer）から性別コード（gender_cd）の割合に基づきランダムに10%のデータを層化抽出し、性別コードごとに件数を集計せよ。

```julia
df = @chain df_customer begin
    groupby(:gender_cd)
    @combine(:customer_num = length(:gender_cd))
end
head(df)
```

    size = (3, 2)
    [1m3×2 DataFrame[0m
    [1m Row [0m│[1m gender_cd [0m[1m customer_num [0m
         │[90m String    [0m[90m Int64        [0m
    ─────┼─────────────────────────
       1 │ 1                 17918
       2 │ 9                  1072
       3 │ 0                  2981

```julia
function sampling(df)
    n = nrow(df)
    return df[sample(1:n, floor(Int, n*0.1), replace=false), :]
end

gd = groupby(df_customer, :gender_cd)
df = sampling(gd[1])
for i in 2:3
    df = vcat(df, sampling(gd[i]))
end

gd = groupby(df, :gender_cd)
combine(gd, nrow)
```

<div><div style = "float: left;"><span>3×2 DataFrame</span></div><div style = "clear: both;"></div></div><div class = "data-frame" style = "overflow-x: scroll;"><table class = "data-frame" style = "margin-bottom: 6px;"><thead><tr class = "header"><th class = "rowNumber" style = "font-weight: bold; text-align: right;">Row</th><th style = "text-align: left;">gender_cd</th><th style = "text-align: left;">nrow</th></tr><tr class = "subheader headerLastRow"><th class = "rowNumber" style = "font-weight: bold; text-align: right;"></th><th title = "String" style = "text-align: left;">String</th><th title = "Int64" style = "text-align: left;">Int64</th></tr></thead><tbody><tr><td class = "rowNumber" style = "font-weight: bold; text-align: right;">1</td><td style = "text-align: left;">1</td><td style = "text-align: right;">1791</td></tr><tr><td class = "rowNumber" style = "font-weight: bold; text-align: right;">2</td><td style = "text-align: left;">9</td><td style = "text-align: right;">107</td></tr><tr><td class = "rowNumber" style = "font-weight: bold; text-align: right;">3</td><td style = "text-align: left;">0</td><td style = "text-align: right;">298</td></tr></tbody></table></div>

**データフレームをグループ化し，それぞれのデータフレームに対して一定の処理を行い，再度一つのデータフレームにまとめるという作業は以下のようにすれば簡単になる。**

```julia
# 別解
gd = groupby(df_customer, :gender_cd)  # グループ化
df = [sampling(df) for df in gd]  # 処理（関数内で行う）
df2 = vcat(df...)  # 再度一つのデータフレームにまとめる

gd2 = groupby(df2, :gender_cd)
combine(gd2, nrow)
```

<div><div style = "float: left;"><span>3×2 DataFrame</span></div><div style = "clear: both;"></div></div><div class = "data-frame" style = "overflow-x: scroll;"><table class = "data-frame" style = "margin-bottom: 6px;"><thead><tr class = "header"><th class = "rowNumber" style = "font-weight: bold; text-align: right;">Row</th><th style = "text-align: left;">gender_cd</th><th style = "text-align: left;">nrow</th></tr><tr class = "subheader headerLastRow"><th class = "rowNumber" style = "font-weight: bold; text-align: right;"></th><th title = "String" style = "text-align: left;">String</th><th title = "Int64" style = "text-align: left;">Int64</th></tr></thead><tbody><tr><td class = "rowNumber" style = "font-weight: bold; text-align: right;">1</td><td style = "text-align: left;">1</td><td style = "text-align: right;">1791</td></tr><tr><td class = "rowNumber" style = "font-weight: bold; text-align: right;">2</td><td style = "text-align: left;">9</td><td style = "text-align: right;">107</td></tr><tr><td class = "rowNumber" style = "font-weight: bold; text-align: right;">3</td><td style = "text-align: left;">0</td><td style = "text-align: right;">298</td></tr></tbody></table></div>

# 外れ値

---
> J-077: レシート明細データ（df_receipt）の売上金額を顧客単位に合計し、合計した売上金額の外れ値を抽出せよ。なお、外れ値は売上金額合計を対数化したうえで平均と標準偏差を計算し、その平均から3σを超えて離れたものとする（自然対数と常用対数のどちらでも可）。結果は10件表示せよ。

```julia
df = @chain df_receipt begin
    groupby(:customer_id)
    @combine(:sum_amount = sum(:amount), :log_sum_amount = log(sum(:amount)))
end;
```

```julia
using StatsBase
df2 = @rsubset(df, abs(:log_sum_amount - mean(df.log_sum_amount)) / std(df.log_sum_amount) > 3)
head(df2)
```

    size = (1, 3)
    [1m1×3 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m sum_amount [0m[1m log_sum_amount [0m
         │[90m String15       [0m[90m Int64      [0m[90m Float64        [0m
    ─────┼────────────────────────────────────────────
       1 │ ZZ000000000000    12395003         16.3328

---
> J-078: レシート明細データ（df_receipt）の売上金額（amount）を顧客単位に合計し、合計した売上金額の外れ値を抽出せよ。ただし、顧客IDが"Z"から始まるのものは非会員を表すため、除外して計算すること。なお、ここでは外れ値を第1四分位と第3四分位の差であるIQRを用いて、「第1四分位数-1.5×IQR」を下回るもの、または「第3四分位数+1.5×IQR」を超えるものとする。結果は10件表示せよ。

```julia
df = @chain df_receipt begin
    @rsubset(!occursin(r"^Z", :customer_id))
    groupby(:customer_id)
    @combine(:sum_amount = sum(:amount))
end;
```

```julia
(Q1, Q3) = quantile(df.sum_amount, [0.25, 0.75])
(qt1, qt3) = (Q1 - 1.5(Q3 - Q1), Q3 + 1.5(Q3 - Q1))
println("qt1 = $qt1,  qt3 = $qt3")
```

    qt1 = -4104.0,  qt3 = 8302.0

```julia
df2 = @chain df begin
    @rsubset(:sum_amount < qt1 || :sum_amount > qt3)
    @orderby(:customer_id)
end
df2 = sort(df2, :customer_id)
head(df2)
```

    size = (393, 2)
    [1m10×2 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m sum_amount [0m
         │[90m String15       [0m[90m Int64      [0m
    ─────┼────────────────────────────
       1 │ CS001414000048        8584
       2 │ CS001605000009       18925
       3 │ CS002415000594        9568
       4 │ CS004414000181        9584
       5 │ CS005415000137        8734
       6 │ CS006414000001        9156
       7 │ CS006414000029        9179
       8 │ CS006415000105       10042
       9 │ CS006415000147       12723
      10 │ CS006415000157       10648

# 欠損値の補完

---
> J-079: 商品データ（df_product）の各項目に対し、欠損数を確認せよ。

```julia
a = map(eachcol(df_product)) do col
    length(col) - length(collect(skipmissing(col)))
end;

DataFrame(variable = names(df_product), missings = a)
```

<div><div style = "float: left;"><span>6×2 DataFrame</span></div><div style = "clear: both;"></div></div><div class = "data-frame" style = "overflow-x: scroll;"><table class = "data-frame" style = "margin-bottom: 6px;"><thead><tr class = "header"><th class = "rowNumber" style = "font-weight: bold; text-align: right;">Row</th><th style = "text-align: left;">variable</th><th style = "text-align: left;">missings</th></tr><tr class = "subheader headerLastRow"><th class = "rowNumber" style = "font-weight: bold; text-align: right;"></th><th title = "String" style = "text-align: left;">String</th><th title = "Int64" style = "text-align: left;">Int64</th></tr></thead><tbody><tr><td class = "rowNumber" style = "font-weight: bold; text-align: right;">1</td><td style = "text-align: left;">product_cd</td><td style = "text-align: right;">0</td></tr><tr><td class = "rowNumber" style = "font-weight: bold; text-align: right;">2</td><td style = "text-align: left;">category_major_cd</td><td style = "text-align: right;">0</td></tr><tr><td class = "rowNumber" style = "font-weight: bold; text-align: right;">3</td><td style = "text-align: left;">category_medium_cd</td><td style = "text-align: right;">0</td></tr><tr><td class = "rowNumber" style = "font-weight: bold; text-align: right;">4</td><td style = "text-align: left;">category_small_cd</td><td style = "text-align: right;">0</td></tr><tr><td class = "rowNumber" style = "font-weight: bold; text-align: right;">5</td><td style = "text-align: left;">unit_price</td><td style = "text-align: right;">7</td></tr><tr><td class = "rowNumber" style = "font-weight: bold; text-align: right;">6</td><td style = "text-align: left;">unit_cost</td><td style = "text-align: right;">7</td></tr></tbody></table></div>

---
> J-080: 商品データ（df_product）のいずれかの項目に欠損が発生しているレコードを全て削除した新たな商品データを作成せよ。なお、削除前後の件数を表示させ、079で確認した件数だけ減少していることも確認すること。

```julia
df = df_product |> dropmissing
println("欠損値削除前の件数 = $(nrow(df_product)),  欠損値削除後の件数 = $(nrow(df))")
```

    欠損値削除前の件数 = 10030,  欠損値削除後の件数 = 10023

---
> J-081: 単価（unit_price）と原価（unit_cost）の欠損値について、それぞれの平均値で補完した新たな商品データを作成せよ。なお、平均値については1円未満を丸めること（四捨五入または偶数への丸めで良い）。補完実施後、各項目について欠損が生じていないことも確認すること。

```julia
price_mean = round(Int, mean(skipmissing(df_product.unit_price)))
cost_mean  = round(Int, mean(skipmissing(df_product.unit_cost)))
println("price_mean = $price_mean,  cost_mean = $cost_mean")

println(df[[159, 160, 197, 497, 1532, 2013, 6297, 7076], :])
println("nrow = $(nrow(df))")
df2 = @chain df_product begin
    @rtransform(:unit_price = coalesce(:unit_price, price_mean))
    @rtransform(:unit_cost  = coalesce(:unit_cost,  cost_mean))
end
println(df2[[159, 160, 197, 497, 1532, 2013, 6297, 7076], :])
println("nrow = $(nrow(df2))")
```

    price_mean = 403,  cost_mean = 302
    [1m8×6 DataFrame[0m
    [1m Row [0m│[1m product_cd [0m[1m category_major_cd [0m[1m category_medium_cd [0m[1m category_small_cd [0m[1m unit_price [0m[1m unit_cost [0m
         │[90m String15   [0m[90m String            [0m[90m String             [0m[90m String            [0m[90m Int64      [0m[90m Int64     [0m
    ─────┼─────────────────────────────────────────────────────────────────────────────────────────────
       1 │ P040802006  04                 0408                040802                    438        329
       2 │ P041302001  04                 0413                041302                    450        338
       3 │ P050104002  05                 0501                050104                    115         86
       4 │ P050406003  05                 0504                050406                    128         96
       5 │ P060804002  06                 0608                060804                    450        347
       6 │ P070203005  07                 0702                070203                    100         75
       7 │ P080603001  08                 0806                080603                     80         63
       8 │ P090205007  09                 0902                090205                    285        214
    nrow = 10023
    [1m8×6 DataFrame[0m
    [1m Row [0m│[1m product_cd [0m[1m category_major_cd [0m[1m category_medium_cd [0m[1m category_small_cd [0m[1m unit_price [0m[1m unit_cost [0m
         │[90m String15   [0m[90m String            [0m[90m String             [0m[90m String            [0m[90m Int64      [0m[90m Int64     [0m
    ─────┼─────────────────────────────────────────────────────────────────────────────────────────────
       1 │ P040802006  04                 0408                040802                    438        329
       2 │ P040802007  04                 0408                040802                    403        302
       3 │ P050103021  05                 0501                050103                    403        302
       4 │ P050405009  05                 0504                050405                    403        302
       5 │ P060802026  06                 0608                060802                    403        302
       6 │ P070202092  07                 0702                070202                    403        302
       7 │ P080504027  08                 0805                080504                    403        302
       8 │ P090204185  09                 0902                090204                    403        302
    nrow = 10030

---
> J-082: 単価（unit_price）と原価（unit_cost）の欠損値について、それぞれの中央値で補完した新たな商品データを作成せよ。なお、中央値については1円未満を丸めること（四捨五入または偶数への丸めで良い）。補完実施後、各項目について欠損が生じていないことも確認すること。

```julia
price_median = round(Int, median(skipmissing(df_product.unit_price)))
cost_median  = round(Int, median(skipmissing(df_product.unit_cost)))
println("price_median = $price_median,  cost_median = $cost_median")

println(df[[159, 160, 197, 497, 1532, 2013, 6297, 7076], :])
println("nrow = $(nrow(df))")
df2 = @chain df_product begin
    @rtransform(:unit_price = coalesce(:unit_price, price_median))
    @rtransform(:unit_cost  = coalesce(:unit_cost,  cost_median))
end
println(df2[[159, 160, 197, 497, 1532, 2013, 6297, 7076], :])
println("nrow = $(nrow(df2))")
```

    price_median = 252,  cost_median = 189
    [1m8×6 DataFrame[0m
    [1m Row [0m│[1m product_cd [0m[1m category_major_cd [0m[1m category_medium_cd [0m[1m category_small_cd [0m[1m unit_price [0m[1m unit_cost [0m
         │[90m String15   [0m[90m String            [0m[90m String             [0m[90m String            [0m[90m Int64      [0m[90m Int64     [0m
    ─────┼─────────────────────────────────────────────────────────────────────────────────────────────
       1 │ P040802006  04                 0408                040802                    438        329
       2 │ P041302001  04                 0413                041302                    450        338
       3 │ P050104002  05                 0501                050104                    115         86
       4 │ P050406003  05                 0504                050406                    128         96
       5 │ P060804002  06                 0608                060804                    450        347
       6 │ P070203005  07                 0702                070203                    100         75
       7 │ P080603001  08                 0806                080603                     80         63
       8 │ P090205007  09                 0902                090205                    285        214
    nrow = 10023
    [1m8×6 DataFrame[0m
    [1m Row [0m│[1m product_cd [0m[1m category_major_cd [0m[1m category_medium_cd [0m[1m category_small_cd [0m[1m unit_price [0m[1m unit_cost [0m
         │[90m String15   [0m[90m String            [0m[90m String             [0m[90m String            [0m[90m Int64      [0m[90m Int64     [0m
    ─────┼─────────────────────────────────────────────────────────────────────────────────────────────
       1 │ P040802006  04                 0408                040802                    438        329
       2 │ P040802007  04                 0408                040802                    252        189
       3 │ P050103021  05                 0501                050103                    252        189
       4 │ P050405009  05                 0504                050405                    252        189
       5 │ P060802026  06                 0608                060802                    252        189
       6 │ P070202092  07                 0702                070202                    252        189
       7 │ P080504027  08                 0805                080504                    252        189
       8 │ P090204185  09                 0902                090204                    252        189
    nrow = 10030

---
> J-083: 単価（unit_price）と原価（unit_cost）の欠損値について、各商品のカテゴリ小区分コード（category_small_cd）ごとに算出した中央値で補完した新たな商品データを作成せよ。なお、中央値については1円未満を丸めること（四捨五入または偶数への丸めで良い）。補完実施後、各項目について欠損が生じていないことも確認すること。

```julia
using StatsBase

df = @chain df_product begin
    groupby(:category_small_cd)
    @combine(:unit_price_median = round(Int, median(skipmissing(:unit_price))),
             :unit_cost_median  = round(Int, median(skipmissing(:unit_cost))))
end;
```

```julia
df2 = innerjoin(df_product, df, on=:category_small_cd);
```

```julia
println("Before")
println(df2[[159, 160, 197, 497, 1532, 2013, 6297, 7076], :])
for i in 1:nrow(df2)
    ismissing(df2.unit_price[i]) && (df2.unit_price[i] = df2.unit_price_median[i])
    ismissing(df2.unit_cost[i]) && (df2.unit_cost[i] = df2.unit_cost_median[i])
end
println("After")
println(df2[[159, 160, 197, 497, 1532, 2013, 6297, 7076], :])
```

    Before
    [1m8×8 DataFrame[0m
    [1m Row [0m│[1m product_cd [0m[1m category_major_cd [0m[1m category_medium_cd [0m[1m category_small_cd [0m[1m unit_price [0m[1m unit_cost [0m[1m unit_price_median [0m[1m unit_cost_median [0m
         │[90m String15   [0m[90m String            [0m[90m String             [0m[90m String            [0m[90m Int64?     [0m[90m Int64?    [0m[90m Int64             [0m[90m Int64            [0m
    ─────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       1 │ P040802006  04                 0408                040802                    438        329                313               235
       2 │ P040802007  04                 0408                040802            [90m    missing [0m[90m   missing [0m               313               235
       3 │ P050103021  05                 0501                050103            [90m    missing [0m[90m   missing [0m               132               100
       4 │ P050405009  05                 0504                050405            [90m    missing [0m[90m   missing [0m               178               134
       5 │ P060802026  06                 0608                060802            [90m    missing [0m[90m   missing [0m               270               200
       6 │ P070202092  07                 0702                070202            [90m    missing [0m[90m   missing [0m               238               179
       7 │ P080504027  08                 0805                080504            [90m    missing [0m[90m   missing [0m               258               196
       8 │ P090204185  09                 0902                090204            [90m    missing [0m[90m   missing [0m               694               521
    After
    [1m8×8 DataFrame[0m
    [1m Row [0m│[1m product_cd [0m[1m category_major_cd [0m[1m category_medium_cd [0m[1m category_small_cd [0m[1m unit_price [0m[1m unit_cost [0m[1m unit_price_median [0m[1m unit_cost_median [0m
         │[90m String15   [0m[90m String            [0m[90m String             [0m[90m String            [0m[90m Int64?     [0m[90m Int64?    [0m[90m Int64             [0m[90m Int64            [0m
    ─────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       1 │ P040802006  04                 0408                040802                    438        329                313               235
       2 │ P040802007  04                 0408                040802                    313        235                313               235
       3 │ P050103021  05                 0501                050103                    132        100                132               100
       4 │ P050405009  05                 0504                050405                    178        134                178               134
       5 │ P060802026  06                 0608                060802                    270        200                270               200
       6 │ P070202092  07                 0702                070202                    238        179                238               179
       7 │ P080504027  08                 0805                080504                    258        196                258               196
       8 │ P090204185  09                 0902                090204                    694        521                694               521

---
> J-084: 顧客データ（df_customer）の全顧客に対して全期間の売上金額に占める2019年売上金額の割合を計算し、新たなデータを作成せよ。ただし、売上実績がない場合は0として扱うこと。そして計算した割合が0超のものを抽出し、結果を10件表示せよ。また、作成したデータに欠損が存在しないことを確認せよ。

```julia
df2019 = @chain df_receipt begin
    @rsubset(:sales_ymd ÷ 10000 == 2019)
    @select(:customer_id, :amount)
    groupby(:customer_id)
    @combine(:amount_2019 = sum(:amount))
end;

dfall = @chain df_receipt begin
    @select(:customer_id, :amount)
    groupby(:customer_id)
    @combine(:amount_all = sum(:amount))
end;
```

```julia
df = @chain leftjoin(df2019, dfall, on=:customer_id) begin
    coalesce(0)
    @rtransform(:amount_rate = :amount_2019 / :amount_all)
    @rsubset(:amount_rate > 0)
    @orderby(:customer_id)
end;
head(df)
```

    size = (5020, 4)
    [1m10×4 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m amount_2019 [0m[1m amount_all [0m[1m amount_rate [0m
         │[90m String15       [0m[90m Int64       [0m[90m Int64?     [0m[90m Float64     [0m
    ─────┼──────────────────────────────────────────────────────
       1 │ CS001113000004         1298        1298     1.0
       2 │ CS001114000005          188         626     0.300319
       3 │ CS001115000010          578        3044     0.189882
       4 │ CS001205000004          702        1988     0.353119
       5 │ CS001205000006          486        3337     0.14564
       6 │ CS001211000025          456         456     1.0
       7 │ CS001212000070          456         456     1.0
       8 │ CS001214000009          664        4685     0.141729
       9 │ CS001214000017         2962        4132     0.716844
      10 │ CS001214000048         1889        2374     0.795703

```julia
a = map(eachcol(df)) do col
    length(col) - length(collect(skipmissing(col)))
end;

DataFrame(variable = names(df), missings = a)
```

<div><div style = "float: left;"><span>4×2 DataFrame</span></div><div style = "clear: both;"></div></div><div class = "data-frame" style = "overflow-x: scroll;"><table class = "data-frame" style = "margin-bottom: 6px;"><thead><tr class = "header"><th class = "rowNumber" style = "font-weight: bold; text-align: right;">Row</th><th style = "text-align: left;">variable</th><th style = "text-align: left;">missings</th></tr><tr class = "subheader headerLastRow"><th class = "rowNumber" style = "font-weight: bold; text-align: right;"></th><th title = "String" style = "text-align: left;">String</th><th title = "Int64" style = "text-align: left;">Int64</th></tr></thead><tbody><tr><td class = "rowNumber" style = "font-weight: bold; text-align: right;">1</td><td style = "text-align: left;">customer_id</td><td style = "text-align: right;">0</td></tr><tr><td class = "rowNumber" style = "font-weight: bold; text-align: right;">2</td><td style = "text-align: left;">amount_2019</td><td style = "text-align: right;">0</td></tr><tr><td class = "rowNumber" style = "font-weight: bold; text-align: right;">3</td><td style = "text-align: left;">amount_all</td><td style = "text-align: right;">0</td></tr><tr><td class = "rowNumber" style = "font-weight: bold; text-align: right;">4</td><td style = "text-align: left;">amount_rate</td><td style = "text-align: right;">0</td></tr></tbody></table></div>

# 位置情報（経緯度と距離）

---
> J-085: 顧客データ（df_customer）の全顧客に対し、郵便番号（postal_cd）を用いてジオコードデータ（df_geocode）を紐付け、新たな顧客データを作成せよ。ただし、1つの郵便番号（postal_cd）に複数の経度（longitude）、緯度（latitude）情報が紐づく場合は、経度（longitude）、緯度（latitude）の平均値を算出して使用すること。また、作成結果を確認するために結果を10件表示せよ。

```julia
using StatsBase

df = @chain df_geocode begin
    @rselect(:postal_cd, :longitude, :latitude)
    groupby(:postal_cd)
    @combine(:m_longitude = mean(:longitude), :m_latitude = mean(:latitude))
end;
df_customer2 = innerjoin(df_customer, df, on=:postal_cd)
head(df_customer2)
```

    size = (21971, 13)
    [1m10×13 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m customer_name   [0m[1m gender_cd [0m[1m gender  [0m[1m birth_day  [0m[1m age   [0m[1m postal_cd [0m[1m address                    [0m[1m application_store_cd [0m[1m application_date [0m[1m status_cd    [0m[1m m_longitude [0m[1m m_latitude [0m
         │[90m String15       [0m[90m String31        [0m[90m String    [0m[90m String7 [0m[90m Date       [0m[90m Int64 [0m[90m String15  [0m[90m String                     [0m[90m String7              [0m[90m String           [0m[90m String15     [0m[90m Float64     [0m[90m Float64    [0m
    ─────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       1 │ CS020301000012  都築 育二        0          男性     1985-08-20     33  332-0031   埼玉県川口市青木**********  S13020                20161018          0-00000000-0      139.727     35.8088
       2 │ CS051412000011  奥村 愛          1          女性     1969-06-04     49  332-0023   埼玉県川口市飯塚**********  S13051                20180702          0-00000000-0      139.711     35.7965
       3 │ CS051412000012  おかやま 未華子  1          女性     1977-03-13     42  332-0023   埼玉県川口市飯塚**********  S13051                20180511          0-00000000-0      139.711     35.7965
       4 │ CS051212000001  美木 瞬          1          女性     1991-10-19     27  332-0023   埼玉県川口市飯塚**********  S13051                20180509          2-20101018-4      139.711     35.7965
       5 │ CS020112000003  荒川 まなみ      1          女性     2004-05-13     14  332-0015   埼玉県川口市川口**********  S13020                20151116          0-00000000-0      139.716     35.8023
       6 │ CS020212000016  藤沢 恵梨香      1          女性     1991-08-08     27  332-0015   埼玉県川口市川口**********  S13020                20150122          4-20100207-3      139.716     35.8023
       7 │ CS051512000001  田原 夏希        1          女性     1959-11-21     59  332-0015   埼玉県川口市川口**********  S13051                20190325          0-00000000-0      139.716     35.8023
       8 │ CS020212000004  前田 美佐        1          女性     1989-12-11     29  332-0015   埼玉県川口市川口**********  S13020                20150814          C-20090906-9      139.716     35.8023
       9 │ CS051502000001  若山 哲平        0          男性     1961-10-07     57  332-0015   埼玉県川口市川口**********  S13051                20180209          0-00000000-0      139.716     35.8023
      10 │ CS020212000008  板垣 瞳          1          女性     1997-01-05     22  332-0015   埼玉県川口市川口**********  S13020                20150105          0-00000000-0      139.716     35.8023

---
> J-086: 085で作成した緯度経度つき顧客データに対し、会員申込店舗コード（application_store_cd）をキーに店舗データ（df_store）と結合せよ。そして申込み店舗の緯度（latitude）・経度情報（longitude)と顧客住所（address）の緯度・経度を用いて申込み店舗と顧客住所の距離（単位：km）を求め、顧客ID（customer_id）、顧客住所（address）、店舗住所（address）とともに表示せよ。計算式は以下の簡易式で良いものとするが、その他精度の高い方式を利用したライブラリを利用してもかまわない。結果は10件表示せよ。

$$
\mbox{緯度（ラジアン）}：\phi \\
\mbox{経度（ラジアン）}：\lambda \\
\mbox{距離}L = 6371 * \arccos(\sin \phi_1 * \sin \phi_2
+ \cos \phi_1 * \cos \phi_2 * \cos(\lambda_1 − \lambda_2))
$$

```julia
calc_distance(x1, y1, x2, y2) = 6371 .* acos.(
                        sin.(x1 * pi / 180) 
                        * sin.(x2 .* pi / 180) 
                        + cos.(x1 .* pi / 180) 
                        * cos.(x2 .* pi / 180) 
                        * cos.((y1 .* pi / 180) - (y2 .* pi / 180 )))
```

    calc_distance (generic function with 1 method)

```julia
result = innerjoin(df_customer2, df_store, on=:application_store_cd => :store_cd, makeunique=true);
```

```julia
result_df = @chain result begin
    @rtransform(:distance = calc_distance(:m_latitude, :m_longitude, :latitude, :longitude))
    @select(:customer_id, :customer_address = :address, :store_address = :address_1, :distance)
    @orderby(:customer_id)
end
head(result_df)
```

    size = (21971, 4)
    [1m10×4 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m customer_address             [0m[1m store_address            [0m[1m distance [0m
         │[90m String15       [0m[90m String                       [0m[90m String                   [0m[90m Float64  [0m
    ─────┼──────────────────────────────────────────────────────────────────────────────────
       1 │ CS001105000001  東京都大田区西六郷**********  東京都大田区仲六郷二丁目   1.47979
       2 │ CS001112000009  東京都大田区西馬込**********  東京都大田区仲六郷二丁目   4.02049
       3 │ CS001112000019  東京都大田区昭和島**********  東京都大田区仲六郷二丁目   3.78301
       4 │ CS001112000021  東京都大田区西六郷**********  東京都大田区仲六郷二丁目   1.47979
       5 │ CS001112000023  東京都大田区昭和島**********  東京都大田区仲六郷二丁目   3.78301
       6 │ CS001112000024  東京都大田区西六郷**********  東京都大田区仲六郷二丁目   1.47979
       7 │ CS001112000029  東京都大田区西六郷**********  東京都大田区仲六郷二丁目   1.47979
       8 │ CS001112000030  東京都大田区西六郷**********  東京都大田区仲六郷二丁目   1.47979
       9 │ CS001113000004  東京都大田区西六郷**********  東京都大田区仲六郷二丁目   1.47979
      10 │ CS001113000010  東京都大田区西六郷**********  東京都大田区仲六郷二丁目   1.47979

# 名寄データ

---
> J-087: 顧客データ（df_customer）では、異なる店舗での申込みなどにより同一顧客が複数登録されている。名前（customer_name）と郵便番号（postal_cd）が同じ顧客は同一顧客とみなして1顧客1レコードとなるように名寄せした名寄顧客データを作成し、顧客データの件数、名寄顧客データの件数、重複数を算出せよ。ただし、同一顧客に対しては売上金額合計が最も高いものを残し、売上金額合計が同一もしくは売上実績がない顧客については顧客ID（customer_id）の番号が小さいものを残すこととする。

```julia
df = @chain df_receipt begin
    groupby(:customer_id)
    @combine(:sum_amount = sum(:amount))
end;
df2 = leftjoin(df_customer, df, on=:customer_id);
gd = groupby(df2, [:customer_name, :postal_cd]);

count = 0
df_customer_u = DataFrame()
for dfx in gd
    if nrow(dfx) > 1
        for i in 1:nrow(dfx)
            if ismissing(dfx.sum_amount[i])
                dfx.sum_amount[i] = 0
            end
        end
        count += 1
        dfx2 = @rorderby(dfx, -:sum_amount, :customer_id)
        push!(df_customer_u, dfx2[1,:])
    else
        append!(df_customer_u, dfx)
    end
end
println("df_customer   の行数 = ", nrow(df_customer))
println("df_customer_u の行数 = ", nrow(df_customer_u))
println("差 = ", count)
head(df_customer_u)
```

    df_customer   の行数 = 21971
    df_customer_u の行数 = 21941
    差 = 30
    size = (21941, 12)
    [1m10×12 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m customer_name [0m[1m gender_cd [0m[1m gender  [0m[1m birth_day  [0m[1m age   [0m[1m postal_cd [0m[1m address                           [0m[1m application_store_cd [0m[1m application_date [0m[1m status_cd    [0m[1m sum_amount [0m
         │[90m String15       [0m[90m String31      [0m[90m String    [0m[90m String7 [0m[90m Date       [0m[90m Int64 [0m[90m String15  [0m[90m String                            [0m[90m String7              [0m[90m String           [0m[90m String15     [0m[90m Int64?     [0m
    ─────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       1 │ CS031415000172  宇多田 貴美子  1          女性     1976-10-04     42  151-0053   東京都渋谷区代々木**********       S13031                20150529          D-20100325-C        5088
       2 │ CS001215000145  田崎 美紀      1          女性     1995-03-29     24  144-0055   東京都大田区仲六郷**********       S13001                20170605          6-20090929-2         875
       3 │ CS015414000103  奥野 陽子      1          女性     1977-08-09     41  136-0073   東京都江東区北砂**********         S13015                20150722          B-20100609-B        3122
       4 │ CS033513000180  安斎 遥        1          女性     1962-07-11     56  241-0823   神奈川県横浜市旭区善部町********…  S14033                20150728          6-20080506-5         868
       5 │ CS011215000048  芦田 沙耶      1          女性     1992-02-01     27  223-0062   神奈川県横浜市港北区日吉本町****…  S14011                20150228          C-20100421-9        3444
       6 │ CS040412000191  川井 郁恵      1          女性     1977-01-05     42  226-0021   神奈川県横浜市緑区北八朔町******…  S14040                20151101          1-20091025-4         210
       7 │ CS029415000023  梅田 里穂      1          女性     1976-01-17     43  279-0043   千葉県浦安市富士見**********       S12029                20150610          D-20100918-E        5167
       8 │ CS009315000023  皆川 文世      1          女性     1980-04-15     38  154-0012   東京都世田谷区駒沢**********       S13009                20150319          5-20080322-1         780
       9 │ CS035415000029  寺沢 真希      9          不明     1977-09-27     41  158-0096   東京都世田谷区玉川台**********     S13035                20141220          F-20101029-F        7504
      10 │ CS015315000033  福士 璃奈子    1          女性     1983-03-17     36  135-0043   東京都江東区塩浜**********         S13015                20141024          4-20080219-3         576

---
> J-088: 087で作成したデータを元に、顧客データに統合名寄IDを付与したデータを作成せよ。ただし、統合名寄IDは以下の仕様で付与するものとする。
>
> - 重複していない顧客：顧客ID（customer_id）を設定
> - 重複している顧客：前設問で抽出したレコードの顧客IDを設定
> 
> 顧客IDのユニーク件数と、統合名寄IDのユニーク件数の差も確認すること。

```julia
df = innerjoin(df_customer, df_customer_u[!, [:customer_id, :customer_name, :postal_cd]], on=[:customer_name, :postal_cd], makeunique=true)

df_customer_n = @chain df begin
    @rtransform(:integration_id = :customer_id_1)
    @select($(Not(:customer_id_1)))
end

println("件数の差 = $(length(unique(df_customer_n.customer_id)) - length(unique(df_customer_n.integration_id)))")
head(df_customer_n)
```

    件数の差 = 30
    size = (21971, 12)
    [1m10×12 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m customer_name [0m[1m gender_cd [0m[1m gender  [0m[1m birth_day  [0m[1m age   [0m[1m postal_cd [0m[1m address                           [0m[1m application_store_cd [0m[1m application_date [0m[1m status_cd    [0m[1m integration_id [0m
         │[90m String15       [0m[90m String31      [0m[90m String    [0m[90m String7 [0m[90m Date       [0m[90m Int64 [0m[90m String15  [0m[90m String                            [0m[90m String7              [0m[90m String           [0m[90m String15     [0m[90m String15       [0m
    ─────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       1 │ CS021313000114  大野 あや子    1          女性     1981-04-29     37  259-1113   神奈川県伊勢原市粟窪**********     S14021                20150905          0-00000000-0  CS021313000114
       2 │ CS037613000071  六角 雅彦      9          不明     1952-04-01     66  136-0076   東京都江東区南砂**********         S13037                20150414          0-00000000-0  CS037613000071
       3 │ CS031415000172  宇多田 貴美子  1          女性     1976-10-04     42  151-0053   東京都渋谷区代々木**********       S13031                20150529          D-20100325-C  CS031415000172
       4 │ CS028811000001  堀井 かおり    1          女性     1933-03-27     86  245-0016   神奈川県横浜市泉区和泉町********…  S14028                20160115          0-00000000-0  CS028811000001
       5 │ CS001215000145  田崎 美紀      1          女性     1995-03-29     24  144-0055   東京都大田区仲六郷**********       S13001                20170605          6-20090929-2  CS001215000145
       6 │ CS020401000016  宮下 達士      0          男性     1974-09-15     44  174-0065   東京都板橋区若木**********         S13020                20150225          0-00000000-0  CS020401000016
       7 │ CS015414000103  奥野 陽子      1          女性     1977-08-09     41  136-0073   東京都江東区北砂**********         S13015                20150722          B-20100609-B  CS015414000103
       8 │ CS029403000008  釈 人志        0          男性     1973-08-17     45  279-0003   千葉県浦安市海楽**********         S12029                20150515          0-00000000-0  CS029403000008
       9 │ CS015804000004  松谷 米蔵      0          男性     1931-05-02     87  136-0073   東京都江東区北砂**********         S13015                20150607          0-00000000-0  CS015804000004
      10 │ CS033513000180  安斎 遥        1          女性     1962-07-11     56  241-0823   神奈川県横浜市旭区善部町********…  S14033                20150728          6-20080506-5  CS033513000180

# データの分割

---
> J-089: 売上実績がある顧客を、予測モデル構築のため学習用データとテスト用データに分割したい。それぞれ8:2の割合でランダムにデータを分割せよ。

```julia
df_sales_customer = @chain df_receipt begin
    groupby(:customer_id)
    @combine(:sum_amount = sum(:amount))
end

df = innerjoin(df_customer, df_sales_customer, on=:customer_id)

using Random
n = nrow(df)
index = shuffle(collect(1:n))
df_customer_train = df[index[1:floor(Int, n*0.8)], :]
df_customer_test = df[index[floor(Int, n*0.8)+1:n], :]
println((nrow(df_customer_train)/n, nrow(df_customer_test)/n))
head(df_customer_train, 5)
head(df_customer_test, 5)
```

    (0.7999036840837949, 0.20009631591620516)
    size = (6644, 12)
    [1m5×12 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m customer_name [0m[1m gender_cd [0m[1m gender  [0m[1m birth_day  [0m[1m age   [0m[1m postal_cd [0m[1m address                           [0m[1m application_store_cd [0m[1m application_date [0m[1m status_cd    [0m[1m sum_amount [0m
         │[90m String15       [0m[90m String31      [0m[90m String    [0m[90m String7 [0m[90m Date       [0m[90m Int64 [0m[90m String15  [0m[90m String                            [0m[90m String7              [0m[90m String           [0m[90m String15     [0m[90m Int64      [0m
    ─────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       1 │ CS038515000133  沼田 佳乃      1          女性     1966-06-18     52  279-0043   千葉県浦安市富士見**********       S13038                20141210          9-20100702-B        1871
       2 │ CS008515000115  川瀬 璃奈子    1          女性     1960-12-08     58  157-0066   東京都世田谷区成城**********       S13008                20151110          C-20100524-C        3712
       3 │ CS013515000140  津川 窈        1          女性     1961-02-17     58  275-0025   千葉県習志野市秋津**********       S12013                20141109          F-20100102-D        7476
       4 │ CS012815000001  羽田 ちえみ    1          女性     1929-07-05     89  231-0826   神奈川県横浜市中区本牧荒井******…  S14012                20150811          9-20100924-7        2130
       5 │ CS025615000081  上原 真奈美    1          女性     1954-12-27     64  242-0015   神奈川県大和市下和田**********     S14025                20150703          B-20100705-6        2843
    size = (1662, 12)
    [1m5×12 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m customer_name [0m[1m gender_cd [0m[1m gender  [0m[1m birth_day  [0m[1m age   [0m[1m postal_cd [0m[1m address                           [0m[1m application_store_cd [0m[1m application_date [0m[1m status_cd    [0m[1m sum_amount [0m
         │[90m String15       [0m[90m String31      [0m[90m String    [0m[90m String7 [0m[90m Date       [0m[90m Int64 [0m[90m String15  [0m[90m String                            [0m[90m String7              [0m[90m String           [0m[90m String15     [0m[90m Int64      [0m
    ─────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       1 │ CS003413000532  紺野 恵麻      1          女性     1975-11-10     43  182-0004   東京都調布市入間町**********       S13003                20171219          2-20100418-2         306
       2 │ CS013514000080  金城 佳乃      1          女性     1961-08-04     57  261-0025   千葉県千葉市美浜区浜田**********   S12013                20150803          8-20100822-A        1453
       3 │ CS038513000088  森岡 菜々美    1          女性     1966-12-30     52  279-0043   千葉県浦安市富士見**********       S13038                20150508          6-20100816-9         807
       4 │ CS028513000121  田原 あや子    1          女性     1967-06-17     51  241-0816   神奈川県横浜市旭区笹野台********…  S14028                20150524          8-20100711-6        1548
       5 │ CS037415000261  牧 奈々        1          女性     1976-02-07     43  135-0002   東京都江東区住吉**********         S13037                20150120          D-20100205-B        4596

---
> J-090: レシート明細データ（df_receipt）は2017年1月1日〜2019年10月31日までのデータを有している。売上金額（amount）を月次で集計し、学習用に12ヶ月、テスト用に6ヶ月の時系列モデル構築用データを3セット作成せよ。

```julia
df = @chain df_receipt begin
    @rtransform(:sales_ym = :sales_ymd ÷ 100)
    groupby(:sales_ym)
    @combine(:sum_amount = sum(:amount))
    @select(:sales_ym, :sum_amount)
end

function sampling()
    start12 = rand(1:23, 1)[1]
    start6  = rand(1:29, 1)[1]
    df_train_1 = df[start12:start12+11, :];
    df_test_1  = df[start6:start6+5, :];
    return df_train_1, df_test_1
end
train, test = sampling();
println("***** first set")
println(train)
println(test)

println("***** second set")
train, test = sampling();
println(train)
println(test)

println("***** third set")
train, test = sampling();
println(train)
println(test)
```

    ***** first set
    [1m12×2 DataFrame[0m
    [1m Row [0m│[1m sales_ym [0m[1m sum_amount [0m
         │[90m Int64    [0m[90m Int64      [0m
    ─────┼──────────────────────
       1 │   201806     1012329
       2 │   201807     1058472
       3 │   201808     1045793
       4 │   201809      977114
       5 │   201810     1069939
       6 │   201811      967479
       7 │   201812     1016425
       8 │   201901     1064085
       9 │   201902      959538
      10 │   201903     1093753
      11 │   201904     1044210
      12 │   201905     1111985
    [1m6×2 DataFrame[0m
    [1m Row [0m│[1m sales_ym [0m[1m sum_amount [0m
         │[90m Int64    [0m[90m Int64      [0m
    ─────┼──────────────────────
       1 │   201806     1012329
       2 │   201807     1058472
       3 │   201808     1045793
       4 │   201809      977114
       5 │   201810     1069939
       6 │   201811      967479
    ***** second set
    [1m12×2 DataFrame[0m
    [1m Row [0m│[1m sales_ym [0m[1m sum_amount [0m
         │[90m Int64    [0m[90m Int64      [0m
    ─────┼──────────────────────
       1 │   201711      932157
       2 │   201712      939654
       3 │   201801      944509
       4 │   201802      864128
       5 │   201803      946588
       6 │   201804      937099
       7 │   201805     1004438
       8 │   201806     1012329
       9 │   201807     1058472
      10 │   201808     1045793
      11 │   201809      977114
      12 │   201810     1069939
    [1m6×2 DataFrame[0m
    [1m Row [0m│[1m sales_ym [0m[1m sum_amount [0m
         │[90m Int64    [0m[90m Int64      [0m
    ─────┼──────────────────────
       1 │   201812     1016425
       2 │   201901     1064085
       3 │   201902      959538
       4 │   201903     1093753
       5 │   201904     1044210
       6 │   201905     1111985
    ***** third set
    [1m12×2 DataFrame[0m
    [1m Row [0m│[1m sales_ym [0m[1m sum_amount [0m
         │[90m Int64    [0m[90m Int64      [0m
    ─────┼──────────────────────
       1 │   201712      939654
       2 │   201801      944509
       3 │   201802      864128
       4 │   201803      946588
       5 │   201804      937099
       6 │   201805     1004438
       7 │   201806     1012329
       8 │   201807     1058472
       9 │   201808     1045793
      10 │   201809      977114
      11 │   201810     1069939
      12 │   201811      967479
    [1m6×2 DataFrame[0m
    [1m Row [0m│[1m sales_ym [0m[1m sum_amount [0m
         │[90m Int64    [0m[90m Int64      [0m
    ─────┼──────────────────────
       1 │   201708      954836
       2 │   201709      902037
       3 │   201710      905739
       4 │   201711      932157
       5 │   201712      939654
       6 │   201801      944509

---
> J-091: 顧客データ（df_customer）の各顧客に対し、売上実績がある顧客数と売上実績がない顧客数が1:1となるようにアンダーサンプリングで抽出せよ。

```julia
df = @chain df_receipt begin
    groupby(:customer_id)
    @combine(:sum_amount = sum(:amount))
end

df2 = @chain leftjoin(df_customer, df, on=:customer_id) begin
    # @replacena(0) |>
    @rtransform(:flag = :sum_amount > 0)
end;
```

```julia
gd2 = groupby(df2, :flag)
(n1, n2) = (nrow(gd2[1]), nrow(gd2[2]))
```

    (8306, 13665)

```julia
using StatsBase
index = shuffle(collect(1:n2))
df_amount_0 = gd2[1];
df_amount_1 = gd2[2][index[1:n1], :]
nrow(df_amount_0), nrow(df_amount_1)
```

    (8306, 8306)

# 第三正規形，非正規化

---
> J-092: 顧客データ（df_customer）の性別について、第三正規形へと正規化せよ。

```julia
df_customer_std = @chain df_customer begin
    @select($(Not(:gender)))
end
head(df_customer_std, 3)
```

    size = (21971, 10)
    [1m3×10 DataFrame[0m
    [1m Row [0m│[1m customer_id    [0m[1m customer_name [0m[1m gender_cd [0m[1m birth_day  [0m[1m age   [0m[1m postal_cd [0m[1m address                        [0m[1m application_store_cd [0m[1m application_date [0m[1m status_cd    [0m
         │[90m String15       [0m[90m String31      [0m[90m String    [0m[90m Date       [0m[90m Int64 [0m[90m String15  [0m[90m String                         [0m[90m String7              [0m[90m String           [0m[90m String15     [0m
    ─────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       1 │ CS021313000114  大野 あや子    1          1981-04-29     37  259-1113   神奈川県伊勢原市粟窪**********  S14021                20150905          0-00000000-0
       2 │ CS037613000071  六角 雅彦      9          1952-04-01     66  136-0076   東京都江東区南砂**********      S13037                20150414          0-00000000-0
       3 │ CS031415000172  宇多田 貴美子  1          1976-10-04     42  151-0053   東京都渋谷区代々木**********    S13031                20150529          D-20100325-C

```julia
df_gender_std = @chain df_customer begin
    @select(:gender_cd, :gender)
    unique()
end
head(df_gender_std, 3)
```

    size = (3, 2)
    [1m3×2 DataFrame[0m
    [1m Row [0m│[1m gender_cd [0m[1m gender  [0m
         │[90m String    [0m[90m String7 [0m
    ─────┼────────────────────
       1 │ 1          女性
       2 │ 9          不明
       3 │ 0          男性

---
> J-093: 商品データ（df_product）では各カテゴリのコード値だけを保有し、カテゴリ名は保有していない。カテゴリデータ（df_category）と組み合わせて非正規化し、カテゴリ名を保有した新たな商品データを作成せよ。

```julia
df_product_full = innerjoin(df_product, df_category[!, [:category_small_cd, :category_major_name, :category_medium_name, :category_small_name]], on=:category_small_cd);
head(df_product_full, 3)
```

    size = (10030, 9)
    [1m3×9 DataFrame[0m
    [1m Row [0m│[1m product_cd [0m[1m category_major_cd [0m[1m category_medium_cd [0m[1m category_small_cd [0m[1m unit_price [0m[1m unit_cost [0m[1m category_major_name [0m[1m category_medium_name [0m[1m category_small_name [0m
         │[90m String15   [0m[90m String            [0m[90m String             [0m[90m String            [0m[90m Int64?     [0m[90m Int64?    [0m[90m String              [0m[90m String               [0m[90m String              [0m
    ─────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
       1 │ P040101001  04                 0401                040101                    198        149  惣菜                 御飯類                弁当類
       2 │ P040101002  04                 0401                040101                    218        164  惣菜                 御飯類                弁当類
       3 │ P040101003  04                 0401                040101                    230        173  惣菜                 御飯類                弁当類

# ファイル入出力

---
> J-094: 093で作成したカテゴリ名付き商品データを以下の仕様でファイル出力せよ。
>
> |ファイル形式|ヘッダ有無|文字エンコーディング|
> |:--:|:--:|:--:|
> |CSV（カンマ区切り）|有り|UTF-8|
> 
> ファイル出力先のパスは以下のようにすること
> 
> |出力先|
> |:--:|
> |./data|

```julia
CSV.write("-094.csv", df_product_full)
```

    "-094.csv"

**これ以降の問題は重要なものではないのでパスする**

---
> J-095: 093で作成したカテゴリ名付き商品データを以下の仕様でファイル出力せよ。
>
> |ファイル形式|ヘッダ有無|文字エンコーディング|
> |:--:|:--:|:--:|
> |CSV（カンマ区切り）|有り|CP932|
> 
> ファイル出力先のパスは以下のようにすること。
> 
> |出力先|
> |:--:|
> |./data|

---
> J-096: 093で作成したカテゴリ名付き商品データを以下の仕様でファイル出力せよ。
>
> |ファイル形式|ヘッダ有無|文字エンコーディング|
> |:--:|:--:|:--:|
> |CSV（カンマ区切り）|無し|UTF-8|
> 
> ファイル出力先のパスは以下のようにすること。
> 
> |出力先|
> |:--:|
> |./data|

---
> J-097: 094で作成した以下形式のファイルを読み込み、データを3件を表示させて正しく取り込まれていることを確認せよ。
> 
> |ファイル形式|ヘッダ有無|文字エンコーディング|
> |:--:|:--:|:--:|
> |CSV（カンマ区切り）|有り|UTF-8|

---
> J-098: 096で作成した以下形式のファイルを読み込み、データを3件を表示させて正しく取り込まれていることを確認せよ。
> 
> |ファイル形式|ヘッダ有無|文字エンコーディング|
> |:--:|:--:|:--:|
> |CSV（カンマ区切り）|ヘッダ無し|UTF-8|

---
> J-099: 093で作成したカテゴリ名付き商品データを以下の仕様でファイル出力せよ。
>
> |ファイル形式|ヘッダ有無|文字エンコーディング|
> |:--:|:--:|:--:|
> |TSV（タブ区切り）|有り|UTF-8|
> 
> ファイル出力先のパスは以下のようにすること
> 
> |出力先|
> |:--:|
> |./data|

---
> J-100: 099で作成した以下形式のファイルを読み込み、データを3件を表示させて正しく取り込まれていることを確認せよ。
> 
> |ファイル形式|ヘッダ有無|文字エンコーディング|
> |:--:|:--:|:--:|
> |TSV（タブ区切り）|有り|UTF-8|

~~# これで１００本終わりです。おつかれさまでした！

```julia

```