匿アナ流ID-POS分析⓪(データの準備)
本格的に分析の話に移る前に、語句の定義と、使っていくデータの準備をしていきます。
■語句定義
ID-POS分析でしか使わない、聞きなれない言葉がいろいろと出てくると思うので、ざっくり定義のようなものを記しておきます。知ってるよ!という方は読み飛ばしていただいてよいと思います。
☑ 購入人数
客数・ユニーク人数・人数のような表記をされている場合もあります。某ベンダーさんのサービスではトライアル人数という表記もされていました。どの場合でも以下の決めごとに従っていると思います。
定義:期間内のユニーク(一意)な顧客ID数
補足:例えば、期間内で同じ顧客IDが3回出現したとしたら、その購入人数は1人としてカウントしましょうという考え方です
☑ 延べ人数
レシート枚数のような表記をされている場合もあります。
定義:期間内のレジ通過回数の合計
補足:例えば、期間内で同じ顧客IDが3回出現したとしたら、その延べ人数は3人としてカウントしましょうといった考え方です
☑ 購入頻度
言葉の通りですが、先の説明と合わせると期間内の一人当たりのレジ通過回数のような意味合いです。
定義:延べ人数 ÷ 購入人数
☑ 客単価
期間内客単価、平均客単価といった表記をされている場合もあります。
定義:期間内の支出金額 ÷ 購入人数
☑ リピート率(※要注意)
小売り企業様やID-POSベンダー様によって大きく定義が異なる指標です。
リピート購入の割合のことを指しているのか、それともリピーター(複数回購入者)の割合のことを指しているのかで意味合いが変わります。こちらのnoteでは前者をリピート率、後者をリピーター率として分けて定義します。分析の目的や求められるアウトプットによって使い分けてください。
▶ リピート率
定義:(延べ人数 - 購入人数)÷ 延べ人数
補足:すべての購入回数(延べ人数)における複数回購買(延べ人数 - 購入人数)の割合
▶ リピーター率
定義:2回以上購入人数 ÷ 購入人数
■ それぞれの目的
「リピート率」を使う目的:集計対象の複数回購入(3回、4回、…n回)の影響を考慮した継続購入の実力値を測る
「リピーター率」を使う目的:集計対象が抱えているリピーターの割合を測る
☑ 買上率
集計対象の商品・カテゴリーがストアや企業様のなかでどれくらいの購入人数を抱えているかを表す指標です。
定義:集計対象の購入人数 ÷ 全体の購入人数
☑ 購入率
一般的に定義されているわけではありませんが、買上率と非常に概念が近い指標なので、言葉が入り混じることを避けるため私はいつも分けて使用しています。
買上率はストアや企業様の全体の購入人数を分母に算出しますが、購入率は何かしらの条件抽出をした場合の購入人数を分母に算出します。
例えば、女性という条件でデータを抽出した際に、女性の中での購入人数の割合を算出する場合、購入率という言葉を使用しています。
買上率と購入率の概念を利用したリフト値という指標もありますが、また別の機会に紹介します
定義:特定条件下における集計対象の購入人数 ÷ 特定条件下における購入人数
補足:
購入人数(全体):100人
ティッシュペーパーの購入人数(全体):60人
ティッシュペーパーの買上率:60人 ÷ 100人 = 60%
購入人数(女性):80人
ティッシュペーパーの購入人数(女性):40人
ティッシュペーパーの購入率:40人 ÷ 80人 = 50%
■データセットの準備
本物のID-POSデータは使えないので、ダミーのデータを用意します。
分析は全てこちらのデータをもとに進めていきます。
中身は理解しなくて大丈夫です。
ご自身の環境にコピペしてご使用ください
☑ データセットの生成用コード
import pandas as pd
import random
import datetime
# ランダムシードを設定
random.seed(12345) # 任意の整数を指定
# データフレームの初期化
data = {
'日付': [],
'店舗コード': [],
'店舗名': [],
'顧客ID': [],
'性別': [],
'年齢': [],
'大分類': [],
'中分類': [],
'小分類': [],
'JANコード': [],
'商品名': [],
'数量': [],
'金額': [],
'単価': []
}
# 顧客IDを生成
customer_ids = [str(random.randint(1000000000, 9999999999)) for _ in range(500)]
# 性別「その他」のIDを10個に設定
special_customer_ids = random.sample(customer_ids, 10)
special_customer_gender = 'その他'
# 顧客IDの出現頻度を設定
customer_id_frequencies = [random.randint(1, 10) for _ in range(500)]
# 性別と年齢を顧客IDごとに割り当てる
customer_info = {}
for customer_id in customer_ids:
if customer_id in special_customer_ids:
gender = special_customer_gender
else:
gender = random.choice(['男性', '女性'])
age = random.randint(10, 79)
customer_info[customer_id] = {
'性別': gender,
'年齢': age
}
# 店舗名を生成
stores = ['大森店', '蒲田店', '川崎店']
store_choices = random.choices(stores, k=len(customer_ids))
# 店舗コードを設定
store_codes = {'大森店': '1001', '蒲田店': '1002', '川崎店': '1003'}
# 大分類を生成
categories = ['日用品', '食品']
category_choices = random.choices(categories, k=len(customer_ids), weights=[0.6, 0.4])
# 中分類を生成
def generate_middle_category(category):
if category == '日用品':
return random.choice(['日用雑貨', '化粧品'])
elif category == '食品':
return random.choice(['加工食品', '菓子類'])
middle_category_choices = [generate_middle_category(category) for category in category_choices]
# 小分類を生成
def generate_small_category(category, middle_category):
if category == '日用品':
if middle_category == '日用雑貨':
return random.choice(['ティッシュペーパー', 'トイレットペーパー', 'ベビーおむつ', '介護おむつ', '衣料用洗剤', '住居用洗剤'])
elif middle_category == '化粧品':
return random.choice(['基礎化粧品', 'メイク用品', 'ヘアケア', 'ボディケア'])
elif category == '食品':
if middle_category == '加工食品':
return random.choice(['調味料', '冷凍食品', '乳製品'])
elif middle_category == '菓子類':
return random.choice(['チョコレート', 'スナック', 'アイスクリーム'])
small_category_choices = [generate_small_category(category, middle) for category, middle in zip(category_choices, middle_category_choices)]
# 商品名を生成
def generate_product_name(small_category):
return f"{small_category}_{random.choice(['A', 'B', 'C', 'D', 'E'])}"
product_name_choices = [generate_product_name(small) for small in small_category_choices]
# JANコードを生成 (int型)
jan_codes = [random.randint(10000000000000, 99999999999999) for _ in range(500)]
# 数量を生成
quantities = [random.randint(1, 3) for _ in range(len(customer_ids))]
# 日付を生成 (2021-01-01から2022-12-31)
start_date = datetime.date(2021, 1, 1)
end_date = datetime.date(2022, 12, 31)
total_records = 10000
records_per_month = total_records // 24 # 2年間で均等に分配
current_date = start_date
while current_date <= end_date:
for _ in range(records_per_month):
data['日付'].append(current_date.strftime('%Y-%m-%d'))
customer_id = random.choices(customer_ids, customer_id_frequencies)[0]
data['顧客ID'].append(customer_id)
data['性別'].append(customer_info[customer_id]['性別'])
data['年齢'].append(customer_info[customer_id]['年齢'])
store_name = random.choice(store_choices)
data['店舗名'].append(store_name)
data['店舗コード'].append(store_codes[store_name]) # 店舗コードを設定
data['大分類'].append(random.choice(category_choices))
# 大分類に応じて中分類を設定
if data['大分類'][-1] == '日用品':
data['中分類'].append(random.choice(['日用雑貨', '化粧品']))
else:
data['中分類'].append(random.choice(['加工食品', '菓子類']))
# 中分類に応じて小分類を設定
if data['中分類'][-1] == '日用雑貨':
data['小分類'].append(random.choice(['ティッシュペーパー', 'トイレットペーパー', 'ベビーおむつ', '介護おむつ', '衣料用洗剤', '住居用洗剤']))
elif data['中分類'][-1] == '化粧品':
data['小分類'].append(random.choice(['基礎化粧品', 'メイク用品', 'ヘアケア', 'ボディケア']))
elif data['中分類'][-1] == '加工食品':
data['小分類'].append(random.choice(['調味料', '冷凍食品', '乳製品']))
elif data['中分類'][-1] == '菓子類':
data['小分類'].append(random.choice(['チョコレート', 'スナック', 'アイスクリーム']))
# 商品名を生成
current_small_category = data['小分類'][-1]
product_name_choices_for_small_category = [name for name in product_name_choices if name.startswith(current_small_category)]
data['商品名'].append(random.choice(product_name_choices_for_small_category))
data['JANコード'].append(random.choice(jan_codes))
data['数量'].append(random.choice(quantities))
current_date += datetime.timedelta(days=30)
# 単価を生成
price_ranges = {
'調味料': (100, 300),
'冷凍食品': (200, 500),
'ボディケア': (300, 1000),
'ヘアケア': (300, 1000),
'スナック': (100, 300),
'衣料用洗剤': (300, 500),
'乳製品': (100, 300),
'トイレットペーパー': (200, 400),
'ティッシュペーパー': (100, 300),
'ベビーおむつ': (900, 1400),
'チョコレート': (100, 300),
'アイスクリーム': (100, 300),
'基礎化粧品': (500, 2000),
'メイク用品': (500, 2000),
'介護おむつ': (900, 1400),
'住居用洗剤': (300, 500)
}
product_prices = {}
for small_category in price_ranges:
min_price, max_price = price_ranges[small_category]
product_names_for_small_category = [name for name in product_name_choices if name.startswith(small_category)]
for product_name in product_names_for_small_category:
product_prices[product_name] = random.randint(min_price, max_price)
data['単価'] = [product_prices[product_name] for product_name in data['商品名']]
# 金額を計算
data['金額'] = [quantity * price for quantity, price in zip(data['数量'], data['単価'])]
# 3レコード分の金額・数量を欠損値に置き換える
for _ in range(3):
data['数量'][_] = None
data['金額'][_] = None
# ランダムに2つの顧客IDを選ぶ
special_customer_ids = random.sample(customer_ids, 2)
# 選ばれた顧客IDに関連するレコードの金額と数量を0に設定
for special_customer_id in special_customer_ids:
special_customer_indices = [i for i, customer_id in enumerate(data['顧客ID']) if customer_id == special_customer_id]
for index in special_customer_indices:
data['数量'][index] = 0
data['金額'][index] = 0
# データフレームに変換
df = pd.DataFrame(data)
実行したら、以下のようなデータフレームが生成されます。
☑ データセットの概要
「日付」:2021-01-01 - 2022-12-31
「店舗コード」「店舗名」:「大森店」「蒲田店」「川崎店」の3店舗
「顧客ID」:10桁のランダムな値
「性別」:「男性」「女性」「その他」の3種類
「年齢」:10歳から79歳までのランダムな値
「大分類」:「食品」「日用品」の2種類
「中分類」:大分類「食品」に紐づく「加工食品」「菓子類」など。大分類「日用品」に紐づく「日用雑貨」など
「小分類」:中分類に紐づく分類の最小単位
「JANコード」:13桁のランダムな値
「商品名」:小分類に紐づく商品の名称
「数量」:該当レコードにおける商品の合計購入個数
「単価」:該当レコードにおける商品の合計購入金額
「数量」:商品名に紐づく商品単価
これまでの経験上、だいたいどこの企業様からいただくデータも同じような構造になっているかと思います。商品構造(大分類・中分類・小分類のような)は企業様によって独自の区分を持っていたり、JICFSを使用していたりといろいろな場合があります。
また、この他にも「レジ番号」「レシート番号」「レジ通過時間」「値引額」などが含まれている場合もあります。
では、次回からデータを触っていきましょう
かしこ