作者:小小明,Pandas數據處理專家,致力於幫助無數數據從業者解決數據處理難題。
需求說明
有一份下面格式用戶遊覽日誌的數據(複製下面顯示的表格後,運行下面的代碼纔會出現相同的結果):
import pandas as pd
df = pd.read_clipboard()
df
結果:
uid | start | end | |
---|---|---|---|
0 | A | 1 | 2 |
1 | A | 4 | 7 |
2 | A | 3 | 6 |
3 | A | 8 | 9 |
4 | B | 2 | 3 |
5 | B | 4 | 7 |
6 | B | 10 | 11 |
7 | B | 6 | 8 |
8 | B | 12 | 15 |
9 | C | 14 | 15 |
其中uid表示每個用戶,start表示起始遊覽時間,end表示結束遊覽的時間,從上表可以看到,存在遊覽時間重疊的情況,例如用戶A的遊覽時間3-6和4-7重疊,可以認爲遊覽時間是3-7。
我們現在要做的事就是把每個用戶的存在重疊的遊覽時間合併到一起,最終並按照時間順序排序顯示。
注意:3-4和4-6也屬於重疊的時間,可以合併爲3-6。
先對一個用戶進行時間合併並排序
取出一個用戶的數據,用於測試操作:
tmp = df.groupby("uid").get_group('B')
tmp
結果:
uid | start | end | |
---|---|---|---|
4 | B | 2 | 3 |
5 | B | 4 | 7 |
6 | B | 10 | 11 |
7 | B | 6 | 8 |
8 | B | 12 | 15 |
觀察發現,要解決這個問題,我們首先需要對數據按照開始時間排序。
排序後:
tmp = tmp.sort_values('start')
tmp
結果:
uid | start | end | |
---|---|---|---|
4 | B | 2 | 3 |
5 | B | 4 | 7 |
7 | B | 6 | 8 |
6 | B | 10 | 11 |
8 | B | 12 | 15 |
觀察排序後的數據,我們就能很快的觀察出合併的規則:
當前遊覽記錄的的起始時間小於等於上一條記錄的結束時間時就進行合併,非常簡單:
result = []
for uid, start, end in tmp.values:
# 如果結果集中還沒有數據或者當前記錄的起始時間大於上一條記錄的結束時間
# 就可以直接將當前記錄加入到結果集
if not result or start > result[-1][2]:
result.append([uid, start, end])
else:
# 否則,說明可以將當前記錄與上一條記錄合併
# 合併方法是如果當前記錄的結束時間大於上一條記錄的結束時間,
# 則上一條記錄的結束時間修改爲當前記錄的結束時間
result[-1][2] = max(result[-1][2], end)
tmp = pd.DataFrame(result, columns=["uid", "start", "end"])
tmp
結果:
uid | start | end | |
---|---|---|---|
0 | B | 2 | 3 |
1 | B | 4 | 8 |
2 | B | 10 | 11 |
3 | B | 12 | 15 |
完整代碼
然後我們整理一下完整的處理代碼:
result = []
for uid, tmp in df.groupby("uid"):
tmp = tmp[["start", "end"]].sort_values('start')
rows = []
for start, end in tmp.values:
if not rows or start > rows[-1][2]:
rows.append([uid, start, end])
else:
rows[-1][2] = max(rows[-1][2], end)
tmp = pd.DataFrame(rows, columns=["uid", "start", "end"])
result.append(tmp)
result = pd.concat(result)
result
結果:
uid | start | end | |
---|---|---|---|
0 | A | 1 | 2 |
1 | A | 3 | 7 |
2 | A | 8 | 9 |
0 | B | 2 | 3 |
1 | B | 4 | 8 |
2 | B | 10 | 11 |
3 | B | 12 | 15 |
0 | C | 14 | 15 |
好了,完結,撒花!