2015-02-26 32 views
5

Tôi có một dataframe gấu trúc trông như thế này:Tạo một nhóm duy trì từ một dataframe gấu trúc

+-----------+------------------+---------------+------------+ 
| AccountID | RegistrationWeek | Weekly_Visits | Visit_Week | 
+-----------+------------------+---------------+------------+ 
| ACC1  | 2015-01-25  |    0 | NaT  | 
| ACC2  | 2015-01-11  |    0 | NaT  | 
| ACC3  | 2015-01-18  |    0 | NaT  | 
| ACC4  | 2014-12-21  |   14 | 2015-02-12 | 
| ACC5  | 2014-12-21  |    5 | 2015-02-15 | 
| ACC6  | 2014-12-21  |    0 | 2015-02-22 | 
+-----------+------------------+---------------+------------+ 

Đó là về cơ bản một bản ghi truy cập các loại, vì nó chứa tất cả các dữ liệu cần thiết cho việc tạo ra phân tích theo nhóm.

Mỗi tuần đăng ký là một nhóm thuần tập. Để biết có bao nhiêu người là một phần của nhóm tôi có thể sử dụng:

visit_log.groupby('RegistrationWeek').AccountID.nunique() 

Những gì tôi muốn làm là tạo một bảng pivot với những tuần trước bạ như phím. Các cột phải là visit_weeks và các giá trị phải là số lượng id tài khoản duy nhất có hơn 0 lượt truy cập hàng tuần.

Cùng với tổng số tài khoản trong mỗi nhóm, tôi sẽ có thể hiển thị phần trăm thay vì giá trị tuyệt đối.

Các sản phẩm cuối cùng sẽ giống như thế này:

+-------------------+-------------+-------------+-------------+ 
| Registration Week | Visit_week1 | Visit_Week2 | Visit_week3 | 
+-------------------+-------------+-------------+-------------+ 
| week1    | 70%   | 30%   | 20%   | 
| week2    | 70%   | 30%   |    | 
| week3    | 40%   |    |    | 
+-------------------+-------------+-------------+-------------+ 

tôi đã cố gắng xoay vòng các dataframe như thế này:

visit_log.pivot_table(index='RegistrationWeek', columns='Visit_Week') 

Nhưng tôi đã không đóng đinh xuống phần giá trị. Tôi sẽ cần bằng cách nào đó tính số Id tài khoản và chia số tiền cho tổng hợp tuần đăng ký từ trên cao.

Tôi mới tham gia gấu trúc nên nếu đây không phải là cách tốt nhất để thực hiện các nhóm thuần tập, hãy khai sáng cho tôi!

Cảm ơn

+0

Bạn có thể dán mẫu DataFrame của mình vào bảng HTML hợp lệ không? Điều đó sẽ cho phép người khác đọc nó vào gấu trúc để trả lời câu hỏi của họ cho câu hỏi của bạn. –

Trả lời

9

Có một số khía cạnh cho câu hỏi của bạn.

Những gì bạn có thể xây dựng với các dữ liệu bạn có

several kinds of retention. Để đơn giản, chúng tôi sẽ chỉ đề cập đến hai:

  • Giữ ngày-N: nếu người dùng đăng ký vào ngày 0, cô ấy có đăng nhập vào ngày N không? (Đăng nhập vào ngày N + 1 không ảnh hưởng đến chỉ số này). Để đo lường nó, bạn cần phải theo dõi tất cả các bản ghi của người dùng của bạn.
  • Tỷ lệ giữ chân: nếu người dùng đăng ký vào ngày 0, cô ấy có đăng nhập vào ngày N hoặc bất kỳ ngày nào sau đó không? (Đăng nhập vào ngày N + 1 ảnh hưởng đến chỉ số này). Để đo lường, bạn chỉ cần biết nhật ký cuối cùng của người dùng.

Nếu tôi hiểu bảng của bạn một cách chính xác, bạn có hai biến có liên quan để xây dựng bảng nhóm: ngày đăng ký và nhật ký cuối cùng (tuần truy cập). Số lượt truy cập hàng tuần có vẻ không liên quan.

Vì vậy, với điều này, bạn chỉ có thể đi với tùy chọn 2, giữ chân.

Làm thế nào để xây dựng bảng

Đầu tiên, chúng ta hãy xây dựng một dữ liệu giả thiết để chúng ta có đủ để làm việc và bạn có thể tái tạo nó:

import pandas as pd 
import numpy as np 
import math 
import datetime as dt 

np.random.seed(0) # so that we all have the same results 

def random_date(start, end,p=None): 
    # Return a date randomly chosen between two dates 
    if p is None: 
     p = np.random.random() 
    return start + dt.timedelta(seconds=math.ceil(p * (end - start).days*24*3600)) 

n_samples = 1000 # How many users do we want ? 
index = range(1,n_samples+1) 

# A range of signup dates, say, one year. 
end = dt.datetime.today() 
from dateutil.relativedelta import relativedelta 
start = end - relativedelta(years=1) 

