根据 Pandas DataFrame 中每个项目的开始和结束日期计算每月活动的项目数
Posted
技术标签:
【中文标题】根据 Pandas DataFrame 中每个项目的开始和结束日期计算每月活动的项目数【英文标题】:Counting number of items active in each month, calculated from per-item start and end dates in a Pandas DataFrame 【发布时间】:2018-11-12 15:57:47 【问题描述】:假设我们有以下DataFrame
,它详细说明了错误跟踪系统中的错误:
import pandas as pd
bugs = pd.DataFrame([
'key': 'ABC-1', 'priority': 'high', 'start': pd.Timestamp(2018, 1, 1), 'end': pd.Timestamp(2018,3,20),
'key': 'ABC-2', 'priority': 'med', 'start': pd.Timestamp(2018, 1, 2), 'end': pd.Timestamp(2018,1,20),
'key': 'ABC-3', 'priority': 'high', 'start': pd.Timestamp(2018, 2, 3), 'end': pd.Timestamp(2018,3,20),
'key': 'ABC-4', 'priority': 'med', 'start': pd.Timestamp(2018, 1, 4), 'end': pd.Timestamp(2018,3,20),
'key': 'ABC-5', 'priority': 'high', 'start': pd.Timestamp(2018, 2, 5), 'end': pd.Timestamp(2018,2,20),
'key': 'ABC-6', 'priority': 'med', 'start': pd.Timestamp(2018, 3, 6), 'end': pd.Timestamp(2018,3,20)
], columns=['key', 'priority', 'start', 'end'])
这里,start
和 end
表示第一次发现错误的日期和关闭日期。
我们如何按月计算“打开”错误的数量,按优先级细分?也就是说,输出如下所示:
High Med
Month
January 1 2
February 3 1
March 2 2
挑战在于同时考虑“开始”和“结束”日期。因此,在 1 月 5 日打开并在 2 月 3 日关闭的优先级“高”错误应计入 1 月和 2 月的“高”优先级错误,而不是 3 月。以此类推。
【问题讨论】:
两个具有相同优先级、发生在相同月份但不同年份的错误最终会被视为相同吗? 不,这个想法是“每个日历月”,而不是“任何一月” 【参考方案1】:简单而简短:) 这个想法是每月选择其错误重叠的行。
months = ['January', 'February', 'March',
'April'] # of course you can complete this list
bugs[months] = pd.concat([((bugs['start'].dt.month <= i) &
(i <= bugs['end'].dt.month)).astype(int)
for i in range(1, len(months) + 1)], axis=1)
bugs.groupby('priority')[months].sum()
结果:
January February March April
priority
high 1 3 2 0
med 2 1 2 0
【讨论】:
这需要我预先定义月份,并将两个不同年份的同一月份计为同一类别。也许我没有很好地解释这个问题,但这并不完全正确。这个想法是为了显示历史趋势,例如“上个月开放了 10 个高优先级缺陷,高于前一个月开放的 5 个”。【参考方案2】:我正在使用stack
和resample
from pandas.tseries.offsets import MonthEnd
s=bugs.set_index(['key','priority']).stack() # faltten your dataframe , make start and end in the same row , since we do need a range of date
s=pd.to_datetime(s)+MonthEnd(1) # change the date to same scale , month end , since you need monthly data only
s=s.reset_index().drop_duplicates(['key',0]) # if the start is same with end , we only need keep one of them.
s=s.groupby('key').apply(lambda x : x.set_index(0).resample('M').ffill()).reset_index(level=1) # groupby the key then we resample , adding the value between the start and end prepare for the frequency count
pd.crosstab(s[0].dt.month,s['priority'])# count the frequency
Out[149]:
priority high med
0
1 1 2
2 3 1
3 2 2
【讨论】:
希望能解释一下这里发生了什么。即使通过它,它也会做很多......的事情。 :-) 似乎在我的测试中有效! @optilude 逐行添加解释:-)【参考方案3】:from pandas.tseries.offsets import MonthEnd
# Last day of the previous month
bugs['start1'] = bugs.start + MonthEnd(-1)
# The first days of the months that > start1 and <= end
bugs['months'] = bugs[['start1', 'end']].apply(lambda x: tuple(pd.date_range(x[0], x[1], freq='MS')), axis=1, raw=True)
# Create dummy columns
dummies = bugs.months.apply(lambda x: pd.Series(k:1 for k in x)).fillna(0)
bugs = bugs.join(dummies)
# Aggregation
bugs.groupby('priority')[dummies.columns].sum().T
priority high med
2018-01-01 1.0 2.0
2018-02-01 3.0 1.0
2018-03-01 2.0 2.0
【讨论】:
【参考方案4】:基于this answer,我设法完成了以下工作:
def strip_day(timestamp):
return pd.Timestamp(timestamp.year, timestamp.month, 1)
monthly_bugs = pd.concat([
pd.DataFrame(index=pd.date_range(strip_day(b.start), strip_day(b.end), freq='MS'), data=[[b.key]], columns=[b.priority])
for b in bugs.itertuples()
]).resample('MS').count()
不过,我不确定它是否比此处发布的其他一些答案更好(谢谢!)。
【讨论】:
以上是关于根据 Pandas DataFrame 中每个项目的开始和结束日期计算每月活动的项目数的主要内容,如果未能解决你的问题,请参考以下文章
Pandas Dataframe to pivot table - 根据前两列的添加创建新列
Pandas Dataframe - 按照Col A分组并对每个组进行求和[C]重复