آنچه در این مقاله میخوانید [پنهانسازی]
برای انجام Cohort Analysis با پایتون و سنجش بازگشت کاربران، تاریخ اولین فعالیت هر کاربر را به عنوان کوهورت بگیرید، فاصله ماهانه تا هر تعامل بعدی را محاسبه کنید، تعداد کاربران یکتا را در هر فاصله زمانی بشمارید و نتیجه را به صورت جدول نگهداشت و هیت مپ ترسیم کنید. همین چهار گام، تصویر دقیقی از وفاداری و نگهداشت کاربر به شما می دهد.
پیش نیازها و شکل داده
به یک لاگ رویداد سطح کاربر نیاز دارید. حداقل ستون ها:
- user_id: شناسه یکتا کاربر
- event_time: زمان وقوع رویداد (قابل تبدیل به تاریخ)
- event_name: نوع رویداد (اختیاری؛ برای تعریف بازگشت بر اساس تعامل خاص مانند login یا purchase)
هدف: برای هر user_id تاریخ اولین فعالیت را پیدا کنیم (cohort)، سپس فعالیت های بعدی او را نسبت به همان ماه پایه اندازه بگیریم.
آماده سازی محیط و خواندن داده
کتابخانه های مورد نیاز را نصب و داده را بارگذاری کنید.
pip install pandas numpy seaborn matplotlibimport pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
# خواندن داده؛ نام فایل و نام ستون ها را با داده خود هماهنگ کنید
df = pd.read_csv('events.csv')
# تبدیل زمان به نوع datetime
df['event_time'] = pd.to_datetime(df['event_time'], utc=True, errors='coerce')
# اگر منطقه زمانی ندارید و همه زمان ها یکسان هستند، می توانید utc را حذف کنید:
# df['event_time'] = pd.to_datetime(df['event_time'], errors='coerce')
# حذف رکوردهای نامعتبر
df = df.dropna(subset=['user_id', 'event_time']).copy()
# اگر فقط بازگشت مبتنی بر رویداد خاص مدنظر است، فیلتر کنید. مثال:
# df = df[df['event_name'].isin(['login','purchase'])]اگر داده آماده ندارید: ساخت دیتاست تمرینی کوچک
برای تمرین، می توانید یک دیتاست مصنوعی بسازید تا کدها را اجرا و نتیجه را ببینید.
import numpy as np
rng = np.random.default_rng(42)
n_users = 800
start = pd.Timestamp('2024-01-01', tz='UTC')
end = pd.Timestamp('2024-12-31', tz='UTC')
# تاریخ اولین فعالیت هر کاربر در بازه یک سال
first_times = pd.to_datetime(rng.integers(start.value//10**9, end.value//10**9, n_users), unit='s', utc=True)
users = pd.Index(range(1, n_users+1), name='user_id')
cohort_df = pd.DataFrame({'user_id': users, 'first_time': first_times}).sort_values('first_time')
rows = []
for uid, ft in cohort_df.itertuples(index=False):
# هر کاربر تا 12 ماه بعد احتمال بازگشت دارد
for m in range(0, 12):
# احتمال بازگشت در ماه m: در ماه های اولیه بیشتر
p = max(0.7 - 0.07*m, 0.02)
if rng.random() < p:
event_month = (ft.to_period('M') + m).to_timestamp(tz='UTC')
# یک روز تصادفی در همان ماه
day = int(rng.integers(0, 27))
event_time = event_month + pd.Timedelta(days=day, hours=int(rng.integers(0, 23)))
rows.append((uid, event_time, 'active'))
df = pd.DataFrame(rows, columns=['user_id','event_time','event_name'])ساخت کوهورت و شاخص فاصله زمانی
در این مرحله اولین فعالیت هر کاربر را پیدا می کنیم و فاصله ماهانه رویدادها تا ماه کوهورت را حساب می کنیم.
# اولین فعالیت (first_seen) هر کاربر
first_seen = df.groupby('user_id', as_index=False)['event_time'].min().rename(columns={'event_time':'first_time'})
# اتصال first_time به لاگ رویداد
df = df.merge(first_seen, on='user_id', how='left')
# همتراز کردن به ابتدای ماه (برای همسنجی ماه به ماه)
df['cohort_month'] = df['first_time'].dt.to_period('M').dt.to_timestamp()
df['event_month'] = df['event_time'].dt.to_period('M').dt.to_timestamp()
# شاخص فاصله ماهانه از کوهورت (ماه 1 = ماه کوهورت)
df['cohort_index'] = ((df['event_month'].dt.year - df['cohort_month'].dt.year) * 12
+ (df['event_month'].dt.month - df['cohort_month'].dt.month) + 1)
# فقط شاخص های معتبر را نگه دارید
df = df[df['cohort_index'] >= 1]محاسبه نگهداشت و جدول بازگشت
تعداد کاربران یکتا در هر کوهورت و هر فاصله زمانی را می شماریم و سپس آن را به درصد نسبت به اندازه اولیه کوهورت نرمال می کنیم.
# تعداد کاربران یکتا در هر (cohort_month, cohort_index)
cohort_counts = (df.groupby(['cohort_month','cohort_index'])['user_id']
.nunique()
.unstack(fill_value=0)
.sort_index())
# اندازه هر کوهورت در ماه 1 (ستون اول)
cohort_sizes = cohort_counts.get(1)
# اگر ستون 1 وجود ندارد، یعنی داده ماه پایه ندارید
# ماتریس نگهداشت به درصد
retention = cohort_counts.divide(cohort_sizes, axis=0).round(3)
# نمایش بخشی از نتیجه
print(retention.head())تفسیر: هر سطر یک کوهورت (بر اساس ماه اولین فعالیت) است و هر ستون نشان می دهد چند درصد از آن کوهورت در ماه n ام بازگشته اند.
ترسیم هیت مپ و خواندن نتیجه
نمایش تصویری به کشف الگوها کمک می کند؛ مثلا افت شدید در ماه 2 یا بهبود نگهداشت در کوهورت های جدیدتر.
plt.figure(figsize=(12, 6))
sns.heatmap(retention, annot=True, fmt='.0%', cmap='YlGnBu', cbar_kws={'format': '%.0f%%'})
plt.title('Cohort Retention by Month')
plt.xlabel('ماه از کوهورت')
plt.ylabel('ماه کوهورت')
plt.yticks(rotation=0)
plt.show()اعتبارسنجی نتیجه: چه چیزهایی را چک کنیم؟
- ستون ماه 1 باید 100 درصد باشد. اگر نیست، تعریف کوهورت یا همترازسازی ماه را بازبینی کنید.
- برای کوهورت های خیلی جدید، ستون های ماه 3 یا 4 ممکن است خالی باشد؛ این طبیعی است چون هنوز زمان کافی نگذشته است.
- جمع کاربران یکتای هر سطر در ستون 1 باید با تعداد کاربران آن کوهورت برابر باشد.
- اگر تحلیل شما بر اساس رویداد خاصی است (مثلا purchase)، مطمئن شوید فیلتر درست اعمال شده است.
خطاهای رایج و نکات مهم
- تعریف نادرست بازگشت: پیش از کدنویسی تصمیم بگیرید «بازگشت» یعنی چه؟ هر لاگین؟ هر خرید؟ یا هر سشن؟ نتیجه کاملا به همین تعریف وابسته است.
- عدم همترازسازی ماه: اگر به ابتدای ماه همتراز نکنید، اختلاف روزها محاسبه را به هم می زند.
- مناطق زمانی متفاوت: اگر داده از چند منطقه زمانی است، ابتدا همه را به UTC یا یک منطقه واحد تبدیل کنید.
- داده ناقص در ماه های آخر: ماه جاری یا کوهورت های تازه را در مقایسه های بین ماهی خیلی جدی نگیرید یا آنها را از نمودار حذف کنید.
- کاربر تکراری یا بات: قبل از تحلیل، کاربران تستی یا بات ها را حذف کنید تا نرخ ها واقعی شوند.
گسترش تحلیل: کانال جذب، پلن یا کشور
برای تصمیم های عملی، نرخ نگهداشت را بر اساس ویژگی های کلیدی تفکیک کنید؛ مثلا کانال جذب (channel)، پلن (plan) یا کشور (country). کافی است این ستون را هم وارد groupby کنید.
# فرض: ستون channel در داده وجود دارد
seg = (df.groupby(['channel','cohort_month','cohort_index'])['user_id']
.nunique()
.unstack(fill_value=0))
# محاسبه نگهداشت برای هر کانال
sizes = seg.get(1)
seg_retention = seg.divide(sizes, axis=0).round(3)
# دسترسی به هیت مپ یک کانال خاص
ch = 'ads'
sns.heatmap(seg_retention.loc[ch], annot=True, fmt='.0%', cmap='YlOrBr')
plt.title(f'Cohort Retention - {ch}')
plt.show()شاخص زمانی جایگزین: هفتگی یا 30 روزه
اگر چرخه محصول شما کوتاه تر است، کوهورت هفتگی یا شاخص 30 روزه بسازید.
# کوهورت هفتگی (شروع هفته دوشنبه)
df['cohort_week'] = df['first_time'].dt.to_period('W-MON').dt.to_timestamp()
df['event_week'] = df['event_time'].dt.to_period('W-MON').dt.to_timestamp()
df['cohort_idx_w'] = ((df['event_week'] - df['cohort_week']) / np.timedelta64(1, 'W')).astype(int) + 1برای بازگشت 30 روزه نیز می توانید فاصله زمانی را بر حسب روز محاسبه و به بازه های 30 روزه نگاشت کنید.
گام بعدی چیست؟
الگوی نگهداشت را که دیدید، آزمایش های محصول و بازاریابی را هدفمند طراحی کنید: بهبود ورود کاربران ماه 2، فعال سازی اولیه بهتر یا پیام رسانی متفاوت برای کانال های ضعیف. همان کدی که نوشتید را به اسکریپت زمان بندی شده تبدیل کنید و خروجی را در یک داشبورد به روز نگه دارید تا تغییرات را به موقع ببینید.







