如何根据每组的条件屏蔽列中的值
How to mask values in column based on a condition per group
我有这样的 pandas DataFrame:
data = {'ID_1':['A', 'A','A', 'B', 'B', 'B'],
'ID_2':[1, 2, 2, 1, 1, 2],
'DATE':['2021-11-21', '2021-12-19', '2021-09-05', '2021-11-07', '2021-12-05','2021-12-26'],
'VALUE': [0.5, 0.5, 0.5, 0.6, 0.6, 0.6]}
df = pd.DataFrame(data)
而且我只想在 'VALUE'
列中保留 'ID_1'
和 'ID_2'
子集的第 'DATE'
列中的最低日期
所需的输出如下所示:
data = {'ID_1':['A', 'A','A', 'B', 'B', 'B'],
'ID_2':[1, 2, 2, 1, 1, 2],
'DATE':['2021-11-21', '2021-12-19', '2021-09-05', '2021-11-07', '2021-12-05','2021-12-26'],
'VALUE': [0.5, np.NaN, 0.5, 0.6, np.NaN, 0.6]}
df = pd.DataFrame(data)
我尝试的是创建两次对该数据框进行分组的函数,但我以 ValueError Length of values (2) does not match length of index (1)
结尾
我的函数:
def foo(val):
def add_mask(val):
val.reset_index(inplace=True)
min_date = val['DATE'].min()
mask = val.DATE == min_date
return val[mask]
return val.groupby('ID_1').apply(add_mask)
test = df.groupby('ID_2').apply(foo)
您可以groupby
“ID_1”和“ID_2”并为 DataFrame 的每个组转换“DATE”的 min
。然后使用 eq
来标识组分钟数所在的行。最后,使用 where
将 NaN 值分配给不是最小值的“VALUE”:
df['VALUE'] = df['VALUE'].where(df.groupby(['ID_1','ID_2'])['DATE'].transform('min').eq(df['DATE']))
输出:
ID_1 ID_2 DATE VALUE
0 A 1 2021-11-21 0.5
1 A 2 2021-12-19 NaN
2 A 2 2021-09-05 0.5
3 B 1 2021-11-07 0.6
4 B 1 2021-12-05 NaN
5 B 2 2021-12-26 0.6
函数 foo
不起作用,因为您从不使用您在其中创建的 mask
来修改每个组中的“VALUE”。如果你更换
return val[mask]
和
val['VALUE'] = val['VALUE'].where(mask)
return val
它将产生预期的结果(您需要修复索引,但总体结构将符合您的预期)。
您可以groupby
your two ID columns and compute the min with transform('min')
, then mask
值不相等的数据:
df['VALUE'] = df['VALUE'].mask(df['DATE'].ne(df.groupby(['ID_1', 'ID_2'])['DATE'].transform('min')))
输出:
ID_1 ID_2 DATE VALUE
0 A 1 2021-11-21 0.5
1 A 2 2021-12-19 NaN
2 A 2 2021-09-05 0.5
3 B 1 2021-11-07 0.6
4 B 1 2021-12-05 NaN
5 B 2 2021-12-26 0.6
许多优雅的答案,但这是我将如何去做的;
grp = df.groupby(["ID_1", "ID_2"])
grp
def change(df):
df.loc[df.DATE != df.DATE.min(), 'VALUE'] = np.nan
return df
grp.apply(change)
结果:
ID_1 ID_2 DATE VALUE
0 A 1 2021-11-21 0.5
1 A 2 2021-12-19 NaN
2 A 2 2021-09-05 0.5
3 B 1 2021-11-07 0.6
4 B 1 2021-12-05 NaN
5 B 2 2021-12-26 0.6
另一种方法:
df['DATE'] = df.groupby(['ID_1','ID_2']).DATE.transform(lambda x: (x==min(x))*x).replace('', np.NaN)
使用布尔值 (x==min(x))
:
df['is_min'] = df.groupby(['ID_1','ID_2']).DATE.transform(lambda x: x==min(x))
#
# ID_1 ID_2 DATE VALUE is_min
# 0 A 1 2021-11-21 0.5 True
# 1 A 2 2021-12-19 0.5 False
# 2 A 2 2021-09-05 0.5 True
# 3 B 1 2021-11-07 0.6 True
# 4 B 1 2021-12-05 0.6 False
# 5 B 2 2021-12-26 0.6 True
我有这样的 pandas DataFrame:
data = {'ID_1':['A', 'A','A', 'B', 'B', 'B'],
'ID_2':[1, 2, 2, 1, 1, 2],
'DATE':['2021-11-21', '2021-12-19', '2021-09-05', '2021-11-07', '2021-12-05','2021-12-26'],
'VALUE': [0.5, 0.5, 0.5, 0.6, 0.6, 0.6]}
df = pd.DataFrame(data)
而且我只想在 'VALUE'
列中保留 'ID_1'
和 'ID_2'
子集的第 'DATE'
列中的最低日期
所需的输出如下所示:
data = {'ID_1':['A', 'A','A', 'B', 'B', 'B'],
'ID_2':[1, 2, 2, 1, 1, 2],
'DATE':['2021-11-21', '2021-12-19', '2021-09-05', '2021-11-07', '2021-12-05','2021-12-26'],
'VALUE': [0.5, np.NaN, 0.5, 0.6, np.NaN, 0.6]}
df = pd.DataFrame(data)
我尝试的是创建两次对该数据框进行分组的函数,但我以 ValueError Length of values (2) does not match length of index (1)
我的函数:
def foo(val):
def add_mask(val):
val.reset_index(inplace=True)
min_date = val['DATE'].min()
mask = val.DATE == min_date
return val[mask]
return val.groupby('ID_1').apply(add_mask)
test = df.groupby('ID_2').apply(foo)
您可以groupby
“ID_1”和“ID_2”并为 DataFrame 的每个组转换“DATE”的 min
。然后使用 eq
来标识组分钟数所在的行。最后,使用 where
将 NaN 值分配给不是最小值的“VALUE”:
df['VALUE'] = df['VALUE'].where(df.groupby(['ID_1','ID_2'])['DATE'].transform('min').eq(df['DATE']))
输出:
ID_1 ID_2 DATE VALUE
0 A 1 2021-11-21 0.5
1 A 2 2021-12-19 NaN
2 A 2 2021-09-05 0.5
3 B 1 2021-11-07 0.6
4 B 1 2021-12-05 NaN
5 B 2 2021-12-26 0.6
函数 foo
不起作用,因为您从不使用您在其中创建的 mask
来修改每个组中的“VALUE”。如果你更换
return val[mask]
和
val['VALUE'] = val['VALUE'].where(mask)
return val
它将产生预期的结果(您需要修复索引,但总体结构将符合您的预期)。
您可以groupby
your two ID columns and compute the min with transform('min')
, then mask
值不相等的数据:
df['VALUE'] = df['VALUE'].mask(df['DATE'].ne(df.groupby(['ID_1', 'ID_2'])['DATE'].transform('min')))
输出:
ID_1 ID_2 DATE VALUE
0 A 1 2021-11-21 0.5
1 A 2 2021-12-19 NaN
2 A 2 2021-09-05 0.5
3 B 1 2021-11-07 0.6
4 B 1 2021-12-05 NaN
5 B 2 2021-12-26 0.6
许多优雅的答案,但这是我将如何去做的;
grp = df.groupby(["ID_1", "ID_2"])
grp
def change(df):
df.loc[df.DATE != df.DATE.min(), 'VALUE'] = np.nan
return df
grp.apply(change)
结果:
ID_1 ID_2 DATE VALUE
0 A 1 2021-11-21 0.5
1 A 2 2021-12-19 NaN
2 A 2 2021-09-05 0.5
3 B 1 2021-11-07 0.6
4 B 1 2021-12-05 NaN
5 B 2 2021-12-26 0.6
另一种方法:
df['DATE'] = df.groupby(['ID_1','ID_2']).DATE.transform(lambda x: (x==min(x))*x).replace('', np.NaN)
使用布尔值 (x==min(x))
:
df['is_min'] = df.groupby(['ID_1','ID_2']).DATE.transform(lambda x: x==min(x))
#
# ID_1 ID_2 DATE VALUE is_min
# 0 A 1 2021-11-21 0.5 True
# 1 A 2 2021-12-19 0.5 False
# 2 A 2 2021-09-05 0.5 True
# 3 B 1 2021-11-07 0.6 True
# 4 B 1 2021-12-05 0.6 False
# 5 B 2 2021-12-26 0.6 True