# Create the dataframe 
users = pd.DataFrame(np.random.rand(n_samples), 
        index=index, columns=['signup_date']) 
users['signup_date'] = users['signup_date'].apply(lambda x : random_date(start, end,x)) 
# last logs randomly distributed within 10 weeks of singing up, so that we can see the retention drop in our table 
users['last_log'] = users['signup_date'].apply(lambda x : random_date(x, x + relativedelta(weeks=10))) 

Vì vậy, bây giờ chúng ta nên có một cái gì đó trông như thế này:

users.head() 

enter image description here

Dưới đây là một số mã để xây dựng một bảng nhóm:

### Some useful functions 
def add_weeks(sourcedate,weeks): 
    return sourcedate + dt.timedelta(days=7*weeks) 

def first_day_of_week(sourcedate): 
    return sourcedate - dt.timedelta(days = sourcedate.weekday()) 

def last_day_of_week(sourcedate): 
    return sourcedate + dt.timedelta(days=(6 - sourcedate.weekday())) 

def retained_in_interval(users,signup_week,n_weeks,end_date): 
    ''' 
     For a given list of users, returns the number of users 
     that signed up in the week of signup_week (the cohort) 
     and that are retained after n_weeks 
     end_date is just here to control that we do not un-necessarily fill the bottom right of the table 
    ''' 
    # Define the span of the given week 
    cohort_start  = first_day_of_week(signup_week) 
    cohort_end   = last_day_of_week(signup_week) 
    if n_weeks == 0: 
     # If this is our first week, we just take the number of users that signed up on the given period of time 
     return len(users[(users['signup_date'] >= cohort_start) 
         & (users['signup_date'] <= cohort_end)]) 
    elif pd.to_datetime(add_weeks(cohort_end,n_weeks)) > pd.to_datetime(end_date) : 
     # If adding n_weeks brings us later than the end date of the table (the bottom right of the table), 
     # We return some easily recognizable date (not 0 as it would cause confusion) 
     return float("Inf") 
    else: 
     # Otherwise, we count the number of users that signed up on the given period of time, 
     # and whose last known log was later than the number of weeks added (rolling retention) 
     return len(users[(users['signup_date'] >= cohort_start) 
         & (users['signup_date'] <= cohort_end) 
         & pd.to_datetime((users['last_log']) >= pd.to_datetime(users['signup_date'].map(lambda x: add_weeks(x,n_weeks)))) 
         ]) 

Với điều này chúng ta có thể tạo ra các chức năng thực tế:

def cohort_table(users,cohort_number=6,period_number=6,cohort_span='W',end_date=None): 
    ''' 
     For a given dataframe of users, return a cohort table with the following parameters : 
     cohort_number : the number of lines of the table 
     period_number : the number of columns of the table 
     cohort_span : the span of every period of time between the cohort (D, W, M) 
     end_date = the date after which we stop counting the users 
    ''' 
    # the last column of the table will end today : 
    if end_date is None: 
     end_date = dt.datetime.today() 
    # The index of the dataframe will be a list of dates ranging 
    dates = pd.date_range(add_weeks(end_date,-cohort_number), periods=cohort_number, freq=cohort_span) 

    cohort = pd.DataFrame(columns=['Sign up']) 
    cohort['Sign up'] = dates 
    # We will compute the number of retained users, column-by-column 
    #  (There probably is a more pythonesque way of doing it) 
    range_dates = range(0,period_number+1) 
    for p in range_dates: 
     # Name of the column 
     s_p = 'Week '+str(p) 
     cohort[s_p] = cohort.apply(lambda row: retained_in_interval(users,row['Sign up'],p,end_date), axis=1) 

    cohort = cohort.set_index('Sign up')   
    # absolute values to percentage by dividing by the value of week 0 : 
    cohort = cohort.astype('float').div(cohort['Week 0'].astype('float'),axis='index') 
    return cohort 

Bây giờ bạn có thể gọi nó và xem kết quả:

cohort_table(users) 

enter image description here

Hy vọng điều này sẽ giúp

0

Sử dụng cùng định dạng users dữ liệu từ câu trả lời của rom_j, điều này sẽ sạch hơn/nhanh hơn, nhưng chỉ hoạt động với giả sử có ít nhất một lần đăng ký/tuần. Không phải là một giả định khủng khiếp về dữ liệu đủ lớn.

users = users.applymap(lambda d: d.strftime('%Y-%m-%V') if pd.notnull(d) else d) 
tab = pd.crosstab(signup_date, last_log) 
totals = tab.T.sum() 
retention_counts = ((tab.T.cumsum().T * -1) 
        .replace(0, pd.NaT) 
        .add(totals, axis=0) 
        ) 
retention = retention_counts.div(totals, axis=0) 

realined = [retention.loc[a].dropna().values for a in retention.index] 
realigned_retention = pd.DataFrame(realined, index=retention.index) 
Các vấn đề liên quan