为Python中的pandas DataFrame:
<表类>
ID
日期
方向
tbody><<tr>0 2022-01-03 10:00:01 0 2022-01-03 11:00:01 0 2022-01-03 11:10:01 出 0 2022-01-03 12:00:03 0 2022-01-03 14:32:01 出 12022-01-03 10:32:01 出 12022-01-04 11:32:01 12022-01-04 14:32:01 出 22022-01-02 08:00:01 出 22022-01-02 08:02:01 表类>
我不确定我是否完全理解,但这里有一个尝试。
沿着date
列(date
应该是datetime
)排序后:在ID
上分组,然后分三步消除所有不符合所需模式的行:在每组开始时消除OUT
块。2. 将每组IN
/OUT
的连续块减少到第一个。3.如果组的最后一行是IN
-row,则删除它。剩下的基本上是将结果数据帧分割成两个交错的部分,并将它们连接到另一个部分。
def connect(df):
df = df[df["direction"].cummax()]
df = df[df["direction"].diff().fillna(True)]
if df.shape[0] % 2:
df = df.iloc[:-1]
return df
result = (
df
.assign(direction=df["direction"].replace({"IN": True, "OUT": False}))
.sort_values(["ID", "date"])
.groupby("ID").apply(connect)
.drop(columns="direction")
.droplevel(-1, axis=0)
)
result = pd.concat(
[result.iloc[0::2], result[["date"]].iloc[1::2]], axis=1
).reset_index(drop=True)
result.columns = ["ID", "entry_date", "exit_date"]
结果:
ID entry_date exit_date
0 0 2022-01-03 10:00:01 2022-01-03 11:10:01
1 0 2022-01-03 12:00:03 2022-01-03 14:32:01
2 1 2022-01-04 11:32:01 2022-01-04 14:32:01
另一个解决方案是按ID
分组,然后,在每组中,将directions
分成2个堆栈,ins
和outs
,并通过它们运行以收集连接:
def connect(df):
mask = df["direction"].eq("IN")
ins = list(reversed(df.loc[mask, "date"].values))
outs = list(reversed(df.loc[~mask, "date"].values))
connects = []
if ins:
dt_out = ins[-1] + pd.Timedelta(days=-1)
while ins and outs:
dt_in = None
while ins:
dt = ins.pop()
if dt > dt_out:
dt_in = dt
break
while dt_in and outs:
dt_out = outs.pop()
if dt_in < dt_out:
connects.append([dt_in, dt_out])
break
return pd.DataFrame(
connects, columns=["entry_date", "exit_date"]
)
result = (
df.sort_values(["ID", "date"]).groupby("ID").apply(connect)
.droplevel(1, axis=0).reset_index()
)