我是py的新手,无法弄清楚如何在首次销售后20天找到销售电话的数量。问题是让我计算出在开始的20天内至少拨打了10个电话的销售人员的百分比。
每行是一个销售电话,销售人员由id
列表示,销售电话的时间记录在call_starttime
中。
df非常简单,看起来像这样
id call_starttime level
0 66547 7/28/2015 23:18 1
1 66272 8/10/2015 20:48 0
2 66547 8/20/2015 17:32 2
3 66272 8/31/2015 18:21 0
4 66272 8/31/2015 20:25 0
我已经计算出每个id的convos数量,并且可以过滤掉至少没有进行10次电话销售的人
当前正在使用的代码为
df_withcount=df.groupby(['cc_user_id','cc_cohort']).size().reset_index(name='count')
df_20andmore=df_withcount.loc[(df_withcount['count'] >= 20)]
我希望输出的结果是给我ID(销售人员),他们在前20天至少拨打了10个电话。到目前为止,我只能弄清楚该如何在整个时间内至少拨打10次电话
答案 0 :(得分:0)
我假设 call_starttime 列为 DateTime 类型。
让我们从简化的解决方案开始,仅检查 second 调用 (不是10个后续通话)。
我稍微更改了您的测试数据,所以id = 66272的人 在第一个通话(8月10日和19日)之后的20天内进行了第二次通话:
id call_starttime level
0 66547 2015-07-28 23:18:00 1
1 66272 2015-08-10 20:48:00 0
2 66547 2015-08-20 17:32:00 2
3 66272 2015-08-19 18:21:00 0
4 66272 2015-08-31 20:25:00 0
第一步是定义一个函数,说明当前是否 这个人是“活跃的”(他在第一次通话后的20天内进行了第二次通话):
def active(grp):
if grp.shape[0] < 2:
return False # Single call
d0 = grp.call_starttime.iloc[0]
d1 = grp.call_starttime.iloc[1]
return (d1 - d0).days < 20
此功能将应用于每组行(针对每个人)。
要获取有关每个人的活动的详细信息信息,您可以运行:
df.groupby('id').apply(active)
对于我的样本数据,结果是:
id
66272 True
66547 False
dtype: bool
但是,如果您只对活跃人数的人数感兴趣,请使用
np.count_nonzero
处理以上结果:
np.count_nonzero(df.groupby('id').apply(active))
对于我的样本数据,结果为 1 。
如果您想要活跃的人的百分比,请将该数字除以 df.id.unique()。size (乘以100,以百分比表示结果)。
现在,如何更改此解决方案以检查某人是否做出了 在最初的20天内至少致电10 :
唯一的区别是 active 函数应该比较日期 呼叫No 0 和 9 。
因此,此功能应更改为:
def active(grp):
if grp.shape[0] < 10:
return False # Too little calls
d0 = grp.call_starttime.iloc[0]
d1 = grp.call_starttime.iloc[9]
return (d1 - d0).days < 20
我假设源行按 call_starttime 排序。 如果不是这种情况,请先调用 sort_values(by ='call_starttime')。
我想出了另一种解决方案,包括按 level 列分组, 对源数据排序没有要求,并且易于参数化 有关此期间的初始天数和致电次数。
测试DataFrame:
id call_starttime level
0 66547 2015-07-28 23:18:00 1
1 66272 2015-08-10 19:48:00 0
2 66547 2015-08-20 17:32:00 1
3 66272 2015-08-19 18:21:00 0
4 66272 2015-08-29 20:25:00 0
5 66777 2015-08-30 20:00:00 0
级别 0 包含一个在前20天内(8月10日,19日和29日)进行3次通话的人。 但是请注意,最后一次通话的时间比第一次通话晚,因此实际上 相较于前19天,这2个时间戳记要更多,但是自从我解决方案以来 清除时间部分,将计入最后一次呼叫 。
从定义函数开始:
def activity(grp, dayNo):
stDates = grp.dt.floor('d') # Delete time component
# Leave dates from starting "dayNo" days
stDates = stDates[stDates < stDates.min() + pd.offsets.Day(dayNo)]
return stDates.size
提供特定人员的通话次数(一组 call_starttime 值) 在头 dayNo 天内。
下一个要定义的功能是:
def percentage(s, callNo):
return s[s >= callNo].size * 100 / s.size
计算 s (当前级别的系列)中值的百分比 > = callNo 。
第一步是计算 Series -调用次数, 在定义的“开始时间”内,针对每个级别 / id :
calls = df.groupby(['level', 'id']).call_starttime.apply(activity, dayNo=20)
(用于我的数据)结果是:
level id
0 66272 3
66777 1
1 66547 1
Name: call_starttime, dtype: int64
要获得最终结果(每个级的百分比,假设 要求进行 3 个调用),运行:
calls.groupby(level=0).apply(percentage, callNo=3)
请注意,上面的 level = 0 是对 MultiIndex级别的引用, 不是列名。
结果(同样是我的数据)是:
level
0 50.0
1 0.0
Name: call_starttime, dtype: float64
第0级有1个符合条件的人(总共2人 级别),因此该百分比为 50 ,而在 1级中,没有人符合条件, 因此百分比为 0 。
请注意,使用 dayNo 和 callNo 参数可以轻松进行参数化 关于每个人的“初始期限”的长度,以及 在此期间要拨打的电话数。
上面描述的计算是针对 3 个调用的,但对于您而言 将 callNo 更改为您的值,即 10 。
如您所见,此解决方案很短(只有8行代码), 比其他解决方案要短得多,而且“熊猫人”也更多。
如果您喜欢“简洁”的编码样式,也可以进行整个计算 在单(尽管链接明显)的说明中:
df.groupby(['level', 'id']).call_starttime\
.apply(activity, dayNo=20).rename('Percentage')\
.groupby(level=0).apply(percentage, callNo=3)
我添加了 .rename('Percentage')来更改结果 Series 的名称。
答案 1 :(得分:0)
我使用Person类来帮助解决此问题。
我已经测试了我的代码,并且效果很好。可以进行改进,但是我的主要重点是实现良好的工作解决方案。如果您有任何问题,请告诉我。
import pandas as pd
from datetime import timedelta
import datetime
import numpy as np
# prep data for dataframe
lst = {'call_start_time':['7/28/2015','8/10/2015','7/28/2015','7/28/2015'],
'level':['1','0','1','1'],
'id':['66547', '66272', '66547','66547']}
# create dataframe
df = pd.DataFrame(lst)
# convert to TimeDelta object to subtract days
for index, row in df.iterrows():
row['call_start_time'] = datetime.datetime.strptime(row['call_start_time'], "%m/%d/%Y").date()
# get the end date by adding 20 days to start day
df["end_of_20_days"] = df["call_start_time"] + timedelta(days=20)
# used below comment for testing might need it later
# df['Difference'] = (df['end_of_20_days'] - df['call_start_time']).dt.days
# created person class to keep track of days_count and id
class Person(object):
def __init__(self, id, start_date, end_date):
self.id = id
self.start_date = start_date
self.end_date = end_date
self.days_count = 1
# create list to hold objects of person class
person_list = []
# populate person_list with Person objects and their attributes
for index, row in df.iterrows():
# get result_id to use as conditional for populating Person objects
result_id = any(x.id == row['id'] for x in person_list)
# initialize Person objects and inject with data from dataframe
if len(person_list) == 0:
person_list.append(Person(row['id'], row['call_start_time'], row['end_of_20_days']))
elif not(result_id):
person_list.append(Person(row['id'], row['call_start_time'], row['end_of_20_days']))
else:
for x in person_list:
# if call_start_time is within 20 days time frame, increment day_count to Person object
diff = (x.end_date - row['call_start_time']).days
if x.id == row['id'] and diff <= 20 :
x.days_count += 1
break
# flag to check if nobody hit the sales mark
flag = 0
# print out only person_list ids who have hit the sales mark
for person in person_list:
if person.days_count >= 10:
flag = 1
print("person id:{} has made {} calls within the past 20 days since first call date".format(person.id, person.days_count))
if flag == 0:
print("No one has hit the sales mark")