Хотя ответ @ ScottBoston отлично работает с кадром данных, который я дал в этом вопросе, он не работает в тех случаях, когда пропущен год.Так, например, в случае
df = pd.DataFrame({
"name": 4*["A"] + 6*["B"],
"year": [1999,2000,2001,2002,2008,2010,2011,2012,2013,2014],
"tag": [0,1,0,0,0,1,0,0,1,0],
"x1": np.random.normal(size=10),
"x2": np.random.uniform(size=10)
})
print df
name tag x1 x2 year
0 A 0 -0.387840 0.729721 1999
1 A 1 -0.112094 0.813332 2000
2 A 0 0.913186 0.115521 2001
3 A 0 -1.088056 0.983111 2002
4 B 0 0.037521 0.743706 2008
5 B 1 0.602878 0.007256 2010
6 B 0 -0.340498 0.961602 2011
7 B 0 0.170654 0.293789 2012
8 B 1 0.973555 0.942687 2013
9 B 0 -0.643503 0.133091 2014
код будет давать
grp = df.groupby(['name',
df.tag.cumsum().rolling(3, center=True, min_periods=1).max()])
chunks_df = {}
for n, g in grp:
if g.shape[0] >= 3:
chunks_df[n] = g
print n
print g, "\n"
('A', 1.0)
name tag x1 x2 year
0 A 0 -0.387840 0.729721 1999
1 A 1 -0.112094 0.813332 2000
2 A 0 0.913186 0.115521 2001
3 A 0 -1.088056 0.983111 2002
('B', 2.0)
name tag x1 x2 year
4 B 0 0.037521 0.743706 2008
5 B 1 0.602878 0.007256 2010
6 B 0 -0.340498 0.961602 2011
('B', 3.0)
name tag x1 x2 year
7 B 0 0.170654 0.293789 2012
8 B 1 0.973555 0.942687 2013
9 B 0 -0.643503 0.133091 2014
, что показывает, что размер первого блока неверен, а второго блока не должно быть в соответствии свторое условие в исходном вопросе (годы 2008, 2010 и 2011).
Двумя проблемами являются
- Вопрос явно оставляет открытой возможность для строки находиться вболее одного куска, поэтому одного дополнительного индекса обычно не может быть достаточно.
- Необходимо включить условие для лет, поэтому скользящий расчет должен выполняться по двум столбцам (тег и год) одновременно,который в настоящее время не поддерживается пандами в соответствии с https://stackoverflow.com/a/37491779/2336654.
Так что мой обходной путь теперь следующий:
def rolling(df, func, window_size=3):
dxl = int(window_size/2)
if window_size % 2 == 0:
dxu = dxl
else:
dxu = dxl+1
xmin = dxl
xmax = len(df)-dxu+1
for i in xrange(xmin,xmax):
chunk = df.iloc[i-dxl:i+dxu,:]
if func(chunk):
yield chunk
def valid(chunk):
if len(chunk.name.value_counts()) != 1:
return False
if chunk.tag.iloc[1] != 1:
return False
if chunk.year.iloc[2]-chunk.year.iloc[0] != 2:
return False
return True
new_df = pd.DataFrame()
for ichunk, chunk in enumerate(rolling(df, window_size=3, func=valid)):
new_df = new_df.append(chunk.assign(new_tag=ichunk), ignore_index=True)
for name, g in new_df.groupby(["name","new_tag"]):
print name
print g,"\n"
('A', 0)
name tag x1 x2 year new_tag
0 A 0 -1.046241 0.692206 1999 0
1 A 1 0.373060 0.919130 2000 0
2 A 0 1.316474 0.463517 2001 0
('B', 1)
name tag x1 x2 year new_tag
3 B 0 0.376408 0.743188 2012 1
4 B 1 0.019062 0.647851 2013 1
5 B 0 -0.442368 0.506169 2014 1
Просто подумал, что я должен добавить это на тот случай, если кто-то в будущем будет удивлятьсяпочему принятый ответ не работает для аналогичной проблемы.