search
LoginSignup
30

More than 1 year has passed since last update.

posted at

データサイエンス100本ノック解説(P001~020)

1. はじめに

データサイエンス協会から出ているデータサイエンス100本ノックの解説をします。

導入についてはこちらの記事を参考に進めてください(※ MacでDockerを扱います)

基本的には解答の解説ですが別解についても記述しています。

2. 解説編

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

P-001
# head()で先頭行を参照できる。()カッコ内に件数を入力する。空欄の場合は5行。
df_receipt.head(10)

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

P-002
# [[]](二重カッコ)となっているのはDataFrame型にするため
df_receipt[['sales_ymd', 'customer_id', 'product_cd', 'amount']].head(10)

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

P-003
# [[]]二重カッコ内に'sales_ymd', 'customer_id', 'product_cd', 'amount'を入れることでカラムを選択。
# カラム名を変更するにはrename(columns={変更前のカラム名: 変更後のカラム名})
# head(10)で先頭10行を選択
df_receipt[['sales_ymd', 'customer_id', 'product_cd', 'amount']].rename(columns={'sales_ymd': 'sales_date'}).head(10)

参考:pandas.DataFrameの行名・列名の変更

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

P-004
# 二重カッコ内に'sales_ymd', 'customer_id', 'product_cd', 'amount'を入れることでカラムを選択。
# query()メソッドでDataFrameの行を条件で抽出。
df_receipt[['sales_ymd', 'customer_id', 'product_cd', 'amount']].query('customer_id == "CS018205000001"')

# (別解)
df_receipt[df_receipt['customer_id'] == 'CS018205000001']

参考: https://note.nkmk.me/python-pandas-query/

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

P-005
# 前半部分は、P-004を参考。
# queryメソッドは複数条件の場合、"&"で記述する。
df_receipt[['sales_ymd', 'customer_id', 'product_cd', 'amount']].query('customer_id == "CS018205000001" & amount >= 1000')

参考: https://note.nkmk.me/python-pandas-query/

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

P-006
# 前半部分は、P-004を参考。
# queryメソッドは複数条件の場合、"&"(かつ) "|"(もしくは)で記述
df_receipt[['sales_ymd', 'customer_id', 'product_cd', 'quantity', 'amount']] \
    .query('customer_id=="CS018205000001" & (amount>=1000 | quantity>=5)')

参考: https://note.nkmk.me/python-pandas-query/

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

P-007
# P-006を参考
# 売上金額(amount)が1,000以上2,000以下は、1000<=amount<=2000と記述する。
df_receipt[['sales_ymd', 'customer_id', 'product_cd', 'amount']] \
    .query('customer_id=="CS018205000001" & 1000<=amount<=2000')

参考: https://note.nkmk.me/python-pandas-query/

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

P-008
# P-006を参考
# 以外の表記は、"!=" で表記する。
df_receipt[['sales_ymd', 'customer_id', 'product_cd', 'amount']] \
    .query('customer_id=="CS018205000001" & product_cd!="P071401019"')

参考(queryメソッド): https://note.nkmk.me/python-pandas-query/
参考(比較演算): https://www.javadrive.jp/python/if/index4.html

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

P-009
# query()メソッドでDataFrameの行を条件で抽出。
df_store.query('prefecture_cd != "13" & floor_area <= 900')

分かりやすく図にすると以下のようになる。
スクリーンショット 2020-08-19 17.11.09.png

P-010: 店舗データフレーム(df_store)から、店舗コード(store_cd)が"S14"で始まるものだけ全項目抽出し、10件だけ表示せよ。

P-010
# query()メソッドでDataFrameの行を条件で抽出。
# 前方一致はstartswithを用いる。文字列メソッドはengine='python'でないとquery()メソッドで使えない
df_store.query("store_cd.str.startswith('S14')", engine='python').head(10)

# (別解)
df_store[df_store['store_cd'].str.startswith('S14')].head(10)

参考(startswith): https://note.nkmk.me/python-str-compare/

P-011: 顧客データフレーム(df_customer)から顧客ID(customer_id)の末尾が1のものだけ全項目抽出し、10件だけ表示せよ。

P-011
# query()メソッドでDataFrameの行を条件で抽出。
# 後方一致はendswithを用いる。文字列メソッドはengine='python'でないとquery()メソッドで使えない
df_customer.query("customer_id.str.endswith('1')", engine='python').head(10)

参考(startswith): https://note.nkmk.me/python-str-compare/

P-012: 店舗データフレーム(df_store)から横浜市の店舗だけ全項目表示せよ。

