2017-10-19 110 views
4

我有以下形式熊貓:伯爵在一組時間間隔的交叉口

import pandas as pd 

Out[1]: 
df = pd.DataFrame({'id':[1,2,3,4,5], 
      'group':['A','A','A','B','B'], 
      'start':['2012-08-19','2012-08-22','2013-08-19','2012-08-19','2013-08-19'], 
      'end':['2012-08-28','2013-09-13','2013-08-19','2012-12-19','2014-08-19']}) 

    id group  start   end 
0 1  A 2012-08-19 2012-08-28 
1 2  A 2012-08-22 2013-09-13 
2 3  A 2013-08-19 2013-08-21 
3 4  B 2012-08-19 2012-12-19 
4 5  B 2013-08-19 2014-08-19 

的數據幀對於我的數據框定的行,我想數一下那個有同組的項目數重疊的時間間隔。

例如,在2012年8月22日至2013年9月13日的A組id 2範圍內,因此id 1(2012年8月19日至2012年8月28日)以及id 3(2013年8月19日至2013年8月21日) 2.

計數相反還有在B組

所以我的例子數據幀中的項目之間沒有重疊上述,我想產生類似

Out[2]: 
    id group  start   end count 
0 1  A 2012-08-19 2012-08-28  1 
1 2  A 2012-08-22 2013-09-13  2 
2 3  A 2013-08-19 2013-08-21  1 
3 4  B 2012-08-19 2012-12-19  0 
4 5  B 2013-08-19 2014-08-19  0 

我可以「brute-強迫「這一點,但我想知道是否有更高效的熊貓這樣做的方式。

在此先感謝您的幫助

+0

你能詳細一點關於「相交時間間隔」。我的意思是說明你是如何計算的 – Dark

+0

@Bharathshetty - 我已經詳細闡述了我的問題 – johnaphun

回答

1

「蠻力」十歲上下,但能夠完成任務:

首先轉換日期字符串的日期,然後比較針對DF的每一行與適用。

df.start = pd.to_datetime(df.start) 
df.end = pd.to_datetime(df.end) 

df['count'] = df.apply(lambda row: len(df[ (((row.start <= df.start) & (df.start <= row.end)) \ 
              | ((df.start <= row.start) & (row.start <= df.end))) 
          & (row.id != df.id) & (row.group == df.group) ]),axis=1) 
+0

注意:to_datetime應該不是您的格式所必需的,它只是習慣將那些 –

+0

轉換成好的,即使我在想也一樣。儘管如此多的條件 – Dark

+0

你可以擺脫「&(row.id!= df.id)」並且做「len(...) - 1」,但不知道我們如何能夠減少apply中的交叉條件 –

1
import datetime 
def ol(a, b): 
    l=[] 
    for x in b: 
     l.append(max(0, int(min(a[1], x[1]) - max(a[0], x[0])>=datetime.timedelta(minutes=0)))) 
    return sum(l) 


df['New']=list(zip(df.start,df.end)) 
df['New2']=df.group.map(df.groupby('group').New.apply(list)) 
df.apply(lambda x : ol(x.New,x.New2),axis=1)-1 

Out[495]: 
0 1 
1 2 
2 1 
3 0 
4 0 
dtype: int64 

時序

#My method 
df.apply(lambda x : ol(x.New,x.New2),axis=1)-1 

100 loops, best of 3: 5.39 ms per loop 

#@Andy's Method 
df.groupby("group").apply(count_overlaps)  
10 loops, best of 3: 23.5 ms per loop 

#@Nathan's Method 

df.apply(lambda row: len(df[ (((row.start <= df.start) & (df.start <= row.end)) \ 
         | ((df.start <= row.start) & (row.start <= df.end))) 
         & (row.id != df.id) & (row.group == df.group) ]),axis=1) 

10 loops, best of 3: 25.8 ms per loop 
+0

你走了。我正在使用拉鍊內應用我搞砸了。這很整齊。 – Dark

+1

@Bharathshetty當我使用'apply'時,我總是在'lambda'之外進行所有的實施過程,因爲我知道我會搞砸了...... – Wen

+1

你知道urs仍然是最快的。你可以添加時間。如果你使用numba,它會更快。 – Dark

2

所以,我會看到力如何蠻力展覽會...如果它的速度慢我cythonize這個邏輯。這並不是那麼糟糕,就像O(M^2)在團隊規模上一樣,如果有很多小團體,它可能不會那麼糟糕。

In [11]: def interval_overlaps(a, b): 
    ...:  return min(a["end"], b["end"]) - max(a["start"], b["start"]) > np.timedelta64(-1) 


In [12]: def count_overlaps(df1): 
    ...:  return sum(interval_overlaps(df1.iloc[i], df1.iloc[j]) for i in range(len(df1) - 1) for j in range(i, len(df1)) if i < j) 

In [13]: df.groupby("group").apply(count_overlaps) 
Out[13]: 
group 
A 2 
B 0 
dtype: int64 

前者是this interval overlap function一個調整。


編輯:在重新閱讀它看起來像count_overlaps是每行,而不是每個組,所以AGG功能應該更像:

In [21]: def count_overlaps(df1): 
    ...:  return pd.Series([df1.apply(lambda x: interval_overlaps(x, df1.iloc[i]), axis=1).sum() - 1 for i in range(len(df1))], df1.index) 

In [22]: df.groupby("group").apply(count_overlaps) 
Out[22]: 
group 
A  0 1 
     1 2 
     2 1 
B  3 0 
     4 0 
dtype: int64 

In [22]: df["count"] = df.groupby("group").apply(count_overlaps).values 

In [23]: df 
Out[23]: 
     end group id  start count 
0 2012-08-28  A 1 2012-08-19  1 
1 2013-09-13  A 2 2012-08-22  2 
2 2013-08-19  A 3 2013-08-19  1 
3 2012-12-19  B 4 2012-08-19  0 
4 2014-08-19  B 5 2013-08-19  0 
+0

我不知道爲什麼,但我無法寫這適用於一個轉換...(我認爲它不採取快速路徑,因爲它是一個python對象,而不是一個numpy/pandas) –

+0

你喜歡變換那麼多嗎?我可以知道爲什麼 – Dark

+1

@Bharathshetty我認爲我誤解了這個問題,OP的例子並不需要轉換。但是,如果我們計算每個組內的重疊,它會將這些信息傳回原始的DataFrame中,這往往是一個很好的地方。 –