【问题标题】:How to replace negative numbers in Pandas Data Frame by zero如何将 Pandas 数据框中的负数替换为零
【发布时间】:2015-03-01 18:48:56
【问题描述】:

我想知道是否有办法将所有 DataFrame 负数替换为零?

【问题讨论】:

  • 另外,我相信你的第二行应该是num[num < 0] = 0

标签: python pandas dataframe replace negative-number


【解决方案1】:

如果所有列都是数字,则可以使用布尔索引:

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

对于更一般的情况,this answer 显示私有方法 _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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

      另一种简洁的方法是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
      

      还有df.clip_lower(0)

      【讨论】:

      • 这是我一直在寻找的内联解决方案!谢谢!
      • 如果您只想在特定列上应用clip,您可以使用df['col_name'] = df['col_name'].clip(lower=0)
      • clip_lower 一直是deprecated 所以宁愿坚持df.clip(lower=0)
      • 这似乎是最快的方法
      【解决方案4】:

      如果您正在处理较大的 df(在我的情况下为 40m x 700),它可以通过在列上进行迭代来更快地工作并且节省内存。

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

      【讨论】:

      • 当您执行此操作时,您会收到 试图在数据帧的切片副本上设置值 警告
      • 也许使用 .copy() 会避免它
      【解决方案5】:

      我发现另一个有用的干净选项是pandas.DataFrame.mask,它将“替换条件为真的值”。

      创建数据框:

      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() 就像它得到的吻一样!
      【解决方案6】:

      带 lambda 函数

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

      【讨论】:

        【解决方案7】:

        对现有答案稍作修改。

        让我们识别所有数值列并创建一个包含所有数值的数据框。 然后在新数据框中用 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)
        

        【讨论】:

          猜你喜欢
          • 2012-10-29
          • 2021-07-07
          • 1970-01-01
          • 2022-01-26
          • 2021-10-25
          • 1970-01-01
          相关资源
          最近更新 更多