如何在Pandas数据框中将负数替换为零

135

我想知道是否有一种方法可以用0替换所有DataFrame中的负数?


另外,我认为你的第二行应该写成 num[num < 0] = 0 - hlin117
9个回答

148

如果您的所有列都是数值型的,您可以使用布尔索引:

In [1]: import pandas as pd

In [2]: df = pd.DataFrame({'a': [0, -1, 2], 'b': [-3, 2, 1]})

In [3]: df
Out[3]: 
   a  b
0  0 -3
1 -1  2
2  2  1

In [4]: df[df < 0] = 0

In [5]: df
Out[5]: 
   a  b
0  0  0
1  0  2
2  2  1

对于更一般的情况,这个答案展示了私有方法_get_numeric_data

In [1]: import pandas as pd

In [2]: df = pd.DataFrame({'a': [0, -1, 2], 'b': [-3, 2, 1],
                           'c': ['foo', 'goo', 'bar']})

In [3]: df
Out[3]: 
   a  b    c
0  0 -3  foo
1 -1  2  goo
2  2  1  bar

In [4]: num = df._get_numeric_data()

In [5]: num[num < 0] = 0

In [6]: df
Out[6]: 
   a  b    c
0  0  0  foo
1  0  2  goo
2  2  1  bar

使用 timedelta 类型时,布尔索引似乎只能在单独的列上起作用,而不能在整个数据帧上起作用。因此,您可以执行以下操作:

In [1]: import pandas as pd

In [2]: df = pd.DataFrame({'a': pd.to_timedelta([0, -1, 2], 'd'),
   ...:                    'b': pd.to_timedelta([-3, 2, 1], 'd')})

In [3]: df
Out[3]: 
        a       b
0  0 days -3 days
1 -1 days  2 days
2  2 days  1 days

In [4]: for k, v in df.iteritems():
   ...:     v[v < 0] = 0
   ...:     

In [5]: df
Out[5]: 
       a      b
0 0 days 0 days
1 0 days 2 days
2 2 days 1 days

更新:pd.Timedelta 的比较可以作用于整个 DataFrame:

In [1]: import pandas as pd

In [2]: df = pd.DataFrame({'a': pd.to_timedelta([0, -1, 2], 'd'),
   ...:                    'b': pd.to_timedelta([-3, 2, 1], 'd')})

In [3]: df[df < pd.Timedelta(0)] = 0

In [4]: df
Out[4]: 
       a      b
0 0 days 0 days
1 0 days 2 days
2 2 days 1 days

139

另一种简洁的方法是使用pandas.DataFrame.clip

例如:

import pandas as pd

In [20]: df = pd.DataFrame({'a': [-1, 100, -2]})

In [21]: df
Out[21]: 
     a
0   -1
1  100
2   -2

In [22]: df.clip(lower=0)
Out[22]: 
     a
0    0
1  100
2    0

1
这就是我正在寻找的内联解决方案!谢谢! - DomingoR
19
如果您只想在特定列上应用 clip,可以这样做:df['col_name'] = df['col_name'].clip(lower=0) - gies0r
6
clip_lower已经被弃用,因此最好使用df.clip(lower=0)。请注意,这两个函数的作用相同,都是将DataFrame或Series中的值限制在下限之上(本例中为0)。 - Sally Levesque
这似乎是最快的方法。 - Alaa M.
@DomingoR说这是内联的,但这不会直接编辑值,您需要分配到同一列才能使剪辑生效。 - Diedre
@Diedre Inline不等同于inplace。 - DomingoR

21

我发现另一个干净的选项是 pandas.DataFrame.mask,它可以“替换满足条件的值”。

创建DataFrame:

In [2]: import pandas as pd

In [3]: df = pd.DataFrame({'a': [0, -1, 2], 'b': [-3, 2, 1]})

In [4]: df
Out[4]: 
   a  b
0  0 -3
1 -1  2
2  2  1

将负数替换为0:

In [5]: df.mask(df < 0, 0)
Out[5]: 
   a  b
0  0  0
1  0  2
2  2  1

或者,将负数替换为 NaN,这是我经常需要的:

In [7]: df.mask(df < 0)
Out[7]: 
     a    b
0  0.0  NaN
1  NaN  2.0
2  2.0  1.0

.mask() 就是最简单易懂的! - mirekphd

14

或许你可以这样使用 pandas.where(args)

data_frame = data_frame.where(data_frame < 0, 0)

13

使用Lambda函数

df['column'] = df['column'].apply(lambda x : x if x > 0 else 0)

2

如果您正在处理一个大的数据框(例如我的情况是40m x 700),通过迭代列来处理会更快速和更省内存,可以使用以下代码:

for col in df.columns:
    df[col][df[col] < 0] = 0

当您执行此操作时,会出现“正在尝试在DataFrame的切片副本上设置值”的警告。 - ajrlewis
也许使用 .copy() 可以避免这种情况。 - user10381466

1

稍作修改的答案如下:

让我们识别所有数值列,并创建一个包含所有数值的数据框。然后在新数据框中将负值替换为NaN。

df_numeric = df.select_dtypes(include=[np.number])
df_numeric = df_numeric.where(lambda x: x > 0, np.nan)

现在,删除主数据框中处理负值的列,然后将新列的值与主数据框连接起来。
numeric_cols = df_numeric.columns.values
df = df.drop(columns=numeric_cols)
df = pd.concat([df, df_numeric], axis = 1)

0
如果您有一个包含混合数据类型的数据集,还应考虑将非数字数据移动到索引中,更新数据,然后删除索引:
df = pd.DataFrame({'a': [0, -1, 2], 'b': [-3, 2, 1],
                       'c': ['foo', 'goo', 'bar']})
df = df.set_index('c')
df[df < 0] = 0
df = df.reset_index()

使用_get_numeric_data()方法的方式出了一些问题,对我来说无效。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接