У меня есть DataFrame, который содержит имя, год, тег и множество других переменных. Таким образом, это может выглядеть так
df = pd.DataFrame({
"name": 4*["A"] + 5*["B"],
"year": [1999,2000,2001,2002,2010,2011,2012,2013,2014],
"tag": [0,1,0,0,1,0,0,1,0],
"x1": np.random.normal(size=9),
"x2": np.random.uniform(size=9)
})
print df
name tag x1 x2 year
0 A 0 -1.352707 0.932559 1999
1 A 1 -1.359828 0.724635 2000
2 A 0 1.289980 0.477135 2001
3 A 0 -0.409960 0.863443 2002
4 B 1 -1.469220 0.324349 2010
5 B 0 0.372617 0.871734 2011
6 B 0 -0.047398 0.307596 2012
7 B 1 1.240108 0.667082 2013
8 B 0 0.558432 0.284363 2014
Я ищу способ сгруппировать или разделить DataFrame на куски, где каждый кусок должен содержать
Simpy put, я хочу куски размером 3, где средний ряд отмечен и окружен двумя непомеченными рядами одной и той же компании. Таким образом, в приведенном выше примере только два куска, которые проходят эти условия,
name tag x1 x2 year
0 A 0 -1.352707 0.932559 1999
1 A 1 -1.359828 0.724635 2000
2 A 0 1.289980 0.477135 2001
а также
7 B 0 -0.047398 0.307596 2012
8 B 1 1.240108 0.667082 2013
9 B 0 0.558432 0.284363 2014
Я подумал о группировке по нескольким столбцам, но проблема в том, что строки, которые мне нужно группировать, не имеют ничего общего, кроме имени. Я также подумал о том, чтобы вручную ввести (в цикле for) другой столбец, который дает каждому фрагменту новый идентификатор, который я мог бы затем группировать. Однако я очень недоволен этим подходом, поскольку он не кажется ни эффективным, ни элегантным.
Буду признателен за любые идеи.
Попробуем этот бит логики:
df = pd.DataFrame({
"name": 4*["A"] + 5*["B"],
"year": [1999,2000,2001,2002,2010,2011,2012,2013,2014],
"tag": [0,1,0,0,1,0,0,1,0],
"x1": np.random.normal(size=9),
"x2": np.random.uniform(size=9)
})
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
Где chunks_df - словарь вашего разбитого кадра данных:
chunks_df[('A', 1.0)]
name year tag x1 x2
0 A 1999 0 -0.015852 0.553314
1 A 2000 1 0.367290 0.245546
2 A 2001 0 0.605592 0.524358
chunks_df[('B', 3.0)]
name year tag x1 x2
6 B 2012 0 -0.750010 0.432032
7 B 2013 1 -0.682009 0.971042
8 B 2014 0 1.066113 0.179048
Подробности:
Хотя ответ @ScottBoston отлично подходит для DataFrame, который я дал в вопросе, он не работает в тех случаях, когда отсутствует год. Так, например, в случае
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).
Проблемы с двумя мужчинами
Итак, теперь я общаюсь следующим образом
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
Просто подумал, что я должен добавить это, если кто-то в будущем задается вопросом, почему принятый ответ не работает для аналогичной проблемы.
DataFrame
?