2015-03-13 59 views
2

我有一些與開始和結束時間的事件這樣的數據:聚集事件與熊貓開始和結束時間

df = pd.DataFrame({'start': ['2015-01-05', '2015-01-10', '2015-01-11'], 'end': ['2015-01-07', '2015-01-15', '2015-01-13'], 'value': [3, 4, 5]}) 
df['end'] = pd.to_datetime(df['end']) 
df['start'] = pd.to_datetime(df['start']) 

日期:

  end  start value 
0 2015-01-07 2015-01-05  3 
1 2015-01-15 2015-01-10  4 
2 2015-01-13 2015-01-11  5 

現在我需要計算同時活動的事件數量,例如。它們的值的總和。所以結果應該是這樣的:

 date count sum 
2015-01-05  1  3 
2015-01-06  1  3 
2015-01-07  1  3 
2015-01-08  0  0 
2015-01-09  0  0 
2015-01-10  1  4 
2015-01-11  2  9 
2015-01-12  2  9 
2015-01-13  2  9 
2015-01-14  1  4 
2015-01-15  1  4 

任何想法如何做到這一點?我正在考慮爲groupby使用自定義的Grouper,但據我所見,Grouper只能將一行分配給單個組,因此看起來不太有用。

編輯:一些測試,我發現這個相當醜陋的方式來獲得期望的結果後:

df['count'] = 1 
dates = pd.date_range('2015-01-05', '2015-01-15', freq='1D') 

start = df[['start', 'value', 'count']].set_index('start').reindex(dates) 
end = df[['end', 'value', 'count']].set_index('end').reindex(dates).shift(1) 

rstart = pd.rolling_sum(start, len(start), min_periods=1) 
rend = pd.rolling_sum(end, len(end), min_periods=1) 

rstart.subtract(rend, fill_value=0).fillna(0) 

然而,這僅與資金運作,我不能看到一個明顯的方法,使其與工作其他功能。例如,有沒有辦法讓它與中位數而不是總和一起工作?

+0

讓人聯想到計數纏繞或開合分隔符,但不清楚如何移植算法。 – cphlewis 2015-03-14 02:48:58

回答

1

如果我使用SQL,我想通過參加這樣做事件表的全日期表,然後按日期分組。大熊貓不使這種方法特別簡單,因爲沒有辦法在一個條件,左加入,但我們可以僞造它使用虛擬列和重建索引:

df = pd.DataFrame({'start': ['2015-01-05', '2015-01-10', '2015-01-11'], 'end': ['2015-01-07', '2015-01-15', '2015-01-13'], 'value': [3, 4, 5]}) 
df['end'] = pd.to_datetime(df['end']) 
df['start'] = pd.to_datetime(df['start']) 
df['dummy'] = 1 

然後:

date_series = pd.date_range('2015-01-05', '2015-01-15', freq='1D') 
date_df = pd.DataFrame(dict(date=date_series, dummy=1)) 

cross_join = date_df.merge(df, on='dummy') 
cond_join = cross_join[(cross_join.start <= cross_join.date) & (cross_join.date <= cross_join.end)] 
grp_join = cond_join.groupby(['date']) 
final = (
    pd.DataFrame(dict(
     val_count=grp_join.size(), 
     val_sum=grp_join.value.sum(), 
     val_median=grp_join.value.median() 
    ), index=date_series) 
    .fillna(0) 
    .reset_index() 
) 

fillna(0)並不完美,因爲它會將val_median列中的零點設置爲0,當它們應該保持爲零時。

或者,pandas-ply我們可以代碼起來就是:

date_series = pd.date_range('2015-01-05', '2015-01-15', freq='1D') 
date_df = pd.DataFrame(dict(date=date_series, dummy=1)) 

final = (
    date_df 
    .merge(df, on='dummy') 
    .ply_where(X.start <= X.date, X.date <= X.end) 
    .groupby('date') 
    .ply_select(val_count=X.size(), val_sum=X.value.sum(), median=X.value.median()) 
    .reindex(date_series) 
    .ply_select('*', val_count=X.val_count.fillna(0), val_sum=X.val_sum.fillna(0)) 
    .reset_index() 
) 

它處理空值的好一點。

+0

不錯,謝謝!這是用條件構造連接表的一種巧妙方式。我將不得不用一些真實數據來測試它,並查看大型表的性能。 – 2015-04-24 08:52:20

0

這就是我想出來的。得想有一個更好的辦法

鑑於你的框架

  end  start value 
0 2015-01-07 2015-01-05  3 
1 2015-01-15 2015-01-10  4 
2 2015-01-13 2015-01-11  5 

然後

dList = [] 
vList = [] 
d = {} 

def buildDict(row): 
    for x in pd.date_range(row["start"],row["end"]): #build a range for each row 
     dList.append(x) #date list 
     vList.append(row["value"]) #value list 

df.apply(buildDict,axis=1) #each row in df is passed to buildDict 

#this d will be used to create our new frame 
d["date"] = dList 
d["value"] = vList 

#from here you can use whatever agg functions you want 
pd.DataFrame(d).groupby("date").agg(["count","sum"]) 

產生

  value 
     count sum 
date   
2015-01-05 1 3 
2015-01-06 1 3 
2015-01-07 1 3 
2015-01-10 1 4 
2015-01-11 2 9 
2015-01-12 2 9 
2015-01-13 2 9 
2015-01-14 1 4 
2015-01-15 1 4 
相關問題