我有以下数据框df:
data={'id':[1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2],
'value':[2,2,3,2,2,2,3,3,3,3,1,4,1,1,1,4,4,1,1,1,1,1]}
df=pd.DataFrame.from_dict(data)
df
Out[8]:
id value
0 1 2
1 1 2
2 1 3
3 1 2
4 1 2
5 1 2
6 1 3
7 1 3
8 1 3
9 1 3
10 2 1
11 2 4
12 2 1
13 2 1
14 2 1
15 2 4
16 2 4
17 2 1
18 2 1
19 2 1
20 2 1
21 2 1
我需要做的是在id级别(df.groupby [' id'])中识别,当值连续显示相同的数字3次或更多次时。
我希望以上结果如下:
df
Out[12]:
id value flag
0 1 2 0
1 1 2 0
2 1 3 0
3 1 2 1
4 1 2 1
5 1 2 1
6 1 3 1
7 1 3 1
8 1 3 1
9 1 3 1
10 2 1 0
11 2 4 0
12 2 1 1
13 2 1 1
14 2 1 1
15 2 4 0
16 2 4 0
17 2 1 1
18 2 1 1
19 2 1 1
20 2 1 1
21 2 1 1
我尝试使用pandas rolling.mean来测试groupby和lambda的变体,以确定滚动周期的平均值然后与“'值”进行比较,并且它们是相同的,这表示标记。但是这有几个问题,包括你可能有不同的值,它们将平均值到你想要标记的值。另外,我无法弄清楚如何标记'滚动的所有值都意味着创建了初始标志。看到这里,这标识了右侧'标志,但后来我需要填写滚动平均长度的先前值。请在此处查看我的代码:
test=df.copy()
test['rma']=test.groupby('id')['value'].transform(lambda x: x.rolling(min_periods=3,window=3).mean())
test['flag']=np.where(test.rma==test.value,1,0)
结果在这里:
test
Out[61]:
id value rma flag
0 1 2 NaN 0
1 1 2 NaN 0
2 1 3 2.333333 0
3 1 2 2.333333 0
4 1 2 2.333333 0
5 1 2 2.000000 1
6 1 3 2.333333 0
7 1 3 2.666667 0
8 1 3 3.000000 1
9 1 3 3.000000 1
10 2 1 NaN 0
11 2 4 NaN 0
12 2 1 2.000000 0
13 2 1 2.000000 0
14 2 1 1.000000 1
15 2 4 2.000000 0
16 2 4 3.000000 0
17 2 1 3.000000 0
18 2 1 2.000000 0
19 2 1 1.000000 1
20 2 1 1.000000 1
21 2 1 1.000000 1
不能等着看我错过了什么!感谢
答案 0 :(得分:17)
你可以试试这个; 1)使用df.value.diff().ne(0).cumsum()
创建一个额外的组变量来表示值的变化; 2)使用transform('size')
计算组大小并与3进行比较,然后获得所需的flag
列:
df['flag'] = df.value.groupby([df.id, df.value.diff().ne(0).cumsum()]).transform('size').ge(3).astype(int)
df
分解:
1) diff
不等于零(字面意思是df.value.diff().ne(0)
的含义)只要值发生变化就会给出条件True
:< / p>
df.value.diff().ne(0)
#0 True
#1 False
#2 True
#3 True
#4 False
#5 False
#6 True
#7 False
#8 False
#9 False
#10 True
#11 True
#12 True
#13 False
#14 False
#15 True
#16 False
#17 True
#18 False
#19 False
#20 False
#21 False
#Name: value, dtype: bool
2)然后cumsum
给出一个非降序的id序列,其中每个id表示一个具有相同值的连续块,注意在对布尔值求和时,True
被视为一个False
被视为零:
df.value.diff().ne(0).cumsum()
#0 1
#1 1
#2 2
#3 3
#4 3
#5 3
#6 4
#7 4
#8 4
#9 4
#10 5
#11 6
#12 7
#13 7
#14 7
#15 8
#16 8
#17 9
#18 9
#19 9
#20 9
#21 9
#Name: value, dtype: int64
3)结合id
列,您可以对数据框进行分组,计算组大小并获取flag
列。
答案 1 :(得分:2)
#try this simpler version
a= pd.Series([1,1,1,2,3,4,5,5,5,7,8,0,0,0])
b= a.groupby([a.ne(0), a]).transform('size').ge(3).astype('int')
#ge(x) <- x is the number of consecutive repeated values
print b
答案 2 :(得分:1)
请参阅EDIT2以获得更强大的解决方案
结果相同,但速度要快一点:
labels = (df.value != df.value.shift()).cumsum()
df['flag'] = (labels.map(labels.value_counts()) >= 3).astype(int)
id value flag
0 1 2 0
1 1 2 0
2 1 3 0
3 1 2 1
4 1 2 1
5 1 2 1
6 1 3 1
7 1 3 1
8 1 3 1
9 1 3 1
10 2 1 0
11 2 4 0
12 2 1 1
13 2 1 1
14 2 1 1
15 2 4 0
16 2 4 0
17 2 1 1
18 2 1 1
19 2 1 1
20 2 1 1
21 2 1 1
其中:
df.value != df.value.shift()
给出了值更改cumsum()
为每个具有相同值的组创建“标签”labels.value_counts()
计算每个标签的出现次数labels.map(...)
用上面计算的计数替换标签>= 3
在计数值astype(int)
将布尔值转换为int 在我的手中,你的df为1.03ms,而Psidoms的方法为2.1ms。 但我的不是单行。
编辑:
两种方法之间的混合更快
labels = df.value.diff().ne(0).cumsum()
df['flag'] = (labels.map(labels.value_counts()) >= 3).astype(int)
使用样品df给出911μs。
EDIT2:正确的解决方案来解释id更改,正如@ clg4
所指出的那样labels = (df.value.diff().ne(0) | df.id.diff().ne(0)).cumsum()
df['flag'] = (labels.map(labels.value_counts()) >= 3).astype(int)
... | df.id.diff().ne(0)
增加id更改的标签
这甚至在id更改时使用相同的值(在索引10上使用值3进行测试)并且需要1.28ms
EDIT3:更好的解释
假设索引10的值为3 df.id.diff().ne(0)
data={'id':[1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2],
'value':[2,2,3,2,2,2,3,3,3,3,3,4,1,1,1,4,4,1,1,1,1,1]}
df=pd.DataFrame.from_dict(data)
df['id_diff'] = df.id.diff().ne(0).astype(int)
df['val_diff'] = df.value.diff().ne(0).astype(int)
df['diff_or'] = (df.id.diff().ne(0) | df.value.diff().ne(0)).astype(int)
df['labels'] = df['diff_or'].cumsum()
id value id_diff val_diff diff_or labels
0 1 2 1 1 1 1
1 1 2 0 0 0 1
2 1 3 0 1 1 2
3 1 2 0 1 1 3
4 1 2 0 0 0 3
5 1 2 0 0 0 3
6 1 3 0 1 1 4
7 1 3 0 0 0 4
8 1 3 0 0 0 4
9 1 3 0 0 0 4
>10 2 3 1 | 0 = 1 5 <== label increment
11 2 4 0 1 1 6
12 2 1 0 1 1 7
13 2 1 0 0 0 7
14 2 1 0 0 0 7
15 2 4 0 1 1 8
16 2 4 0 0 0 8
17 2 1 0 1 1 9
18 2 1 0 0 0 9
19 2 1 0 0 0 9
20 2 1 0 0 0 9
21 2 1 0 0 0 9
|
是运算符“按位 - 或”,只要其中一个元素为True
,就会给出True
。因此,如果id更改的值没有diff,则|
会反映id更改。否则它什么都没改变。
执行.cumsum()
时,标签会在id更改的位置递增,因此索引10处的值3
不会与索引6-9中的值3
分组。
答案 3 :(得分:0)
df=pd.DataFrame.from_dict(
{'id':[1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2],
'value':[2,2,3,2,2,2,3,3,3,3,1,4,1,1,1,4,4,1,1,1,1,1]})
df2 = df.groupby((df['value'].shift() != df['value']).\
cumsum()).filter(lambda x: len(x) >= 3)
df['flag'] = np.where(df.index.isin(df2.index),1,0)