Pandas 每个零值后的常量值
Pandas Constant Values after each Zero Value
假设我有以下数据框:
values
0 4
1 0
2 2
3 3
4 0
5 8
6 5
7 1
8 0
9 4
10 7
我想找到一个 pandas 向量化函数(最好使用 groupby),它将所有非零值替换为该非零值块中的第一个非零值,即可以给我的东西
values new
0 4 4
1 0 0
2 2 2
3 3 2
4 0 0
5 8 8
6 5 8
7 1 8
8 0 0
9 4 4
10 7 4
有什么好的方法可以实现吗?
为 select 具有零的行及其后续行创建一个布尔掩码,然后将此布尔掩码与 where
一起使用以将剩余值替换为 NaN
,然后使用前向填充向前传播值。
m = df['values'].eq(0)
df['new'] = df['values'].where(m | m.shift()).ffill().fillna(df['values'])
结果
print(df)
values new
0 4 4.0
1 0 0.0
2 2 2.0
3 3 2.0
4 0 0.0
5 8 8.0
6 5 8.0
7 1 8.0
8 0 0.0
9 4 4.0
10 7 4.0
以下函数应该可以为您完成这项工作。查看函数中的注释以了解解决方案的工作流程。
import pandas as pd
def ffill_nonZeros(values):
# get the values that are not equal to 0
non_zero = values[df['values'] != 0]
# get their indexes
non_zero_idx = non_zero.index.to_series()
# find where indexes are consecutive
diff = non_zero_idx.diff()
mask = diff == 1
# using the mask make all places in non_zero where the change is consecutive equal None
non_zero[mask] = None
# fill forward (replace all None values with previous valid value)
new_non_zero = non_zero.fillna(method='ffill')
# put new values back in their indexs
new = values.copy()
new[new_non_zero.index] = new_non_zero
return new
现在将此函数应用于您的数据:
df = pd.DataFrame([4, 0, 2, 3, 0, 8, 5, 1, 0, 4, 7], columns=['values'])
df['new'] = ffill_nonZeros(df['values'])
print(df)
输出:
values new
0 4 4
1 0 0
2 2 2
3 3 2
4 0 0
5 8 8
6 5 8
7 1 8
8 0 0
9 4 4
10 7 4
获取零的行,以及紧随其后的行:
zeros = df.index[df['values'].eq(0)]
after_zeros = zeros.union(zeros +1)
获取需要前向填充的行:
replace = df.index.difference(after_zeros)
replace = replace[replace > zeros[0]]
在 replace
上设置值并向前填充:
df['new'] = df['values']
df.loc[replace, 'new'] = np.nan
df.ffill()
values new
0 4 4.0
1 0 0.0
2 2 2.0
3 3 2.0
4 0 0.0
5 8 8.0
6 5 8.0
7 1 8.0
8 0 0.0
9 4 4.0
10 7 4.0
假设我有以下数据框:
values
0 4
1 0
2 2
3 3
4 0
5 8
6 5
7 1
8 0
9 4
10 7
我想找到一个 pandas 向量化函数(最好使用 groupby),它将所有非零值替换为该非零值块中的第一个非零值,即可以给我的东西
values new
0 4 4
1 0 0
2 2 2
3 3 2
4 0 0
5 8 8
6 5 8
7 1 8
8 0 0
9 4 4
10 7 4
有什么好的方法可以实现吗?
为 select 具有零的行及其后续行创建一个布尔掩码,然后将此布尔掩码与 where
一起使用以将剩余值替换为 NaN
,然后使用前向填充向前传播值。
m = df['values'].eq(0)
df['new'] = df['values'].where(m | m.shift()).ffill().fillna(df['values'])
结果
print(df)
values new
0 4 4.0
1 0 0.0
2 2 2.0
3 3 2.0
4 0 0.0
5 8 8.0
6 5 8.0
7 1 8.0
8 0 0.0
9 4 4.0
10 7 4.0
以下函数应该可以为您完成这项工作。查看函数中的注释以了解解决方案的工作流程。
import pandas as pd
def ffill_nonZeros(values):
# get the values that are not equal to 0
non_zero = values[df['values'] != 0]
# get their indexes
non_zero_idx = non_zero.index.to_series()
# find where indexes are consecutive
diff = non_zero_idx.diff()
mask = diff == 1
# using the mask make all places in non_zero where the change is consecutive equal None
non_zero[mask] = None
# fill forward (replace all None values with previous valid value)
new_non_zero = non_zero.fillna(method='ffill')
# put new values back in their indexs
new = values.copy()
new[new_non_zero.index] = new_non_zero
return new
现在将此函数应用于您的数据:
df = pd.DataFrame([4, 0, 2, 3, 0, 8, 5, 1, 0, 4, 7], columns=['values'])
df['new'] = ffill_nonZeros(df['values'])
print(df)
输出:
values new
0 4 4
1 0 0
2 2 2
3 3 2
4 0 0
5 8 8
6 5 8
7 1 8
8 0 0
9 4 4
10 7 4
获取零的行,以及紧随其后的行:
zeros = df.index[df['values'].eq(0)]
after_zeros = zeros.union(zeros +1)
获取需要前向填充的行:
replace = df.index.difference(after_zeros)
replace = replace[replace > zeros[0]]
在 replace
上设置值并向前填充:
df['new'] = df['values']
df.loc[replace, 'new'] = np.nan
df.ffill()
values new
0 4 4.0
1 0 0.0
2 2 2.0
3 3 2.0
4 0 0.0
5 8 8.0
6 5 8.0
7 1 8.0
8 0 0.0
9 4 4.0
10 7 4.0