我有一个数据框,其中某些日期相同。为了说明这个问题,我创建了一个具有相同日期的示例df。
df = pd.DataFrame({"column1": range(6),
"column2": range(6),
'group': 3*['A','B'],
'date':pd.date_range("20190101", periods=6)})
df.loc[:,'date']=df.loc[0,'date']
df
# Output of DF
column1 column2 group date
0 0 0 A 2019-01-01
1 1 1 B 2019-01-01
2 2 2 A 2019-01-01
3 3 3 B 2019-01-01
4 4 4 A 2019-01-01
5 5 5 B 2019-01-01
在datetime列上执行groupby滚动操作时会出现问题:索引未保留。如果日期相同,这是个问题,因为无法合并回原始数据框(这是我的目标)。
df.groupby('group').rolling('2D',on='date')['column1'].sum()
# Output of Groupby Rolling
group date
A 2019-01-01 0.0
2019-01-01 2.0
2019-01-01 6.0
B 2019-01-01 1.0
2019-01-01 4.0
2019-01-01 9.0
Name: column1, dtype: float64
我有一个可行的替代解决方案,但是它慢得多。
df.groupby('group').apply(lambda x: x.rolling('2D',on='date')['column1'].sum())
# Output of Groupby Apply Rolling
group
A 0 0.0
2 2.0
4 6.0
B 1 1.0
3 4.0
5 9.0
Name: column1, dtype: float64
希望获得比上述效率更高的东西。
答案 0 :(得分:0)
您可以使用.reset_index()
,然后将该index
列作为其余部分与.groupby
和.agg
的转售。我想这会比lambda x快得多。
df = pd.DataFrame({"column1": range(6),
"column2": range(6),
'group': 3*['A','B'],
'date':pd.date_range("20190101", periods=6)})
df = df.reset_index().groupby('group').rolling('5D',on='date').agg({'index' : 'max', 'column1' : 'sum'}))
df
index column1
group date
A 2019-01-01 0.0 0.0
2019-01-03 2.0 2.0
2019-01-05 4.0 6.0
B 2019-01-02 1.0 1.0
2019-01-04 3.0 4.0
2019-01-06 5.0 9.0
从那里开始,如果您希望最终输出的格式不带日期,则可以执行以下操作:
df = df.reset_index().groupby(['group','index'])['column1'].sum()
group index
A 0.0 0.0
2.0 2.0
4.0 6.0
B 1.0 1.0
3.0 4.0
5.0 9.0
答案 1 :(得分:0)
对于那些感兴趣的人,我创建了一个更复杂的df示例来测试上面提出的每种解决方案的效率。
我最初的方法(这里是最慢的方法,但是如果组很少,则是有效的):
%%timeit
df = pd.DataFrame({"column1": range(600),
"column2": range(600),
"column3": range(600),
"column4": range(600),
"column5": range(600),
"column6": range(600),
"column7": range(600),
"column8": range(600),
'group': 5*['l'+str(i) for i in range(120)],
'date':pd.date_range("20190101", periods=600)})
### Set the date the same
df.loc[:,'date']=df.loc[0,'date']
cols = ['column1','column2','column3','column4','column5','column6','column7','column8']
newcols = ['col1','col2','col3','col4','col5','col6','col7','col8']
if newcols[0] not in df.columns:
df = df.reindex(columns=df.columns.tolist()+newcols)
df[newcols]=df.groupby('group').apply(lambda x: x.rolling('2D',on='date')[cols].sum()
).sort_index(level=1).drop('date',axis=1).values
# timeit output
345 ms ± 28 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
David Erikson的解决方案。如果每个组中有很多组且观测值较少,那么这样做会很有效。
%%timeit
df = pd.DataFrame({"column1": range(600),
"column2": range(600),
"column3": range(600),
"column4": range(600),
"column5": range(600),
"column6": range(600),
"column7": range(600),
"column8": range(600),
'group': 5*['l'+str(i) for i in range(120)],
'date':pd.date_range("20190101", periods=600)})
### Set the date the same
df.loc[:,'date']=df.loc[0,'date']
cols = ['column1','column2','column3','column4','column5','column6','column7','column8']
newcols = ['col1','col2','col3','col4','col5','col6','col7','col8']
if newcols[0] not in df.columns:
df = df.reindex(columns=df.columns.tolist()+newcols)
my_dict = {}
my_dict["index"] = "max"
my_dict.update(dict.fromkeys(cols, "sum"))
df[newcols]=df.reset_index().groupby('group').rolling('2D',
on='date').agg(my_dict).sort_values('index').drop('index',axis=1).values
# timeit output
110 ms ± 11.5 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
RichieV提出的最快解决方案:
%%timeit
df = pd.DataFrame({"column1": range(600),
"column2": range(600),
"column3": range(600),
"column4": range(600),
"column5": range(600),
"column6": range(600),
"column7": range(600),
"column8": range(600),
'group': 5*['l'+str(i) for i in range(120)],
'date':pd.date_range("20190101", periods=600)})
### Set the date the same
df.loc[:,'date']=df.loc[0,'date']
cols = ['column1','column2','column3','column4','column5','column6','column7','column8']
newcols = ['col1','col2','col3','col4','col5','col6','col7','col8']
if newcols[0] not in df.columns:
df = df.reindex(columns=df.columns.tolist()+newcols)
df=df.sort_values(['group','date'],kind='mergesort').reset_index(drop=True)
df[newcols]=df.groupby('group').rolling('2D',on='date')[cols].sum().values
df=df.sort_values('column1',kind='mergesort').reset_index(drop=True)
# timeit output
40 ms ± 6.41 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)