Python 使用Groupby标识数据帧中连续的相同值
我有以下数据帧df:Python 使用Groupby标识数据帧中连续的相同值,python,pandas,numpy,lambda,Python,Pandas,Numpy,Lambda,我有以下数据帧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
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
迫不及待地想看看我错过了什么!谢谢你可以试试这个;1) 使用df.value.diff().ne(0.cumsum()
创建一个额外的组变量来表示值的更改;2) 使用transform('size')
计算组大小并与三个值进行比较,然后得到所需的标志列:
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
:
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
列相结合,您可以对数据帧进行分组,计算组大小并获得标志
列。有关更可靠的解决方案,请参见EDIT2
同样的结果,但要快一点:
labels=(df.value!=df.value.shift()).cumsum()
df['flag']=(labels.map(labels.value_counts())>=3.astype(int)
id值标志
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\u counts()
统计每个标签的出现次数
labels.map(…)
用上面计算的计数替换标签
=3
在计数值上创建布尔掩码
astype(int)
将布尔值强制转换为int
在我的手中,它给你的df 1.03毫秒,相比之下,Psidoms的方法是2.1毫秒。
但我的不是一艘班轮
编辑:
两种方法的混合甚至更快
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
编辑3:更好的解释
假设索引10的值为3<代码>df.id.diff().ne(0)
data={'id':[1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2],
“值”:[2,2,3,2,2,3,3,3,3,4,1,1,1,4,4,1,1,1,1]}
df=pd.DataFrame.from_dict(数据)
df['id_diff']=df.id.diff().ne(0).astype(int)
df['val_diff']=df.value.diff().ne(0).astype(int)
df['diff_或']=(df.id.diff().ne(0)| df.value.diff().ne(0)).astype(int)
df['labels']=df['diff_或'].cumsum()
id值id_diff val_diff diff_或标签
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#试试这个更简单的版本
a=pd.系列([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)惊人的速度~+1@Wen谢谢你,这是一个非常好的回答。你怎么回答得这么快?工作完美。你能解释一下吗?我理解.diff但是.ne(0)之后会发生什么?@clg4提示:大多数时候新问题根本不是新问题,试着将它转换为旧问题,例如,这个问题你只需要找到获取groupid的方法。@Psidom:很好的方法!由于OP要求效率,我将您的解决方案与我自己的解决方案混合在一起,并获得了很好的加速。仅供参考。你确定你的产出吗?我不相信这会奏效,没有分组。如果索引10处的值为3,这将不起作用。。。我想…@clg4:你说得很对。谢谢你指出这一点!编辑答案以得到有效的解决方案。我会被诅咒的。。。这管子是干什么的?巫术。。。很难理解,如果ID改变,为什么会增加@clg4:我添加了更多关于管道技巧工作原理的解释
#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
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)