簡體   English   中英

根據日期范圍的數據框創建熊貓每日匯總時間序列

[英]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秒。

這里我學到了一些教訓:

  • 使用日期范圍的計數器創建一個Series並使用df.applydf.itertuples遍歷數據集df並增加計數器的速度要慢得多。 奇怪的是, applyitertuples慢。 甚至不用考慮iterrows
  • 我的數據集每行都有一個product_id,因此過濾每個產品的數據集並在過濾結果(每個產品)上運行計算的速度是將product_id添加到多索引並在該級別切片的兩倍
  • 建立一個中間活動日期系列(從遍歷df每一行並將活動范圍中的每個日期添加到該系列中),然后按日期分組,要慢得多。
  • 在具有多索引的df上運行問題中的代碼不會改變性能。
  • 在具有有限列集(我的實際數據集有22列)的df上運行問題中的代碼不會改變性能。
  • 我當時在看pd.crosstabpd.Period但是我什么都無法工作
  • Pandas非常棒,要想使其超越智能真的很難(特別是在Python中非矢量化)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM