Pandas 赋值结果为 NaN 的布尔过滤器
Pandas Boolean Filter with Assignment resulting in NaN
我很好奇为什么 Pandas 中同时布尔索引 + 赋值的这个玩具示例不起作用:
df = pd.DataFrame({'Source': ['A', 'B', 'C', 'A', 'B', 'C'],
'Period': ['1 hr', '1 hr', '1 hr', '24 hr', '24 hr', '24 hr'],
'CO': [1.1, 1.2, 1.3, 2.1, 2.2, 2.3],
'DPM': [11.1, 11.2, 11.3, 12.1, 12.2, 12.3],
'NOx': [21.1, 21.2, 21.3, 22.1, 22.2, 22.3]})
生成的玩具 DataFrame 在这里:
Source Period CO DPM NOx
0 A 1 hr 1.1 11.1 21.1
1 B 1 hr 1.2 11.2 21.2
2 C 1 hr 1.3 11.3 21.3
3 A 24 hr 2.1 12.1 22.1
4 B 24 hr 2.2 12.2 22.2
5 C 24 hr 2.3 12.3 22.3
现在,我希望最终的 DataFrame 采用 24 hr
值并将其分配给源 A 和 B 的 1 hr
值。最终的 DataFrame 应如下所示:
Source Period CO DPM NOx
0 A 1 hr 2.1 12.1 22.1
1 B 1 hr 2.2 12.2 22.2
2 C 1 hr 1.3 11.3 21.3
3 A 24 hr 2.1 12.1 22.1
4 B 24 hr 2.2 12.2 22.2
5 C 24 hr 2.3 12.3 22.3
我尝试执行以下命令:
df.loc[df['Source'].isin(['A', 'B']) & (df['Period'] == '1 hr'), ['CO', 'DPM', 'NOx']] =\
df.loc[df['Source'].isin(['A', 'B']) & (df['Period'] == '24 hr'), ['CO', 'DPM', 'NOx']]
但最后我的 DataFrame 被 NaNs 替换了:
Source Period CO DPM NOx
0 A 1 hr NaN NaN NaN
1 B 1 hr NaN NaN NaN
2 C 1 hr 1.3 11.3 21.3
3 A 24 hr 2.1 12.1 22.1
4 B 24 hr 2.2 12.2 22.2
5 C 24 hr 2.3 12.3 22.3
分配的 LHS 和 RHS 上的过滤器表达式都正确过滤了相同的行数,似乎分配是在它被丢弃的地方。我该如何正确地做到这一点?请注意,我只想更改 CO、DPM 和 NOx 值,而不是任何其他列。
问题是索引不匹配。您可以通过使用底层 numpy 数组来解决该问题:
msk = (df['Period'] == '24 hr')
cols = ['DPM', 'NOx']
df.loc[~msk & df['Source'].isin(['A','B']), cols] = df.loc[msk & df['Source'].isin(['A','B']), cols].to_numpy()
输出:
Source Period CO DPM NOx
0 A 1 hr 1.1 12.1 22.1
1 B 1 hr 1.2 12.2 22.2
2 C 1 hr 1.3 11.3 21.3
3 A 24 hr 2.1 12.1 22.1
4 B 24 hr 2.2 12.2 22.2
5 C 24 hr 2.3 12.3 22.3
请注意,如果每种“来源”类型的“1 小时”和“24 小时”之间存在 one-to-one 关系,这只会如您所愿。
您也可以使用 groupby
+ last
:
cols = ['DPM', 'NOx']
filt = df['Source'].isin(['A','B'])
df.loc[filt, cols] = df[filt].groupby('Source')[cols].transform('last')
我很好奇为什么 Pandas 中同时布尔索引 + 赋值的这个玩具示例不起作用:
df = pd.DataFrame({'Source': ['A', 'B', 'C', 'A', 'B', 'C'],
'Period': ['1 hr', '1 hr', '1 hr', '24 hr', '24 hr', '24 hr'],
'CO': [1.1, 1.2, 1.3, 2.1, 2.2, 2.3],
'DPM': [11.1, 11.2, 11.3, 12.1, 12.2, 12.3],
'NOx': [21.1, 21.2, 21.3, 22.1, 22.2, 22.3]})
生成的玩具 DataFrame 在这里:
Source Period CO DPM NOx
0 A 1 hr 1.1 11.1 21.1
1 B 1 hr 1.2 11.2 21.2
2 C 1 hr 1.3 11.3 21.3
3 A 24 hr 2.1 12.1 22.1
4 B 24 hr 2.2 12.2 22.2
5 C 24 hr 2.3 12.3 22.3
现在,我希望最终的 DataFrame 采用 24 hr
值并将其分配给源 A 和 B 的 1 hr
值。最终的 DataFrame 应如下所示:
Source Period CO DPM NOx
0 A 1 hr 2.1 12.1 22.1
1 B 1 hr 2.2 12.2 22.2
2 C 1 hr 1.3 11.3 21.3
3 A 24 hr 2.1 12.1 22.1
4 B 24 hr 2.2 12.2 22.2
5 C 24 hr 2.3 12.3 22.3
我尝试执行以下命令:
df.loc[df['Source'].isin(['A', 'B']) & (df['Period'] == '1 hr'), ['CO', 'DPM', 'NOx']] =\
df.loc[df['Source'].isin(['A', 'B']) & (df['Period'] == '24 hr'), ['CO', 'DPM', 'NOx']]
但最后我的 DataFrame 被 NaNs 替换了:
Source Period CO DPM NOx
0 A 1 hr NaN NaN NaN
1 B 1 hr NaN NaN NaN
2 C 1 hr 1.3 11.3 21.3
3 A 24 hr 2.1 12.1 22.1
4 B 24 hr 2.2 12.2 22.2
5 C 24 hr 2.3 12.3 22.3
分配的 LHS 和 RHS 上的过滤器表达式都正确过滤了相同的行数,似乎分配是在它被丢弃的地方。我该如何正确地做到这一点?请注意,我只想更改 CO、DPM 和 NOx 值,而不是任何其他列。
问题是索引不匹配。您可以通过使用底层 numpy 数组来解决该问题:
msk = (df['Period'] == '24 hr')
cols = ['DPM', 'NOx']
df.loc[~msk & df['Source'].isin(['A','B']), cols] = df.loc[msk & df['Source'].isin(['A','B']), cols].to_numpy()
输出:
Source Period CO DPM NOx
0 A 1 hr 1.1 12.1 22.1
1 B 1 hr 1.2 12.2 22.2
2 C 1 hr 1.3 11.3 21.3
3 A 24 hr 2.1 12.1 22.1
4 B 24 hr 2.2 12.2 22.2
5 C 24 hr 2.3 12.3 22.3
请注意,如果每种“来源”类型的“1 小时”和“24 小时”之间存在 one-to-one 关系,这只会如您所愿。
您也可以使用 groupby
+ last
:
cols = ['DPM', 'NOx']
filt = df['Source'].isin(['A','B'])
df.loc[filt, cols] = df[filt].groupby('Source')[cols].transform('last')