根据其他列条件更新列

时间:2019-11-13 16:44:06

标签: python pandas

我需要更新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

5 个答案:

答案 0 :(得分:2)

假设数据帧的结构像我们可以做的示例shift一样,然后创建映射dictreplace

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)

  1. 首先,我们检查changesshift发生的位置。
  2. 然后,我们创建一个新列,在其中将shift的值vid下移一行。
  3. 然后我们使用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

最后Groupbyforwardfill

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)

如果您不介意将所有替换数字重新编号为连续的,则可以通过减去shiftcumsum的变化来获得一个非常干净的版本:

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

具有原始编号的另一个选项是使用mapgroupby

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']

...这是结果

enter image description here

编辑

我已经分析了每个答案,以查看哪个答案最快。结果:

@兰迪是赢家!

enter image description here

..最后我死了,哈哈:)大家好!

以下是分析代码:

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)