Pandas:在另一列中创建包含“next”更改值的列

2024-06-01 10:22:13 发布

您现在位置:Python中文网/ 问答频道 /正文

我想从没有for循环的B列创建C列。。。你知道吗

数据帧:

# |  A  |  B |  C  
--+-----+----+-----
1 |  2  |  3 |  4
2 |  3  |  3 |  4
3 |  4  |  4 |  6
4 |  5  |  4 |  6
5 |  5  |  4 |  6
6 |  3  |  6 |  2
7 |  2  |  6 |  2
8 |  4  |  2 |  3  #< --- loop back around if possible (B value at index 1)

基本上,我想得到B中下一个变化的值,并将其设置为一个新的列C

到目前为止,答案是:Determining when a column value changes in pandas dataframe 我有:

df_filtered = df[df['B'].diff() != 0]

但在那之后,我不知道如何在不使用循环的情况下创建C。。。你知道吗

编辑: @(Ayoub ZAROU)的回答回答了我最初的问题,但是,我注意到如果我们假设数据中有一个循环,我的示例dataframe并不能涵盖所有情况:

# |  A  |  B |  C  
--+-----+----+-----
1 |  2  |  3 |  4
2 |  3  |  3 |  4
3 |  4  |  4 |  6
4 |  5  |  4 |  6
5 |  5  |  4 |  6
6 |  3  |  6 |  2
7 |  2  |  6 |  2
8 |  4  |  2 |  3
9 |  3  |  3 |  4
10|  2  |  3 |  4

在这种情况下,如果将3的最后一段视为3的第一段的一部分,则使用此解决方案C中的最后两个值将不正确。你知道吗

不过,一个简单的解决方法是将最后几个元素移到列表的开头,反之亦然


Tags: 数据答案loopdataframedfforindexif
3条回答

shiftB,groupby on originaldf.B。最后,transformfillna

df.B.shift(-1).groupby([df.B]).transform('last').fillna(df.at[1,'B'])

Out[22]:
1    4.0
2    4.0
3    6.0
4    6.0
5    6.0
6    2.0
7    2.0
8    3.0
Name: B, dtype: float64

您还可以将np.rollassign一起使用,在C上创建列C和groupby Btransform。这样做,您不需要shiftfillna

df.assign(C=np.roll(df.B,-1)).groupby('B').C.transform('last')

Out[36]:
1    4
2    4
3    6
4    6
5    6
6    2
7    2
8    3
Name: C, dtype: int64

你可以试试看,注意np.roll和pandas中的shift是一样的,唯一的区别是它允许你滚动值, 在下面,c给出了没有变化的索引

c = (df.B.diff(-1) == 0)

c
Out[104]: 
0     True
1    False
2     True
3     True
4    False
5     True
6    False
7    False
Name: B, dtype: bool

然后我们将那里的值设置为B列上的下一个值,使用np.roll^{}进行设置,注意,如果更改列c不是True

df['C'] = np.nan
df['C'] = df.C.where(c, np.roll(df.B, -1))
df.C

Out[107]: 
0    NaN
1    4.0
2    NaN
3    NaN
4    6.0
5    NaN
6    2.0
7    3.0
Name: C, dtype: float64

然后我们在pandas上使用bfill填充剩余的行,并将其转换为B'列dtype, 所以,在全球范围内,你是这样做的

c = (df.B.diff(-1) == 0)
df['C'] = np.nan
df['C'] = df.C.where(c, np.roll(df.B, -1)).bfill().astype(df.B.dtype)

df.C
Out[110]: 
0    4
1    4
2    6
3    6
4    6
5    2
6    2
7    3
Name: C, dtype: int32

另一种方法是获取值更改:

In [11]: changes = (df.B != df.B.shift()).cumsum()

In [12]: changes
Out[12]:
0    1
1    1
2    2
3    2
4    2
5    3
6    3
7    4
Name: B, dtype: int64

以及查找映射:

In [13]: lookup = df.B[(df.B != df.B.shift())]

In [14]: lookup.at[len(lookup)] = df.B.iloc[0]

In [15]: lookup
Out[15]:
0    3
2    4
5    6
7    2
4    3
Name: B, dtype: int64

然后使用这些来查找“下一个”:

In [16]: lookup.iloc[changes]
Out[16]:
2    4
2    4
5    6
5    6
5    6
7    2
7    2
4    3
Name: B, dtype: int64

要创建列,需要忽略索引中的重复项:

In [17]: df["C"] = lookup.iloc[changes].values

相关问题 更多 >