修改 pandas 数据框中的行子集

Posted

技术标签:

【中文标题】修改 pandas 数据框中的行子集【英文标题】:Modifying a subset of rows in a pandas dataframe 【发布时间】:2012-08-31 16:30:13 【问题描述】:

假设我有一个带有两列 A 和 B 的 pandas DataFrame。我想修改这个 DataFrame(或创建一个副本),以便在 A 为 0 时 B 始终为 NaN。我该如何实现?

我尝试了以下

df['A'==0]['B'] = np.nan

df['A'==0]['B'].values.fill(np.nan)

没有成功。

【问题讨论】:

如果您正在寻找一个非常快速的解决方案,请使用 NumPy 的 where,如 this solution below 所示 【参考方案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 会返回结果的副本,有时会返回原始对象的视图。根据文档here,这种行为取决于底层的 numpy 行为。我发现在一次操作中访问所有内容(而不是 [one][two])更有可能用于设置。

【讨论】:

本文的第二部分很好地回答了一个甚至没有被问到的问题 ;-) 我想知道这是否仍然是规范的 pandas 答案,特别是 b/c 它是一个明显的 DRY违反,尽管我认为鉴于熊猫内部结构的限制,实际上有必要违反 DRY? (我可能会更详细地发布这类问题,但想在我这样做之前看看你是否有一个快速的答案) 如何对没有列名的数据框进行子集化,如何仅通过索引对 df 进行子集化? df.loc[df[0]==0] 不起作用...有什么替代方法?谢谢 只是一个提示,如果你想分配另一个 DF 例如df.loc[df.A==0, df2] 请注意,如果 df2 从 0 到 end 连续索引,它将执行索引分配。因此,如果过滤后的 df 具有索引 [1, 4, 5] 并且 df2 具有 [1, 2, 3] 则只会分配索引 1。这可以通过在右侧使用未索引的结构来防止,例如df.loc[df.A==0, np.array(df2)],那么它将被成对分配。 pandas .mask() 和 .where() 函数也是如此!【参考方案2】:

Here 来自 pandas 高级索引文档:

该部分将准确解释您的需求!原来df.loc(因为 .ix 已被弃用——正如许多人在下面指出的那样)可用于数据帧的冷切片/切块。和。它也可以用来设置东西。

df.loc[selection criteria, columns I want] = value

所以布伦的回答是'找到df.A == 0 的所有地方,选择B 列并将其设置为np.nan'

【讨论】:

是的,不知怎的,loc[selection criteria, columns I want] 完美地印在了你的脑海中……【参考方案3】:

从 pandas 0.20 ix is deprecated 开始。正确的方法是使用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
>>> 

解释:

如文档here 中所述,.loc 主要基于标签,但也可以与布尔数组一起使用

所以,我们上面所做的就是应用df.loc[row_index, column_index] by:

利用loc 可以将布尔数组作为掩码,告诉pandas 我们要在row_index 中更改哪些行子集 利用loc这一事实也是基于标签的,以使用column_index中的标签'B'选择列

我们可以使用逻辑、条件或任何返回一系列布尔值的操作来构造布尔值数组。在上面的示例中,我们想要任何包含0rows,为此我们可以使用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

有关详细信息,请查看高级索引文档here。

【讨论】:

【参考方案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 倍

【讨论】:

我对此很好奇,所以我自己测试了它,使用其他参数差异更大。 Numpy 在用整数而不是 np.nan 替换 0 时快了近 10 倍。我想知道什么需要额外的时间。 np.where(df.a.values == 0, np.nan, df.b.values)中是否需要使用.values?看起来np.where(df.a == 0, np.nan, df.b) 也有效?【参考方案5】:

使用.values替换多个列转换为numpy数组:

df.loc[df.A==0, ['B', 'C']] = df.loc[df.A==0, ['B', 'C']].values / 2

【讨论】:

【参考方案6】:

替代方案:

no 1 在我看来最好,但奇怪的是我找不到它的支持文档

    将列过滤为系列(注意:过滤器出现在写入列之后,而不是之前)

dataframe.column[过滤条件]=要更改为的值

df.B[df.A==0] = np.nan
    loc

dataframe.loc[过滤条件,要更改的列]=要更改的值

df.loc[df.A == 0, 'B'] = np.nan
    numpy where

dataframe.column=np.where(过滤条件,值为真,值为假)

import numpy as np
df.B = np.where(df.A== 0, np.nan, df.B)
    apply lambda

dataframe.column=df.apply(lambda row: value if condition true else value if false, 使用行而不是列)

df.B = df.apply(lambda x: np.nan if x['A']==0 else x['B'],axis=1)
    zip 和列表语法

dataframe.column=[如果条件为真,则为 a 和 b 列的 zip 函数列表中的元素 a、b 的值为假的值]

df.B = [np.nan if a==0 else b for a,b in zip(df.A,df.B)]

【讨论】:

【参考方案7】:

要在 Pandas 中修改 DataFrame,您可以使用“语法糖”运算符,例如 +=*=/= 等。因此,请不要:

df.loc[df.A == 0, 'B'] = df.loc[df.A == 0, 'B'] / 2

你可以写:

df.loc[df.A == 0, 'B'] /= 2

要用NaN 替换值,您可以使用Pandas 方法maskwhere。例如:

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

【讨论】:

以上是关于修改 pandas 数据框中的行子集的主要内容,如果未能解决你的问题,请参考以下文章

用 Pandas 数据框中的行填充嵌套字典

Pandas:如果特定列不包含特定文本,则删除数据框中的行

折叠 Pandas 数据框中的行,每列具有不同的逻辑 [重复]

Pandas:如何根据特定列上特定值的条件选择数据框中的行[重复]

如何使用另一个日期时间索引获取具有日期时间索引的 Pandas 数据框中的行?

如何根据列中的最新日期聚合 pandas 数据框中的行?