The-Japan-DataScientist-SocietyのPython編を解き終わったので、回答する際に閲覧したサイトを忘備録として、まとめておきます。
▼ Contents
1. データサイエンス100本ノック概要
以下、The-Japan-DataScientist-Societyから引用です。
- データサイエンス100本ノックは構造化データ加工編である
- 演習問題はSQL、Python、Rで共通
- 言語によっては向かない設問もあるが、「この言語のときはこう書けば実現できる」という技術習得を目指すことを優先
- 個人情報のように見える項目は全てダミーデータを利用
- 大学、企業など組織でのご利用にあたっては、「データサイエンティスト協会スキル定義委員」の「データサイエンス100本ノック(構造化データ加工編)」を利用していることを明示いただければ自由に利用してOK
- データサイエンス100本ノック(構造化データ加工編)の利用に関するご質問等について、個別での対応は受けかねますので予めご了承ください
演習問題を行うには、GitとDockerの準備が必要です。
以下では、引用したサイトと100本ノック内で関連のある問題に対し、私の回答を使用例として記載しています。
2. データフレーム関連
2-1. Query関数
最初はpandasでのスライスで対応していたが、途中から複数条件などを記載するのが楽だと気づき、Query関数を多用するようになりました。
pandas.DataFrameの行を条件で抽出するquery
pandas.DataFrame
の列の値に対する条件に応じて行を抽出するにはquery()
メソッドを使う。比較演算子や文字列メソッドを使った条件指定、複数条件の組み合わせなどをかなり簡潔に記述できて便利。
""" P-005: """
""" スライス """
df_receipt[["sales_ymd", "customer_id", "product_cd", "amount"]][(df_receipt['customer_id']=="CS018205000001")&(df_receipt['amount']>=1000)]
""" クエリ """
df_receipt[["sales_ymd", "customer_id", "product_cd", "amount"]].query("customer_id == 'CS018205000001' & amount >= 1000")
2-2. ソート
pandas.DataFrameやpandas.Seriesの値をソートするのに必要な関数。
100本ノックではよく使います。
pandas.DataFrame, Seriesをソートするsort_values, sort_index
pandas.DataFrame
,pandas.Series
をソート(並び替え)するには、sort_values()
,sort_index()
メソッドを使う。昇順・降順を切り替えたり、複数列を基準にソートしたりできる。
""" P-17: """
df_customer.sort_values('birth_day', ascending=True).head(10)
""" P-18: """
df_customer.sort_values('birth_day', ascending=False).head(10)
2-3. ランク
ある値に対し、ランク付けしたいときに使用する関数。引数の設定方法でランク付け方法の変更が可能です。
pandas.DataFrame, Seriesを順位付けするrank
pandas.DataFrame
の行・列,panda.Series
を順位付けするにはrank()
メソッドを使う。
""" P-19: """
df0, df1 = df_receipt[['customer_id', 'amount']], df_receipt['amount'].rank(method='min', ascending=False)
df = pd.concat([df0, df1], axis=1)
df.columns = ['customer_id', 'amount', 'ranking']
df.sort_values('ranking', ascending=True).head(10)
2-4. グルーピング
pandasでのグルーピングでとてもお世話になる関数です。使いこなせれば、データ加工はかなりスムーズになりました。
pandas.DataFrameをGroupByでグルーピングし統計量を算出
pandas.DataFrame
,pandas.Series
のgroupby()
メソッドでデータをグルーピング(グループ分け)できる。グループごとにデータを集約して、それぞれの平均、最小値、最大値、合計などの統計量を算出したり、任意の関数で処理したりすることが可能。
""" P-023: """
df_receipt.groupby(df_receipt['store_cd']).agg({'amount':'sum', 'quantity':'sum'}).reset_index().head(10)
2-5. 重複行の抽出・削除
指定したコラムで重複する値がある行を抽出・削除するときに使用する関数。
使いこなせるとデータ処理が楽になりそう。
pandas.DataFrame, Seriesの重複した行を抽出・削除
pandas.DataFrame
,pandas.Series
から重複した要素を含む行を検出・抽出するにはduplicated()
、削除するにはdrop_duplicates()
を使う。
""" P-039: """
df_sum = df_receipt.groupby('customer_id').agg({'amount':'sum'}).reset_index().query('not customer_id.str.startswith("Z")', engine='python')
df_sum = df_sum.sort_values('amount', ascending=False).head(20)
df_date = df_receipt[~df_receipt.duplicated(subset=['customer_id', 'sales_ymd'])]
df_date = df_date.groupby('customer_id').agg({'sales_ymd':'count'}).reset_index().query('not customer_id.str.startswith("Z")', engine='python')
df_date = df_date.sort_values('sales_ymd', ascending=False).head(20)
pd.merge(df_sum, df_date, how='outer', on='customer_id')
""" P-070: """
df_date = pd.merge(df_receipt[['customer_id', 'sales_ymd']], df_customer[['customer_id', 'application_date']], how='inner', on='customer_id')
df_date = df_date.drop_duplicates()
df_date['sales_ymd'] = pd.to_datetime(df_date['sales_ymd'].astype(str))
df_date['application_date'] = pd.to_datetime(df_date['application_date'])
df_date['elasped_days'] = df_date['sales_ymd'] - df_date['application_date']
df_date.head(10)
2-6. Shift関数
知ることができてよかった思う関数の一つ。使いこなせれば、いろいろな表計算が自動でできると期待してます。
pandasでデータを行・列(縦・横)方向にずらすshift
pandas.DataFrame
,pandas.Series
のデータを行または列方向にずらすにはshift()
を使う。
ついでにこちらも覚えておくと便利でした。
pandasで行・列の差分・変化率を取得するdiff, pct_change
pandas.DataFrame
,pandas.Series
の行または列の差分・変化率を取得するにはdiff()
,pct_change()
メソッドを使う。例えば一行前のデータとの差分・変化率を取得したりできる。
""" P-042: """
df_date_amount = df_receipt.groupby('sales_ymd').agg({'amount':'sum'}).reset_index()
for i in range(1,4):
if i == 1:
df_date_lag = pd.concat([df_date_amount, df_date_amount.shift(i)], axis=1)
else:
df_date_lag = pd.concat([df_date_lag, df_date_amount.shift(i)], axis=1)
df_date_lag.columns = ['sales_ymd_1', 'amount_1', 'sales_ymd_2', 'amount_2','sales_ymd_3', 'amount_3','sales_ymd_4', 'amount_4']
df_date_lag.head(10)
2-7. ピボットテーブル
エクセルでよく使うあれです。
pandas.pivot_table()
関数を使うと、Excelなどの表計算ソフトのピボットテーブル機能と同様の処理が実現できる。カテゴリデータ(カテゴリカルデータ、質的データ)のカテゴリごとにグルーピング(グループ分け)して量的データの統計量(平均、合計、最大、最小、標準偏差など)を確認・分析することが可能。便利。
pandasのピボットテーブルでカテゴリ毎の統計量などを算出
""" P-043: """
df_sales_summary = pd.merge(df_receipt, df_customer, how='inner', on='customer_id')
df_sales_summary['era'] = df_sales_summary['age'].apply(lambda x: int(x / 10) *10)
df_sales_summary_piv = pd.pivot_table(df_sales_summary, index='era', columns='gender_cd', values='amount', aggfunc=sum).reset_index()
df_sales_summary_piv.columns = ['era', 'male', 'female', 'unknown']
df_sales_summary_piv
2-8. Stack関数
データテーブルを並び替えたいときに使用する。
列方向に並んだデータを行方向に並べ替えたり、行方向に並んだデータを列方向に並べ替えたりして、データの構造を再形成できる。
pandasでstack, unstack, pivotを使ってデータを整形
""" P-043, 044: """
df_sales_summary = pd.merge(df_receipt, df_customer, how='inner', on='customer_id')
df_sales_summary['era'] = df_sales_summary['age'].apply(lambda x: int(x / 10) *10)
df_sales_summary_piv = pd.pivot_table(df_sales_summary, index='era', \
columns='gender_cd', values='amount', aggfunc=sum).reset_index()
df_sales_summary_piv.columns = ['era', 'male', 'female', 'unknown']
df_sales_summary_piv.set_index('era').stack().reset_index() \
.replace({'male': '00', 'female': '01', 'unknown': '99'}) \
.rename(columns={'level=1': 'gender_cd', 0: 'sum_amount'})
2-9. データフレーム結合
まずはmerge関数の使用法を抑えておけば問題ないと思います。
日付や名前などの共通のデータ列を持っている複数の
pandas.DataFrame
をその列の値に従って結合するにはpandas.merge()
関数またはpandas.DataFrame
のmerge()
メソッドを使う。インデックス列を基準にする場合は
pandas.DataFrameを結合するmerge, join(列・インデックス基準)pandas.merge()
関数も使えるし、pandas.DataFrame
のjoin()
メソッドも使える。
""" P-036 """
pd.merge(df_receipt, df_store[['store_cd', 'store_name']], how='inner', on='store_cd').head(10)
2-10. データ読み書き(csv, tsvファイル)
csvの他にtsv処理もあるので、記載しておきます。
pandasでcsvファイルの書き出し・追記(to_csv)
panda.DataFrame
またはpandas.Series
のデータをcsvファイルとして書き出したり既存のcsvファイルに追記したりしたい場合は、to_csv()
メソッドを使う。区切り文字を変更できるので、tsvファイル(タブ区切り)として保存することも可能。
csvファイル、tsvファイルを
pandasでcsv/tsvファイル読み込み(read_csv, read_table)pandas.DataFrame
として読み込むには、pandasの関数read_csv()
かread_table()
を使う。
""" P-094: """
df_tmp.to_csv('./data/df_category.csv', encoding='utf-8', index=False)
""" P-097: """
df_category_1 = pd.read_csv('./data/df_category.csv', encoding='utf-8')
df_category_1.head(10)
""" P-099: """
df_tmp.to_csv('./data/df_category_4.tsv', sep='\t', encoding='utf-8', header=False)
""" P-100: """
pd.read_table('./data/df_category_4.tsv', encoding='utf-8')
2-11. Replace関数
DataFrameの要素を変更したいときに使用する。
Frame全体でもコラムでも指定可能。辞書使用で複数指定も可能です。
pandas.DataFrame
,pandas.Series
の要素の値を置換するには、replace()
メソッドを使う。複数の異なる要素を一括で置き換えたり正規表現を使ったりすることもできる。
pandas.DataFrame, Seriesの要素の値を置換するreplace
""" P-44: """
df_sales_summary_piv.set_index('era').stack().reset_index() \
.replace({'male': '00', 'female': '01', 'unknown': '99'}) \
.rename(columns={'level=1': 'gender_cd', 0: 'sum_amount'})
2-12. 行列名の変更
よく使うけど、よく忘れる関数でした。何回も使用して覚えておいたほうが良いと思います。
pandas.DataFrameの行名・列名の変更
pandas.DataFrame
の行名(インデックス,index
)・列名(カラム名,columns
)を変更するには以下についてサンプルコードとともに説明する。任意の行名・列名を変更:rename()
""" P-048: """
df_receipt_unix = pd.to_datetime(df_receipt['sales_epoch'], unit='s')
pd.concat([df_receipt_unix, df_receipt[['receipt_no', 'receipt_sub_no']]], axis=1)\
.rename(columns={'sales_epoch': 'sales_ymd'}).head(10)
3. 欠損値処理関連
3-1. 欠損値要素の処理
データ解析には必須関数!
欠損値を除外(削除)するには
pandasで欠損値NaNを除外(削除)・置換(穴埋め)・抽出dropna()
メソッド、欠損値を他の値に置換(穴埋め)するにはfillna()
メソッドを使う。
""" P-080: """
df_product_1 = df_product.dropna(how='any') #デフォルトany
len(df_product), len(df_product_1)
""" P-081: """
price_mean = round(df_product['unit_price'].mean())
cost_mean = round(df_product['unit_cost'].mean())
df_product_2 = df_product.fillna({'unit_price': price_mean, 'unit_cost': cost_mean})
3-2. 欠損値を含む計算
データには欠損値があることも多く、欠損値を無視して計算することもあります。
その際に使用する関数です。
NumPyの配列
NumPyで欠損値np.nanを含む配列ndarrayの合計や平均を算出ndarray
にひとつでも欠損値np.nan
が含まれている場合、通常の関数sum()
を使うと、np.nan
が返される。nansum()
を用いることで、欠損値np.nan
を除外した値の合計が算出できる。
3-3. 欠損値個数
欠損値の有無および欠損値の個数をカウントすることはデータ前処理で必須ですね。
pandasで欠損値NaNが含まれているか判定、個数をカウント
pandas.DataFrame
,pandas.Series
に欠損値NaN
が含まれているどうかを判定する方法、および、欠損値NaN
の個数をカウントする方法を説明する。
""" P-079: """
df_product.isnull().sum()
4. 時間型関連
4-1. 時間型変更
pandasで時間型を扱う際には必須の関数です。
pandas.DataFrame
の日時(日付・時間)を表した列を操作する方法を説明する。文字列とdatetime64[ns]
型との相互変換、年月日、時刻を数値として抽出する方法など。
pandasで日付・時間の列を処理(文字列変換、年月日抽出など)
pandas.DataFrame
の(日時日付・時間)を表した列を操作する方法を説明する。文字列とdatetime64[ns]
型との相互変換、年月日、時刻を数値として抽出する方法など。
某計測ツールから落としてきたCSVファイルを pandas で読み込んで、日時情報がUNIX時間だったので、pandas で datetime に変換した備忘録。
pandasでunix時間をdatetimeに変換する
""" P-050: """
df_receipt_unix = pd.to_datetime(df_receipt['sales_epoch'], unit='s').dt.strftime('%m') #.month
pd.concat([df_receipt_unix, df_receipt[['receipt_no', 'receipt_sub_no']]], axis=1).rename(columns={'sales_epoch': 'sales_m'}).head(10)
4-2. 日付加算減算
この関数を覚えておくと、日付計算が楽になりました。
標準ライブラリではありませんが、日付処理のデファクトスタンダードともいえる python-dateutil モジュールを使用することで月の加算ができる relativedeltaが使えます。
日付に月単位で加算減算する (relativedelta)
""" P-074: """
df_date = df_receipt[['customer_id', 'sales_ymd']]
df_date = df_date.drop_duplicates()
df_date['sales_ymd'] = pd.to_datetime(df_date['sales_ymd'].astype(str), format='%Y%m%d')
df_date['monday'] = df_date['sales_ymd'].apply(lambda x: x - relativedelta(days=x.weekday()))
df_date['elasped_days'] = df_date['sales_ymd'] - df_date['monday']
df_date.head(10)
5. データ分析関連
5-1. ユニーク要素処理
ユニークな要素に対する処理を行いときに使用します。新たなコラムや、除外したい行などにも利用できます。
pandas.DataFrame
の列、pandas.Series
において、ユニークな要素の個数(重複を除いた件数)、及び、それぞれの要素の頻度(出現回数)を取得する方法を説明する。pandasでユニークな要素の個数、頻度(出現回数)をカウント
pandas.Series
のメソッドunique()
,value_counts()
,nunique()
を使う。nunique()
はpandas.DataFrame
のメソッドとしても用意されている。
""" P-053: """
df_post = df_customer[['customer_id', 'postal_cd']].copy()
df_post['postal_flag'] = df_post['postal_cd'].apply(lambda x: 1 if int(x[:3]) <= 209 else 0)
pd.merge(df_receipt, df_post, how='inner', on='customer_id').groupby('postal_flag').agg({'customer_id': 'nunique'})
5-2. apply, map関数
データ処理では、各要素に関数や、分類処理などを行うことが多いです。map関数やapply関数は、使用方法を覚えておくととても役立ちます。
pandasのオブジェクト(
pandasで要素、行、列に関数を適用するmap, applymap, applypandas.DataFrame
,pandas.Series
)に関数を適用する場合、どんな関数を適用するか、要素・行・列のいずれに適用するかによって、使うメソッドなどが異なる。
""" P-055: """
df_amount_sales = df_receipt[['customer_id', 'amount']].groupby('customer_id').agg({'amount': 'sum'}).reset_index()
df_quan_25 = np.quantile(df_amount_sales['amount'], 0.25)
df_quan_50 = np.quantile(df_amount_sales['amount'], 0.5)
df_quan_75 = np.quantile(df_amount_sales['amount'], 0.75)
def calc_quan(x):
if x <= df_quan_25:
return 1
elif df_quan_25 < x <= df_quan_50:
return 2
elif df_quan_50 < x <= df_quan_75:
return 3
elif x > df_quan_75:
return 4
df_amount_sales['amount_cat'] = df_amount_sales['amount'].apply(lambda x: calc_quan(x))
df_amount_sales.head(10)
5-3. パーセンタイル
統計学で四分位数を使用する場合は、よく使用します。
pandasで分位数・パーセンタイルを取得するquantile
pandas.DataFrame
,pandas.Series
の分位数・パーセンタイルを取得するにはquantile()
メソッドを使う。
""" P-055: """
df_amount_sales = df_receipt[['customer_id', 'amount']].groupby('customer_id').agg({'amount': 'sum'}).reset_index()
df_quan_25 = np.quantile(df_amount_sales['amount'], 0.25)
df_quan_50 = np.quantile(df_amount_sales['amount'], 0.5)
df_quan_75 = np.quantile(df_amount_sales['amount'], 0.75)
def calc_quan(x):
if x <= df_quan_25:
return 1
elif df_quan_25 < x <= df_quan_50:
return 2
elif df_quan_50 < x <= df_quan_75:
return 3
elif x > df_quan_75:
return 4
df_amount_sales['amount_cat'] = df_amount_sales['amount'].apply(lambda x: calc_quan(x))
df_amount_sales.head(10)
5-4. ダミー変数
機械学習などでダミー変数を用意するときは必須の関数ですね。
pandasでカテゴリ変数(カテゴリカルデータ、質的データ)をダミー変数に変換するには、
pandas.get_dummies()
関数を使う。文字列でカテゴリー分けされた性別などのデータを、男を
pandasでカテゴリ変数をダミー変数に変換(get_dummies)0
, 女を1
のように変換したり、多クラスの特徴量をone-hot表現に変換したりすることができる。機械学習の前処理として行うことが多い。
""" P-058: """
pd.get_dummies(df_customer[['gender_cd', 'customer_id']], columns=['gender_cd'], prefix_sep='_').head(10)
5-5. 正規化・標準化
こちらも機械学習の前処理などで行う正規化・標準化です。
Pythonのリスト(
list
型)、NumPy配列(numpy.ndarray
)、および、pandas.DataFrame
を正規化・標準化する方法について説明する。Python標準ライブラリやNumPy、pandasのメソッドを利用して最大値や最大値、平均、標準偏差を求めて処理することも可能だが、SciPyやscikit-learnでは正規化・標準化のための専用の関数やクラスが用意されている。
Pythonで正規化・標準化(リスト、NumPy配列、pandas.DataFrame)
""" P-059: """
df_amount = df_receipt.query('not customer_id.str.startswith("Z")', engine='python').groupby('customer_id').agg({'amount': 'sum'}).reset_index()
df_amount['amount_scale'] = preprocessing.scale(df_amount['amount'])
df_amount.head(10)
5-6. 指数・対数計算
データ処理で多用する指数・対数の関数です。
Pythonの数学関数の標準モジュール
Pythonで指数関数・対数関数を計算(exp, log, log10, log2)math
を使うと、指数関数および対数関数(自然対数、常用対数、二進対数)の計算ができる。
""" P-061:"""
df_sales_amount = df_receipt[['amount', 'customer_id']].query('not customer_id.str.startswith("Z")', engine='python').groupby('customer_id').agg({'amount': 'sum'}).reset_index()
df_sales_amount['amount_log10'] = df_sales_amount['amount'].apply(lambda x: math.log10(x+1))
df_sales_amount.head(10)
""" P-062: """
df_sales_amount = df_receipt.query('not customer_id.str.startswith("Z")', engine='python').groupby('customer_id').agg({'amount': 'sum'}).reset_index()
df_sales_amount['amount_loge'] = df_sales_amount['amount'].apply(lambda x: math.log(x+1))
df_sales_amount.head(10)
5-7. 小数点処理
処理方法で選択する関数が異なる点に注意します。
Pythonで浮動小数点数
Pythonで小数点以下を切り捨て・切り上げ: math.floor(), math.ceil()float
の小数点以下を切り捨て・切り上げするには、標準ライブラリmathモジュールのfloor()
,ceil()
を使う。
Pythonで数値(浮動小数点
Pythonで小数・整数を四捨五入するroundとDecimal.quantizefloat
型または整数int
型)を四捨五入や偶数への丸めで丸める方法について、以下の内容を説明する。
""" P-065 """
df_new_product = df_product[['product_cd', 'unit_price', 'unit_cost']].copy()
df_new_product['new_price'] = df_new_product['unit_cost'].apply(lambda x: np.floor(x/0.7))
df_new_product['new_ratio'] = (df_new_product['new_price'] - df_new_product['unit_cost'])/df_profit['unit_price']
df_new_product.head(10)
""" P-066 """
df_new_product = df_product[['product_cd', 'unit_price', 'unit_cost']].copy()
df_new_product['new_price'] = df_new_product['unit_cost'].apply(lambda x: np.round(x/0.7))
df_new_product['new_ratio'] = (df_new_product['new_price'] - df_new_product['unit_cost'])/df_profit['unit_price']
df_new_product.head(10)
""" P-067 """
df_new_product = df_product[['product_cd', 'unit_price', 'unit_cost']].copy()
df_new_product['new_price'] = df_new_product['unit_cost'].apply(lambda x: np.ceil(x/0.7))
df_new_product['new_ratio'] = (df_new_product['new_price'] - df_new_product['unit_cost'])/df_profit['unit_price']
df_new_product.head(10)
5-8. ランダムサンプリング
統計学などで母集団から標本を取るときなどに使えそうです。
行数の多い
pandas.DataFrame
,pandas.Series
のデータを確認するときに、行または列をランダムに抽出(ランダムサンプリング)するメソッドsample()
が便利。なお、大きいサイズの
pandasの行・列をランダムサンプリング(抽出)するsamplepandas.DataFrame
,pandas.Series
のデータを確認するときに使えるほかのメソッドとして、先頭・末尾の行を返すhead()
とtail()
もある。
""" P-075: """
df_customer.sample(frac=0.01).groupby("gender_cd").agg({"customer_id":"count"})
5-9. 外れ値除去
外れ値を確認して、除去する手法はデータ分析で必須ですね。
データ解析を行う場合、外れ値を含んでいると相関係数などを求めたときにおかしな値が得られてしまうことがあります。今回はこの外れ値を含む行を取り除く関数を、pandasのdataframeを使用して紹介します。
pandasのデータフレームから外れ値を含む行を取り除く
""" P-077: """
df_tmp = df_receipt[~df_receipt.customer_id.str.startswith("Z")].groupby("customer_id").amount.sum()
mean = df_tmp.mean()
std = df_tmp.std(ddof=0)
df_tmp[abs(mean-tmp) >= 3*std].reset_index().head(10)
5-10. 正規表現
正規表現はPython公式サイトを閲覧できれば良いと思います。
とりあえず覚えるよりも、コードを書き続けるほうが良さそう。
このモジュールは Perl に見られる正規表現マッチング操作と同様のものを提供します。
正規表現操作
""" P-013: """
df_customer.query("status_cd.str.contains('^[A-F]')", engine='python').head(10)
""" P-014: """
df_customer.query("status_cd.str.contains('[1-9]$')", engine='python').head(10)
""" P-015: """
df_store.query("tel_no.str.contains('^[0-9]{3}-[0-9]{3}-[0-9]{4}$')", engine='python')
以上です。お疲れさまでした!
合わせてSQL編もどうぞ御覧ください。
[…] 【Python, Docker, Github】初心者向けのTIP集~データサイエンス100本ノック~ […]