[英]Create a Pandas daily aggregate time series from a DataFrame with date ranges
我有一個訂閱的Pandas DataFrame,每個訂閱都有一個開始日期時間(時間戳)和一個可選的結束日期時間(如果已取消)。
為簡單起見,我基於開始和結束日期時間(時間戳)為日期(例如“ 20170901”)創建了字符串列。 看起來像這樣:
df = pd.DataFrame([('20170511', None), ('20170514', '20170613'), ('20170901', None), ...], columns=["sd", "ed"])
最終結果應該是一個時間序列,該時間序列是一個范圍內任何給定日期的活動訂閱數。
為此,我為范圍內的所有日期創建了一個索引:
days = df.groupby(["sd"])["sd"].count()
我可以通過循環創建一個我感興趣的內容,每個循環都對整個DataFrame df
執行一次查詢。
count_by_day = pd.DataFrame([ len(df.loc[(df.sd <= i) & (df.ed.isnull() | (df.ed > i))]) for i in days.index], index=days.index)
請注意,我在原始數據集中的每一天都有值,因此沒有差距。 我確定可以改善日期范圍。
實際的問題是:對於有數千行的大型初始數據集df,有沒有一種有效的方法來計算? 看來我使用的方法的復雜度是二次方的。 我也嘗試過df.query(),但是它比Pythonic過濾器慢66%,並且不會改變復雜性。
我嘗試在Pandas文檔中搜索示例,但似乎使用了錯誤的關鍵字。 有任何想法嗎?
這是一個有趣的問題,這是我的處理方法。 不確定性能
編輯:我的第一個答案是錯誤的,我沒有完全閱讀問題
# Initial data, columns as Timestamps
df = pd.DataFrame([('20170511', None), ('20170514', '20170613'), ('20170901', None)], columns=["sd", "ed"])
df['sd'] = pd.DatetimeIndex(df.sd)
df['ed'] = pd.DatetimeIndex(df.ed)
# Range input and related index
beg = pd.Timestamp('2017-05-15')
end = pd.Timestamp('2017-09-15')
idx = pd.DatetimeIndex(start=beg, end=end, freq='D')
# We filter data for records out of the range and then clip the
# the subscriptions start/end to the range bounds.
fdf = df[(df.sd <= beg) | ((df.ed >= end) | (pd.isnull(df.ed)))]
fdf['ed'].fillna(end, inplace=True)
fdf['ps'] = fdf.sd.apply(lambda x: max(x, beg))
fdf['pe'] = fdf.ed.apply(lambda x: min(x, end))
# We run a conditional count
idx.to_series().apply(lambda x: len(fdf[(fdf.ps<=x) & (fdf.pe >=x)]))
好的,經過大量研究,擺弄和嘗試后,我正在回答自己的問題。 我可能仍然缺少一個明顯的解決方案,但也許有幫助。
迄今為止,我能找到的最快的解決方案是(感謝Alex的一些不錯的代碼模式):
# Start with test data from question
df = pd.DataFrame([('20170511', None), ('20170514', '20170613'),
('20170901', None), ...], columns=['sd', 'ed'])
# Convert to datetime columns
df['sd'] = pd.DatetimeIndex(df['sd'])
df['ed'] = pd.DatetimeIndex(df['ed'])
df.ed.fillna(df.sd.max(), inplace=True)
# Note: In my real data I have timestamps - I convert them like this:
#df['sd'] = pd.to_datetime(df['start_date'], unit='s').apply(lambda x: x.date())
# Set and sort multi-index to enable slices
df = df.set_index(['sd', 'ed'], drop=False)
df.sort_index(inplace=True)
# Compute the active counts by day in range
di = pd.DatetimeIndex(start=df.sd.min(), end=df.sd.max(), freq='D')
count_by_day = di.to_series().apply(lambda i: len(df.loc[
(slice(None, i.date()), slice(i.date(), None)), :]))
在我的真實數據集中( df
行數超過1萬,日期范圍約為一年),這是問題代碼的兩倍,約1.5秒。
這里我學到了一些教訓:
df.apply
或df.itertuples
遍歷數據集df
並增加計數器的速度要慢得多。 奇怪的是, apply
比itertuples
慢。 甚至不用考慮iterrows
df
每一行並將活動范圍中的每個日期添加到該系列中),然后按日期分組,要慢得多。 df
上運行問題中的代碼不會改變性能。 df
上運行問題中的代碼不會改變性能。 pd.crosstab
和pd.Period
但是我什么都無法工作
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.