修改 Pandas 数据框中的行子集
- 2025-04-15 09:18:00
- admin 原创
- 29
问题描述:
假设我有一个包含两列 A 和 B 的 pandas DataFrame。我想修改此 DataFrame(或创建副本),以便每当 A 为 0 时,B 始终为 NaN。我该如何实现这一点?
我尝试了以下
df['A'==0]['B'] = np.nan
和
df['A'==0]['B'].values.fill(np.nan)
没有成功。
解决方案 1:
用于.loc
基于标签的索引:
df.loc[df.A==0, 'B'] = np.nan
该df.A==0
表达式创建一个布尔序列,用于索引行并'B'
选择列。您还可以使用它来转换列的子集,例如:
df.loc[df.A==0, 'B'] = df.loc[df.A==0, 'B'] / 2
我对 Pandas 的内部机制了解不够,无法确切地知道为什么会这样,但基本问题是,有时对 DataFrame 进行索引会返回结果的副本,有时又会返回原始对象的视图。根据此处的文档,此行为取决于底层 numpy 的行为。我发现,对于设置操作来说,通过一次操作访问所有内容(而不是 one)更有可能奏效。
解决方案 2:
以下是来自 Pandas 文档的有关高级索引的内容:
本节将准确解释您需要什么!事实证明df.loc
(正如许多人指出的那样,.ix 已被弃用),它可以用于对数据框进行酷炫的切片/切块。而且,它还可以用于设置。
df.loc[selection criteria, columns I want] = value
所以 Bren 的回答是“找到所有 的地方df.A == 0
,选择列B
并将其设置为np.nan
”
解决方案 3:
从 pandas 0.20 开始ix 已被弃用。正确的方法是使用df.loc
这是一个有效的例子
>>> import pandas as pd
>>> import numpy as np
>>> df = pd.DataFrame({"A":[0,1,0], "B":[2,0,5]}, columns=list('AB'))
>>> df.loc[df.A == 0, 'B'] = np.nan
>>> df
A B
0 0 NaN
1 1 0
2 0 NaN
>>>
解释:
正如此处的文档所解释的,.loc
主要基于标签,但也可以与布尔数组一起使用。
因此,我们上面所做的是df.loc[row_index, column_index]
通过以下方式应用:
利用
loc
布尔数组作为掩码来告诉 Pandas 我们想要更改哪一部分行row_index
利用事实
loc
也是基于标签的,使用标签来选择'B'
列column_index
我们可以使用逻辑运算、条件运算或任何返回一系列布尔值的运算来构造布尔值数组。在上面的例子中,我们想要任何rows
包含 的布尔值0
,为此我们可以使用df.A == 0
,如下例所示,这将返回一系列布尔值。
>>> df = pd.DataFrame({"A":[0,1,0], "B":[2,0,5]}, columns=list('AB'))
>>> df
A B
0 0 2
1 1 0
2 0 5
>>> df.A == 0
0 True
1 False
2 True
Name: A, dtype: bool
>>>
然后,我们使用上面的布尔数组来选择和修改必要的行:
>>> df.loc[df.A == 0, 'B'] = np.nan
>>> df
A B
0 0 NaN
1 1 0
2 0 NaN
有关更多信息,请查看此处的高级索引文档。
解决方案 4:
为了大幅提高速度,请使用 NumPy 的 where 函数。
设置
创建一个包含 100,000 行且包含一些零的两列 DataFrame。
df = pd.DataFrame(np.random.randint(0,3, (100000,2)), columns=list('ab'))
快速解决方案numpy.where
df['b'] = np.where(df.a.values == 0, np.nan, df.b.values)
时间安排
%timeit df['b'] = np.where(df.a.values == 0, np.nan, df.b.values)
685 µs ± 6.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit df.loc[df['a'] == 0, 'b'] = np.nan
3.11 ms ± 17.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Numpy 的where
速度大约是它的 4 倍
解决方案 5:
要替换多列,请使用以下命令将其转换为 numpy 数组.values
:
df.loc[df.A==0, ['B', 'C']] = df.loc[df.A==0, ['B', 'C']].values / 2
解决方案 6:
要修改 Pandas 中的 DataFrame,可以使用“语法糖”运算符+=
,如*=
、/=
等。因此,不要:
df.loc[df.A == 0, 'B'] = df.loc[df.A == 0, 'B'] / 2
你可以写:
df.loc[df.A == 0, 'B'] /= 2
要用 替换值,NaN
可以使用 Pandas 方法mask
或where
。例如:
df = pd.DataFrame({'A': [1, 2, 3], 'B': [0, 0, 4]})
A B
0 1 0
1 2 0
2 3 4
df['A'].mask(df['B'] == 0, inplace=True) # other=np.nan by default
# df['A'].where(df['B'] != 0, inplace=True)
结果:
A B
0 NaN 0
1 NaN 0
2 3.0 4
解决方案 7:
替代方案:
在我看来,没有 1 是最好的,但奇怪的是我找不到它的支持文档
按系列过滤列(注意:过滤在写入列之后进行,而不是之前)
dataframe.column[过滤条件]=要更改为的值
df.B[df.A==0] = np.nan
位置
dataframe.loc[过滤条件,要更改的列]=要更改的值
df.loc[df.A == 0, 'B'] = np.nan
numpy在哪里
dataframe.column=np.where(过滤条件,为真则为值,为假则为值)
import numpy as np
df.B = np.where(df.A== 0, np.nan, df.B)
应用 lambda
dataframe.column=df.apply(lambda row: 如果条件为真则为值,否则为假则为值,使用行而不是列)
df.B = df.apply(lambda x: np.nan if x['A']==0 else x['B'],axis=1)
zip和 list 语法
dataframe.column=[如果条件为真则为值,否则为假,对于列表中元素 a、b,使用 a 和 b 列的 zip 函数计算得出]
df.B = [np.nan if a==0 else b for a,b in zip(df.A,df.B)]
扫码咨询,免费领取项目管理大礼包!