【问题标题】:Select one column of data frame per row using value stored in another column and modify it使用存储在另一列中的值每行选择一列数据框并修改它
【发布时间】:2021-07-07 02:17:23
【问题描述】:

给定一个示例数据框df

df = (pd.DataFrame.from_dict({'a':[np.nan, 10, 20], 
                              'b':[np.nan, np.nan, 20], 
                              'c':[np.nan, 30, np.nan],
                              'c':[40, np.nan, np.nan],
                              'col':['b','c','a']})
)

      a     b     c col
0   NaN   NaN  40.0   b
1  10.0   NaN   NaN   c
2  20.0  20.0   NaN   a

我想fillna(0)df 的每一行根据存储在df.col 中的值只选择列。

输出应该是:

      a     b     c col
0   NaN   0.0  40.0   b
1  10.0   NaN   0.0   c
2  20.0  20.0   NaN   a    

我找到了一个迭代解决方案:

for index, row in df.iterrows():
    df.loc[index, row.col] = np.nanmax([df.loc[index, row.col],0])

但它很慢,而且很难使用链式操作(assign 方法)。

是否有另一种,可能是矢量化方式或apply 版本来获得所需的结果?

【问题讨论】:

    标签: python pandas dataframe vectorization apply


    【解决方案1】:

    方法一:使用stack和unstack

    df = df.set_index('col').stack(dropna=False)
    m1 = df.index.get_level_values(0) == df.index.get_level_values(1)
    m2 = df.isna()
    
    df.loc[m1 & m2] = 0
    df = df.unstack(level=1).reset_index()
    

    方法 2:熔化和旋转

    我们可以融合数据框,然后找到我们需要填充 NaN 的行。最后将我们的数据框转回我们想要的格式:

    m = df.melt(id_vars='col')
    m['value'] = np.where(
        m['col'] == m['variable'], 
        m['value'].fillna(0), 
        m['value']
    )
    
    df = m.pivot_table(
        index='col', 
        columns='variable', 
        values='value'
    ).reindex(df['col'])
    df = df.reset_index().rename_axis(columns=None)
    

    输出

      col     a     b     c
    0   b   NaN  0.00 40.00
    1   c 10.00   NaN  0.00
    2   a 20.00 20.00   NaN
    

    【讨论】:

      【解决方案2】:

      如果将索引设置为colstack()

      s = df.set_index('col').stack(dropna=False)
      
      # col   
      # b    a     NaN
      #      b     NaN
      #      c    40.0
      # c    a    10.0
      #      b     NaN
      #      c     NaN
      # a    a    20.0
      #      b    20.0
      #      c     NaN
      # dtype: float64
      

      你可以在MultiIndex级别匹配的地方设置0,然后unstack()

      s.loc[s.index.get_level_values(0) == s.index.get_level_values(1)] = 0
      df = s.unstack().reset_index()
      
      #   col     a     b     c
      # 0   b   NaN   0.0  40.0
      # 1   c  10.0   NaN   0.0
      # 2   a   0.0  20.0   NaN
      

      【讨论】:

      • 发布后,我意识到这与您的预期输出略有不同,因为它 设置 为 0 而不是 fillna(0),但也许它对其他人仍然有用.
      • 谢谢,是否有可能解决您的解决方案中的缺点并使用fillna(0) 或等效方法?
      【解决方案3】:

      您可以将.transform()np.where 一起使用:

      print(
          df.transform(
              lambda x: np.where(x.isna() & (x.index == x["col"]), 0, x),
              axis=1,
          )
      )
      

      打印:

           a    b    c col
      0  NaN    0   40   b
      1   10  NaN    0   c
      2   20   20  NaN   a
      

      【讨论】:

      • 非常紧凑和清晰的解决方案。但是我注意到在转换所有df.dtypes 后变成object。是否可以保留原始数据类型?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-05-03
      • 2019-06-19
      • 1970-01-01
      • 1970-01-01
      • 2018-11-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多