Nói chung, nếu ngày là hoàn toàn tùy ý, tôi nghĩ bạn sẽ buộc phải sử dụng một Python for-loop
qua các hàng hoặc use df.apply
, (mà dưới mui xe, cũng sử dụng một vòng lặp Python.)
Tuy nhiên, nếu Ngày của bạn chia sẻ tần suất chung, như trường hợp trên, thì có một mẹo cần nhanh hơn nhiều so với sử dụng df.apply
: Mở rộng bộ đếm thời gian theo tần suất chung - trong trường hợp này, 1 phút - điền vào các NaN với số không, và sau đó gọi rolling_sum
:
In [279]: pd.rolling_sum(df.set_index(['Date']).asfreq('1T').fillna(0), window=5, min_periods=1).reindex(df['Date'])
Out[279]:
A
Date
2014-11-21 11:00:00 1
2014-11-21 11:03:00 5
2014-11-21 11:04:00 6
2014-11-21 11:05:00 7
2014-11-21 11:07:00 11
2014-11-21 11:08:00 8
2014-11-21 11:12:00 2
2014-11-21 11:13:00 3
Tất nhiên, bất kỳ chuỗi thời gian nào có tần suất chung nếu bạn sẵn sàng chấp nhận mức độ chi tiết đủ nhỏ, nhưng kích thước yêu cầu của df.asfreq(...)
có thể làm cho mẹo này không thực tế.
Dưới đây là ví dụ về cách tiếp cận tổng quát hơn sử dụng df.apply
. Lưu ý rằng việc gọi số searchsorted
phụ thuộc vào số df['Date']
đang được sắp xếp.
import numpy as np
import pandas as pd
df = pd.read_csv('data', parse_dates=[0], sep=',\s*')
start_dates = df['Date'] - pd.Timedelta(minutes=5)
df['start_index'] = df['Date'].values.searchsorted(start_dates, side='right')
df['end_index'] = np.arange(len(df))
def sum_window(row):
return df['A'].iloc[row['start_index']:row['end_index']+1].sum()
df['rolling_sum'] = df.apply(sum_window, axis=1)
print(df[['Date', 'A', 'rolling_sum']])
mang
Date A rolling_sum
0 2014-11-21 11:00:00 1 1
1 2014-11-21 11:03:00 4 5
2 2014-11-21 11:04:00 1 6
3 2014-11-21 11:05:00 2 7
4 2014-11-21 11:07:00 4 11
5 2014-11-21 11:08:00 1 8
6 2014-11-21 11:12:00 1 2
7 2014-11-21 11:13:00 2 3
Dưới đây là một chuẩn mực so sánh các trick df.asfreq
so với gọi df.apply
:
import numpy as np
import pandas as pd
df = pd.read_csv('data', parse_dates=[0], sep=',\s*')
def big_df(df):
df = df.copy()
for i in range(7):
dates = df['Date'] + pd.Timedelta(df.iloc[-1]['Date']-df.iloc[0]['Date']) + pd.Timedelta('1 minute')
df2 = pd.DataFrame({'Date': dates, 'A': df['A']})
df = pd.concat([df, df2])
df = df.reset_index(drop=True)
return df
def using_apply():
start_dates = df['Date'] - pd.Timedelta(minutes=5)
df['start_index'] = df['Date'].values.searchsorted(start_dates, side='right')
df['end_index'] = np.arange(len(df))
def sum_window(row):
return df['A'].iloc[row['start_index']:row['end_index']+1].sum()
df['rolling_sum'] = df.apply(sum_window, axis=1)
return df[['Date', 'rolling_sum']]
def using_asfreq():
result = (pd.rolling_sum(
df.set_index(['Date']).asfreq('1T').fillna(0),
window=5, min_periods=1).reindex(df['Date']))
return result
In [364]: df = big_df(df)
In [367]: %timeit using_asfreq()
1000 loops, best of 3: 1.21 ms per loop
In [368]: %timeit using_apply()
1 loops, best of 3: 208 ms per loop
Cảm ơn bạn rất nhiều vì câu trả lời nhanh. Tôi không thể sử dụng phương thức df.asfreq (...) vì độ chi tiết nhỏ nhất trong tập dữ liệu của tôi là giây và tôi có hàng triệu hàng. Nhưng phương pháp df.apply thực hiện thủ thuật. –
Cách tiếp cận chung hoạt động, chỉ một điều cần lưu ý nếu nó được sử dụng trong mã dài: hàm 'sum_window' không sử dụng đầu vào' df' như một cách rõ ràng vì vậy cần phải cẩn thận. – nilesh