![](/img/trans.png)
[英]How to effciently calculate non-consecutive number of appereances of element in list?
[英]Efficiently calculate non-consecutive number of appereances of element in data frame
考慮以下數據框
Value
time
2020-02-14 14:16:10.769999872+00:00 74
2020-02-14 14:16:11.360999936+00:00 74
2020-02-14 14:16:11.970000128+00:00 72
2020-02-14 14:16:12.637000192+00:00 72
2020-02-14 14:16:13.210000128+00:00 74
... ...
2020-02-28 08:15:20.340000+00:00 71
2020-02-28 08:15:20.890000128+00:00 71
2020-02-28 08:15:21.424000+00:00 71
2020-02-28 08:15:22.032999936+00:00 72
2020-02-28 08:15:22.594000128+00:00 72
我希望我的代碼遍歷值,找到每個值的開始索引和結束索引,並將這些信息保存到字典中。
results = {74: {start:2020-02-14 14:16:10.769999872+00:00, end:2020-02-14 14:16:11.360999936+00:00},
72: {start: ..., end: ...},
...}
因為這很簡單,棘手的部分是一個或多個值可能以非連續的方式出現多次: 74, 74, 72, 72, 72, 74, 74, 74, 71, 71, 71, 72, 72, 71, 71
。
如果是這種情況,則應為每個 Value 生成一個包含開始和結束索引的新序列。
results = {74:
{Sequence1: {start:2020-02-14 14:16:10.769999872+00:00, end:2020-02-14 14:16:11.360999936+00:00},
Sequence2: {start: ... , end: ...}},
72:
{Sequence1: {start: ..., end: ...},
Seqeunce2: {start: ..., end: ...},
Sequence3: {start: ..., end: ...}},
71: ...,
}
當然,我可以用很多 for 循環來編寫代碼,但我想知道是否有更簡潔、更聰明的解決方案可以讓我省心。 也許最重要的是代碼運行速度快是至關重要的。 數據框大約有 300.000 行。
這可以分兩部分完成。 第一個包括發現連續組。 第二個是找到每個組的最小/最大時間。
要查找組,您可以使用此處描述的解決方案。 這是適用於您的情況的解決方案:
groups = (df.Value != df.Value.shift()).cumsum()
然后你可以應用多個groupby
來查找開始和結束日期。 但是,使用agg
有一種更有效和直接的方法來做到這一點:
result = df.groupby(groups).agg(Value=('Value',min), startTime=('time',min), endTime=('time',max))
最后,如果你想要一個字典,你可以迭代結果數據幀。
這是經過測試的輸入:
time Value
0 2020-02-14 14:16:10.769999872+00:00 74
1 2020-02-14 14:16:11.360999936+00:00 74
2 2020-02-14 14:16:11.970000128+00:00 72
3 2020-02-14 14:16:12.637000192+00:00 72
4 2020-02-14 14:16:13.210000128+00:00 74
5 2020-02-28 08:15:20.340000+00:00 71
6 2020-02-28 08:15:20.890000128+00:00 71
7 2020-02-28 08:15:21.424000+00:00 71
8 2020-02-28 08:15:22.032999936+00:00 72
9 2020-02-28 08:15:22.594000128+00:00 72
這是輸出:
Value startTime endTime
Value
1 74 2020-02-14 14:16:10.769999872+00:00 2020-02-14 14:16:11.360999936+00:00
2 72 2020-02-14 14:16:11.970000128+00:00 2020-02-14 14:16:12.637000192+00:00
3 74 2020-02-14 14:16:13.210000128+00:00 2020-02-14 14:16:13.210000128+00:00
4 71 2020-02-28 08:15:20.340000+00:00 2020-02-28 08:15:21.424000+00:00
5 72 2020-02-28 08:15:22.032999936+00:00 2020-02-28 08:15:22.594000128+00:00
請注意,我使用編碼為字符串的輸入日期進行了測試,這應該沒問題,因為它們是根據 ISO 8601 表示的。
我假設該索引實際上是一個DatetimeIndex 。 如果不是,請轉換它。
要完成您的任務,請從定義要應用於每組行的函數開始:
def fn(grp):
tMin = grp.index.min()
tMax = grp.index.max()
v = grp.Value.iloc[0]
return pd.Series([v, tMin, tMax], index=['val', 'start', 'end'])
然后,它適用於每個組具有相同的價值(價值變動將打開一個新組)行:
df2 = df.groupby([(df.Value != df.Value.shift()).cumsum()])\
.apply(fn).reset_index(drop=True)
下一步是生成一個帶有Sequence...內容的列(首先只有一個數字,然后將其轉換為字符串):
df2['Seq'] = df2.groupby('val').cumcount() + 1
df2['Seq'] = 'Sequence' + df2['Seq'].astype(str)
要計算最終結果,請運行:
result = {}
for key, grp in gr:
result[key] = grp.set_index('Seq')[['start', 'end']].to_dict(orient='index')
對於您的示例數據,結果是:
{71: {'Sequence1': {'start': Timestamp('2020-02-28 08:15:20.340000+0000', tz='UTC'),
'end': Timestamp('2020-02-28 08:15:21.424000+0000', tz='UTC')}},
72: {'Sequence1': {'start': Timestamp('2020-02-14 14:16:11.970000128+0000', tz='UTC'),
'end': Timestamp('2020-02-14 14:16:12.637000192+0000', tz='UTC')},
'Sequence2': {'start': Timestamp('2020-02-28 08:15:22.032999936+0000', tz='UTC'),
'end': Timestamp('2020-02-28 08:15:22.594000128+0000', tz='UTC')}},
74: {'Sequence1': {'start': Timestamp('2020-02-14 14:16:10.769999872+0000', tz='UTC'),
'end': Timestamp('2020-02-14 14:16:11.360999936+0000', tz='UTC')},
'Sequence2': {'start': Timestamp('2020-02-14 14:16:13.210000128+0000', tz='UTC'),
'end': Timestamp('2020-02-14 14:16:13.210000128+0000', tz='UTC')}}}
請注意,保存在開始或結束鍵下的每個值都是一個實際的時間戳。 它也可以是一個普通字符串,但我認為此內容更易於進一步處理。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.