【问题标题】:Pandas replace negative value with zero, in columns that match a regular expressionPandas 在匹配正则表达式的列中用零替换负值
【发布时间】:2018-03-22 23:15:50
【问题描述】:

目标是仅用零替换某些列(“capped1”和“capped2”,但不是“signed”)中的所有负值。需要通过正则表达式选择列。 (实际的 df 有 >1000 列名称更复杂)

我想出了:

import pandas as pd
import re
import numpy as np
index = [1,2,3,4]
d = {'capped1': [1,0,-1,np.nan], 'capped2': [2,0,np.nan,-9999],'signed':[2,0,-3,np.nan]}
df = pd.DataFrame(data=d, index=index)
df_right = df.filter(regex=("capped.*")).clip(lower=0)
df_left = df.drop(list(df_right.columns), 1)
df_out = df_left.merge(df_right,left_index=True,right_index=True,how="outer")
df_out

有没有更好的方法来做到这一点?我的猜测是,这可以用一行而不是 3 行替换,您可以直接替换 df 中的值。

【问题讨论】:

    标签: python regex pandas


    【解决方案1】:

    选项 1
    pd.DataFrame.updatepd.DataFrame.clip 一起使用
    这将编辑df

    df.update(df.filter(regex="^capped.*$").clip(lower=0))
    df
    
       capped1  capped2  signed
    1      1.0      2.0     2.0
    2      0.0      0.0     0.0
    3      0.0      NaN    -3.0
    4      NaN      0.0     NaN
    

    选项 2
    使用pd.DataFrame.assignnp.maximum
    这会产生一个副本并单独留下df
    我使用np.maximum 作为品种。我本可以使用pd.DataFrame.clip
    请注意,我使用**np.maximum 返回的数据帧解包为字典。相当于**{c: s for c, s in d.iteritems()},其中dnp.maximum的返回值

    df.assign(**np.maximum(df.filter(regex='^capped.*'), 0))
    
       capped1  capped2  signed
    1      1.0      2.0     2.0
    2      0.0      0.0     0.0
    3      0.0      NaN    -3.0
    4      NaN      0.0     NaN
    

    【讨论】:

      【解决方案2】:

      您可以获取列名,然后仅在子集中应用函数:

      cols = df.columns[df.columns.str.contains('^capped.*')]
      print (cols)
      Index(['capped1', 'capped2'], dtype='object')
      
      df[cols] = df[cols].clip(lower=0)
      print (df)
         capped1  capped2  signed
      1      1.0      2.0     2.0
      2      0.0      0.0     0.0
      3      0.0      NaN    -3.0
      4      NaN      0.0     NaN
      

      类似的解决方案:

      m = df.columns.str.contains('^capped.*')
      print (m)
      [ True  True False]
      
      df.loc[:, m] = df.loc[:, m].clip(lower=0)
      print (df)
         capped1  capped2  signed
      1      1.0      2.0     2.0
      2      0.0      0.0     0.0
      3      0.0      NaN    -3.0
      4      NaN      0.0     NaN
      

      Jon Clements 评论的好主意 - 没有必要使用 regex,这里可以使用 startswith

       cols = df.columns[df.columns.str.startswith('capped')]
       m = df.columns.str.startswith('capped')
      

      【讨论】:

      • capped* 匹配 cappe,然后匹配 0 个或多个 d 字符。我认为 OP 的意思是 capped\d+capped.*
      • @WiktorStribiżew - 感谢您的评论,^capped\d+ 更好?
      • 我经常想知道为什么df.filter 不支持开箱即用的startswith - 你会认为这是过滤某些列的一种非常常见的方法......
      • 哦...我不会打扰,但如果您是,请使用df.columns.str.startswith('capped'),这比不必要地使用正则表达式要好... :)
      • ^capped\d+ 只匹配以 capped 开头的字符串,然后是 1+ 位数字。我不太明白这个问题,也许capped 就足够了。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-16
      • 1970-01-01
      • 2012-09-19
      • 2015-11-30
      • 1970-01-01
      • 2020-01-21
      相关资源
      最近更新 更多