标记和总结每月访问记录中的空缺(流失)情况

2024-06-26 01:43:09 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个df,看起来像这样:

 visits ={'someID':[111,111,111,222,222,222,333,333],
           'Month':[1,3,5,1,2,4,3,4],
          'visits':[4,10,5,2,2,2,5,5]}
 df = pd.DataFrame.from_dict(visits)


 someID        Month      Visits
 111           1          4
 111           3          10
 111           5          5
 222           1          2
 222           2          2
 222           4          2
 333           3          5
 333           4          5

数据框应解释为:

用户111访问了第1个月,但在第2个月进行了修改,并在第3个月返回,在第4个月进行了修改,并在第5个月返回。 用户222访问了第1个月和第2个月,在第3个月进行了翻腾,并在第4个月返回 用户333第一次访问是在第3个月(他不应该在第1个月和第2个月显示为搅和者)。你知道吗

总结每月客户流失数量和每月新访问数量的有效方法是什么? 客户流失=以前访问过,但当月未访问 新就诊=首次就诊

期望输出:(

 Month     Churns    New_visits  Totals
   2         1           0         1
   3         1           1         2
   4         1           0         2
   5         2           0         1 

Tags: 数据方法用户fromdataframedf数量客户
1条回答
网友
1楼 · 发布于 2024-06-26 01:43:09

这个解决方案是黑客和没有优化,但它可能工作得足够快,为您的目的。你知道吗

首先,使用pivot_table统计每个用户ID每月的存在情况: 你知道吗

df2 = df.pivot_table(columns='someID', 
                     index='Month', 
                     values='visits', 
                     aggfunc='count')
# df2:
#    someID 111 222 333
# Month         
#     1     1.0 1.0 NaN
#     2     NaN 1.0 NaN
#     3     1.0 NaN 1.0
#     4     NaN 1.0 1.0
#     5     1.0 NaN NaN

创建按月数索引的空数据帧,准备接收整数计数:

result = pd.DataFrame(data=np.zeros(df2.shape[0]),
                      index=df2.index,
                      columns=['New_visits'],
                      dtype=np.int16)

在第一个pivot表数据框中,在每个用户的第一次访问之前,用非NaN sentinel值标记每个月,例如-1。使用每个用户的first_valid_index统计每个月的新访问者数量:

for col in df2:
    fvi = df2[col].first_valid_index()
    df2.loc[df2[col].index < fvi, col] = -1
    result.loc[fvi, 'New_visits'] += 1
# df2
#    someID 111 222 333
# Month         
#     1     1.0 1.0 -1.0
#     2     NaN 1.0 -1.0
#     3     1.0 NaN 1.0
#     4     NaN 1.0 1.0
#     5     1.0 NaN NaN

对于每一行,将每一个NaN计算为一个搅动,将每一个正值计算为一个访问者:

result['Churns'] = df2.isnull().sum(axis=1)
result['Totals'] = df2.gt(0).sum(axis=1)
# result
#       New_visits  Churns  Totals
# Month         
#     1         2       0       2
#     2         0       1       1
#     3         1       1       2
#     4         0       1       2
#     5         0       2       1
网友
2楼 · 发布于 2024-06-26 01:43:09

这里有一种方法应该有效。你知道吗

首先,为了检测客户流失,我们需要重新编制索引,以便客户流失月份由NaN值表示。为此,首先生成列表元组(接下来将用于创建多索引):

# get the last month for which data is available
# to define a boundary for reindexing
end_month = df['Month'].max()

index_tuples = df.groupby('someID').apply(lambda x: \
    (np.repeat(x['someID'].unique(), (end_month + 1) - x['Month'].min()), \
        np.arange(x['Month'].min(), end_month + 1)))

# someID
# 111    ([111, 111, 111, 111, 111], [1, 2, 3, 4, 5])
# 222    ([222, 222, 222, 222, 222], [1, 2, 3, 4, 5])
# 333                    ([333, 333, 333], [3, 4, 5])

接下来,将列表压缩为ID-Month对,然后将列表展平并将其传递到pd.MultiIndex

index = pd.MultiIndex.from_tuples( \
    [item for sublist in index_tuples.apply( \
       lambda x: zip(*x)).tolist() for item in sublist], \
     names=['someID', 'Month'])
# MultiIndex(levels=[[111, 222, 333], [1, 2, 3, 4, 5]],
#            labels=[[0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2], [0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 2, 3, 4]],
#            names=[u'someID', u'Month'])

..并创建一个新的数据帧,该数据帧用MultiIndex重新编制索引(而不是创建df的副本,或者将其赋值回df):

df2 = df.set_index(['someID', 'Month']).reindex(index).reset_index()
#     someID  Month  visits
# 0      111      1     4.0
# 1      111      2     NaN
# 2      111      3    10.0
# 3      111      4     NaN
# 4      111      5     5.0
# 5      222      1     2.0
# 6      222      2     2.0
# 7      222      3     NaN
# 8      222      4     2.0
# 9      222      5     NaN
# 10     333      3     5.0
# 11     333      4     5.0
# 12     333      5     NaN

最后,创建一个列来跟踪新访问,如下所示:

df2.loc[df2.groupby('someID').apply(lambda x: x.first_valid_index()), "New_visits"] = 1
#     someID  Month  visits  New_visits
# 0      111      1     4.0         1.0
# 1      111      2     NaN         NaN
# 2      111      3    10.0         NaN
# 3      111      4     NaN         NaN
# 4      111      5     5.0         NaN
# 5      222      1     2.0         1.0
# 6      222      2     2.0         NaN
# 7      222      3     NaN         NaN
# 8      222      4     2.0         NaN
# 9      333      3     5.0         1.0
# 10     333      4     5.0         NaN

…并使用此groupby表达式获取每月的客户流失和新访问次数:

df2.groupby('Month').agg( \
{'visits': lambda x: x.isnull().sum(), 'New_visits':sum}).rename( \
columns={'visits': 'Churn'})
#        Churn  New_visits
# Month                   
# 1        0.0         2.0
# 2        1.0         NaN
# 3        1.0         1.0
# 4        1.0         NaN
# 5        2.0         NaN
网友
3楼 · 发布于 2024-06-26 01:43:09

这里是一种尝试使用更多Python-y数据帧代码而不是迭代循环的方法。我不确定对性能的影响,但我认为数据帧操作比迭代操作快。不管怎样,让我们开始:

我们真的不需要访问栏,所以我们可以放弃

df2 = df.drop('visits', axis=1)

按月份排序和分组-分组依据中的计数基本上是总计列

df2 = ( df2.sort_values(['Month'])
        .groupby(['Month']) 
        .agg(lambda x: list(x)) # convert the unique visitors in this month into a list
        .rename(columns={'someID':'visitors_this_month'}) )

#           visitors_this_month
# Month                    
# 1              [111, 222]
# 2                   [222]
# 3              [111, 333]
# 4              [222, 333]
# 5                   [111]

我们可以通过执行列表的累积和,然后通过应用set构造函数仅获取unique来跟踪所有以前看到的访问者

(这是一种技巧,因为不能直接添加集合)

df2 = df2.assign(all_visitors=df2['visitors_this_month'].cumsum().apply(set))

#       visitors_this_month     all_visitors
# Month                                     
# 1              [111, 222]       {222, 111}
# 2                   [222]       {222, 111}
# 3              [111, 333]  {333, 222, 111}
# 4              [222, 333]  {333, 222, 111}
# 5                   [111]  {333, 222, 111}

我想Python就是Python,但是你可以减去集合,我们可以通过减去这个月看到的访客集合和之前看到的所有访客集合来确定搅动列

df2 = df2.assign(visitors_this_month=df2['visitors_this_month'].apply(set))
df2 = df2.assign(Churns=df2['all_visitors']-df2['visitors_this_month'])

我们可以通过从本月的访客数量中减去上个月的所有访客数量来确定新的访客数量

df2 = df2.assign(New_visits=df2['visitors_this_month'] - df2['all_visitors'].shift())

#       visitors_this_month     all_visitors      Churns New_visits
# Month                                                            
# 1              {222, 111}       {222, 111}          {}        NaN
# 2                   {222}       {222, 111}       {111}         {}
# 3              {333, 111}  {333, 222, 111}       {222}      {333}
# 4              {333, 222}  {333, 222, 111}       {111}         {}
# 5                   {111}  {333, 222, 111}  {333, 222}         {}

我们现在可以删除所有访客的跟踪栏,因为我们已经完成了,还有第一个月的连续记录

df2 = df2.drop('all_visitors', axis=1)
df2 = df2.drop(df2.index[0])

因为我们只对这些集合的计数感兴趣,所以我们可以将len构造函数应用于数据帧中的每个元素

df2 = df2.applymap(len)

我们可以清理最后的数据帧

df_final = df2.rename(columns={'visitors_this_month':'Totals'})
df_final = df_final[['Churns', 'New_visits', 'Totals']]

就这样!你知道吗

# df_final
#            Churns  New_visits  Totals
# Month                            
# 2           1           0       1
# 3           1           1       2
# 4           1           0       2
# 5           2           0       1

相关问题 更多 >