2017-02-25 5 views
2

Ich versuche eine effizientere Möglichkeit zu finden, überlappende Datenbereiche (Start/Enddaten pro Zeile) in einem Datenrahmen basierend auf einer bestimmten Spalte (ID) zu finden.Finden Sie Datumsbereich Überlappung in Python

Datenrahmen wird sortiert auf ‚aus‘ Spalte

Ich denke, es ist ein Weg, „double“ anwenden Funktion wie ich zu vermeiden ...

import pandas as pd 
from datetime import datetime 

df = pd.DataFrame(columns=['id','from','to'], index=range(5), \ 
        data=[[878,'2006-01-01','2007-10-01'], 
         [878,'2007-10-02','2008-12-01'], 
         [878,'2008-12-02','2010-04-03'], 
         [879,'2010-04-04','2199-05-11'], 
         [879,'2016-05-12','2199-12-31']]) 

df['from'] = pd.to_datetime(df['from']) 
df['to'] = pd.to_datetime(df['to']) 


    id from  to 
0 878 2006-01-01 2007-10-01 
1 878 2007-10-02 2008-12-01 
2 878 2008-12-02 2010-04-03 
3 879 2010-04-04 2199-05-11 
4 879 2016-05-12 2199-12-31 

ich verwendet, um die „Anwendung“ -Funktion Schleife auf alle Gruppen und innerhalb jeder Gruppe, verwende ich „apply“ pro Zeile:

def check_date_by_id(df): 

    df['prevFrom'] = df['from'].shift() 
    df['prevTo'] = df['to'].shift() 

    def check_date_by_row(x): 

     if pd.isnull(x.prevFrom) or pd.isnull(x.prevTo): 
      x['overlap'] = False 
      return x 

     latest_start = max(x['from'], x.prevFrom) 
     earliest_end = min(x['to'], x.prevTo) 
     x['overlap'] = int((earliest_end - latest_start).days) + 1 > 0 
     return x 

    return df.apply(check_date_by_row, axis=1).drop(['prevFrom','prevTo'], axis=1) 

df.groupby('id').apply(check_date_by_id) 

    id from  to   overlap 
0 878 2006-01-01 2007-10-01 False 
1 878 2007-10-02 2008-12-01 False 
2 878 2008-12-02 2010-04-03 False 
3 879 2010-04-04 2199-05-11 False 
4 879 2016-05-12 2199-12-31 True 

wurde mein Code aus den folgenden Links inspiriert:

Antwort

2

könnten Sie verschieben nur die to Spalte und eine direkte Subtraktion der Datetimes durchzuführen.

df['overlap'] = (df['to'].shift()-df['from']) > timedelta(0) 

Angewandt während durch id Gruppierung

aussehen kann
df['overlap'] = (df.groupby('id') 
        .apply(lambda x: (x['to'].shift() - x['from']) > timedelta(0)) 
        .reset_index(level=0, drop=True)) 

Demo

>>> df 
    id  from   to 
0 878 2006-01-01 2007-10-01 
1 878 2007-10-02 2008-12-01 
2 878 2008-12-02 2010-04-03 
3 879 2010-04-04 2199-05-11 
4 879 2016-05-12 2199-12-31 

>>> df['overlap'] = (df.groupby('id') 
         .apply(lambda x: (x['to'].shift() - x['from']) > timedelta(0)) 
         .reset_index(level=0, drop=True)) 

>>> df 
    id  from   to overlap 
0 878 2006-01-01 2007-10-01 False 
1 878 2007-10-02 2008-12-01 False 
2 878 2008-12-02 2010-04-03 False 
3 879 2010-04-04 2199-05-11 False 
4 879 2016-05-12 2199-12-31 True 
+0

Dank. Einfach und klar. Würdest du zufällig wissen, wie man dieselbe Operation durchführt (groupby + check), aber für alle Daten und nicht nur für die aufeinanderfolgenden? – Edouard

+0

Ich bin nicht ganz sicher, was du meinst ... wenn die Daten sortiert sind, was würde das mehr bewirken? Und ich habe ein Beispiel für die Gruppierung nach ID für Sie hinzugefügt. – miradulo

0

Sie können die from Spalte sortieren und dann einfach überprüfen, ob es mit einer früheren to überlappt Spalte oder nicht mit Rollen anwenden Funktion, die sehr effizient ist.

df['from'] = pd.DatetimeIndex(df['from']).astype(np.int64) 
df['to'] = pd.DatetimeIndex(df['to']).astype(np.int64) 

sdf = df.sort_values(by='from') 
sdf[["from", "to"]].stack().rolling(window=2).apply(lambda r: 1 if r[1] >= r[0] else 0).unstack() 

nun die überlappenden Perioden sind die mit from=0.0

from to 
0 NaN 1.0 
1 1.0 1.0 
2 1.0 1.0 
3 1.0 1.0 
4 0.0 1.0 
Verwandte Themen