我有一个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分组或拆分为块的方法,每个块应包含
简单地说,我想要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循环中)引入另一列,该列为每个块提供一个新的ID,然后可以对其进行分组。但是,我对这种方法不满意,因为它既不高效也不优雅。
我将不胜感激。
答案 0 :(得分:3)
让我们尝试一下这种逻辑:
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
详细信息:
答案 1 :(得分:1)
尽管@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
只是以为我应该添加此内容,以防将来任何人想知道为什么接受的答案不能解决类似问题。