P-012
# query()メソッドでDataFrameの行を条件で抽出。
# str.contains(): 特定の文字列を含む。
# 文字列メソッドはengine='python'でないとquery()メソッドで使えない
df_store.query("address.str.contains('横浜市')", engine='python')

参考(startswith): https://note.nkmk.me/python-str-compare/

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

P-013
# query()メソッドでDataFrameの行を条件で抽出。
# str.contains(): 特定の文字列を含む。
# ^(先頭から)、[A-F](A-Fまで)、regex=True(正規表現の場合に必要 ※現在は必要ない)
df_customer.query("status_cd.str.contains('^[A-F]', regex=True)", engine='python').head(10)

参考: https://qiita.com/luohao0404/items/7135b2b96f9b0b196bf3

P-014: 顧客データフレーム(df_customer)から、ステータスコード(status_cd)の末尾が数字の1〜9で終わるデータを全項目抽出し、10件だけ表示せよ。

P-014
# df_customer.query("status_cd.str.containsはP-013を参考
# [0-9](0-9まで)、$(文字列の末尾)
df_customer.query("status_cd.str.contains('[0-9]$', regex=True)", engine='python').head(10) 

参考: https://qiita.com/luohao0404/items/7135b2b96f9b0b196bf3

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

P-015
# df_customer.query("status_cd.str.containsはP-013を参考
# ^(先頭から)、[A-F](AからFまで)、 .(任意の1文字)、*(0回以上の繰り返し)、[0-9](0-9まで)、$(文字列の末尾)
df_customer.query("status_cd.str.contains('^[A-F].*[1-9]$', regex=True)", engine='python').head(10)

# (別解)
df_customer.query("(status_cd.str.contains('^[A-F]')) & \
    (status_cd.str.contains('[0-9]$'))", engine='python').head(10)

『 .* 』は、”任意の1文字を繰り返す”つまりはどんな文字が何個あっても良い。
参考: https://qiita.com/luohao0404/items/7135b2b96f9b0b196bf3

P-016: 店舗データフレーム(df_store)から、電話番号(tel_no)が3桁-3桁-4桁のデータを全項目表示せよ。

P-016
# # df_customer.query("tel_no.str.containsはP-013を参考
# ^(先頭から)、[0-9](0から9)、{3}(3回の繰り返し)
df_store.query("tel_no.str.contains('^[0-9]{3}-[0-9]{3}-[0-9]{4}', regex=True)", engine='python')

参考: https://qiita.com/luohao0404/items/7135b2b96f9b0b196bf3

P-17: 顧客データフレーム(df_customer)を生年月日(birth_day)で高齢順にソートし、先頭10件を全項目表示せよ。

P-017
# 要素でソートするsort_values()
# ソートしたい列のラベル(列名)を第一引数(by)に指定する。
# ascending=True: 昇順(高齢順→生誕日が早い(古い))
df_customer.sort_values('birth_day', ascending=True).head(10)

参考: https://note.nkmk.me/python-pandas-sort-values-sort-index/

P-18: 顧客データフレーム(df_customer)を生年月日(birth_day)で若い順にソートし、先頭10件を全項目表示せよ。

P-018
# P-017を参考。
# 若い順なのでascending=False
df_customer.sort_values('birth_day', ascending=False).head(10)

参考: https://note.nkmk.me/python-pandas-sort-values-sort-index/

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

P-019
# 'customer_id', 'amount'のデータフレームと'amount'をランク付けしたデータフレームを横方向に結合。
# rank()メソッドでランクをつける。method='min'で重複値の最小値の値。
df_tmp = pd.concat([df_receipt[['customer_id', 'amount']], df_receipt['amount'] \
                    .rank(method='min', ascending=False)], axis=1)

# カラム名が重複しているのでカラム名を再定義する。
df_tmp.columns = ['customer_id', 'amount', 'ranking']

# rankingカラムでソートする。
df_tmp.sort_values('ranking').head(10)

method='min'で1位、2位、3位、3位、5位といった最小値に合わせた値(テストやスポーツの順位)
参考: https://note.nkmk.me/python-pandas-rank/

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

P-020
# P-019を参考
# method='first'で同一値(重複値)は登場順に順位付けされる。
df_tmp = pd.concat([df_receipt[['customer_id', 'amount']], 
                    df_receipt['amount'].rank(method='first', ascending=False)], axis=1)
df_tmp.columns = ['customer_id', 'amount', 'ranking']
df_tmp.sort_values('ranking', ascending=True).head(10)

参考: https://note.nkmk.me/python-pandas-rank/

3. 参考文献

データサイエンス100本ノック
Macでデータサイエンス100本ノックを動かす方法

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
30