我需要更新vid
或也许根据change
列创建一个新列
df = [{'vid': 14, 'change': 0}, {'vid': 15, 'change': 1}, {'vid': 16, 'change': 0}, {'vid': 16, 'change': 0}, {'vid': 17, 'change': 0}, {'vid': 17, 'change': 1}, {'vid': 18, 'change': 0}, {'vid': 18, 'change': 0}]
vid change
0 14 0
1 15 1
2 16 0
3 16 0
4 17 0
5 17 1
6 18 0
7 18 0
如果change == 1
,则应将下一组vid更改为当前值;如果change == 0
,则应将下一个vid保持不变。
在我上面的示例中,由于15的变化= 1,因此需要将vid 16更改为15,但是由于16的变化没有= 1,所以将17保留了
Change = 1仅在vid将在下一行更改时发生
预期产量
vid change
0 14 0
1 15 1
2 15 0
3 15 0
4 17 0
5 17 1
6 17 0
7 17 0
答案 0 :(得分:2)
假设数据帧的结构像我们可以做的示例shift
一样,然后创建映射dict
和replace
s=df.groupby('vid').change.max().eq(1)
df.vid=df.vid.replace(dict(zip(s[s.shift().fillna(False)].index,s.index[s])))
df
Out[65]:
vid change
0 14 0
1 15 1
2 15 0
3 15 0
4 17 0
5 17 1
6 17 0
7 17 0
答案 1 :(得分:2)
changes
在shift
发生的位置。 shift
的值vid
下移一行。 GroupBy.ffill
来获得所需的列:mask = df['change'].shift().eq(1)
df.loc[mask, 'new_vid'] = df['vid'].shift()
df['new_vid'] = df.groupby('vid')['new_vid'].ffill().fillna(df['vid'])
vid change new_vid
0 14 0 14.0
1 15 1 15.0
2 16 0 15.0
3 16 0 15.0
4 17 0 17.0
5 17 1 17.0
6 18 0 17.0
7 18 0 17.0
逐步
检查发生更改的地方:
df['change'].shift().eq(1)
0 False
1 False
2 True
3 False
4 False
5 False
6 True
7 False
Name: change, dtype: bool
创建一个新列,我们在其中移动vid
的值:
df.loc[mask, 'new_vid'] = df['vid'].shift()
vid change new_vid
0 14 0 NaN
1 15 1 NaN
2 16 0 15.0
3 16 0 NaN
4 17 0 NaN
5 17 1 NaN
6 18 0 17.0
7 18 0 NaN
最后Groupby
和forwardfill
:
df['new_vid'] = df.groupby('vid')['new_vid'].ffill().fillna(df['vid'])
vid change new_vid
0 14 0 14.0
1 15 1 15.0
2 16 0 15.0
3 16 0 15.0
4 17 0 17.0
5 17 1 17.0
6 18 0 17.0
7 18 0 17.0
答案 2 :(得分:2)
IIUC,您可以掩盖这些更改以及块,以及ffill
:
mask = (df['vid'].eq(df['vid'].shift()) # blocks
| df.change.eq(1).shift() # changes
)
df['vid'] = df['vid'].mask(mask).ffill().astype('int64')
输出:
vid change
0 14 0
1 15 1
2 15 0
3 15 0
4 17 0
5 17 1
6 17 0
7 17 0
答案 3 :(得分:1)
如果您不介意将所有替换数字重新编号为连续的,则可以通过减去shift
和cumsum
的变化来获得一个非常干净的版本:
In [59]: df['new_vid'] = df['vid'] - df['change'].shift(fill_value=0).cumsum()
In [60]: df
Out[60]:
vid change new_vid
0 14 0 14
1 15 1 15
2 16 0 15
3 16 0 15
4 17 0 16
5 17 1 16
6 18 0 16
7 18 0 16
具有原始编号的另一个选项是使用map
和groupby
:
In [81]: df['vid'] = df['vid'] - df['vid'].map(df.groupby("vid")['change'].max().shift(fill_value=0))
In [82]: df
Out[82]:
vid change
0 14 0
1 15 1
2 15 0
3 15 0
4 17 0
5 17 1
6 17 0
7 17 0
答案 4 :(得分:1)
那么,为什么不给你另一个选择。这显示了单个数据框中的每个转换步骤:
import pandas as pd
df = pd.read_clipboard()
def subtract_one(group):
return group['vid'] - 1
df['shifted_change'] = df['change'].shift(1)
df['desired_result'] = df.groupby('vid').apply(lambda x: subtract_one(x) if x['shifted_change'].any() == 1 else x['vid']).rename('temp').reset_index()['temp']
...这是结果
编辑
我已经分析了每个答案,以查看哪个答案最快。结果:
@兰迪是赢家!
..最后我死了,哈哈:)大家好!
以下是分析代码:
import pandas as pd
import timeit
df = pd.read_clipboard()
def zelusp(df):
def subtract_one(group):
return group['vid'] - 1
df['shifted_change'] = df['change'].shift(1)
df['desired_result'] = df.groupby('vid').apply(lambda x: subtract_one(x) if x['shifted_change'].any() == 1 else x['vid']).rename('temp').reset_index()['temp']
return None
def WeNYoBen(df):
s=df.groupby('vid').change.max().eq(1)
df.vid=df.vid.replace(dict(zip(s[s.shift().fillna(False)].index,s.index[s])))
return None
def Quang_Hoang (df):
mask = (df['vid'].eq(df['vid'].shift()) # blocks
| df.change.eq(1).shift() # changes
)
df['vid'] = df['vid'].mask(mask).ffill().astype('int64')
return None
def Erfan (df):
mask = df['change'].shift().eq(1)
df.loc[mask, 'new_vid'] = df['vid'].shift()
df['new_vid'] = df.groupby('vid')['new_vid'].ffill().fillna(df['vid'])
return None
def Randy (df):
df['vid'] = df['vid'] - df['vid'].map(df.groupby("vid")['change'].max().shift(fill_value=0))
return None
%timeit for x in range(100): Randy(df.copy())
# 120 ms ± 283 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit for x in range(100): Quang_Hoang(df.copy())
# 177 ms ± 709 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit for x in range(100): WeNYoBen(df.copy())
# 183 ms ± 1.65 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit for x in range(100): Erfan(df.copy())
# 311 ms ± 1.04 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit for x in range(100): zelusp(df.copy())
# 578 ms ± 4.62 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)