【问题标题】:Get first non-null value per row获取每行的第一个非空值
【发布时间】:2018-04-24 23:21:55
【问题描述】:

我有一个示例数据框显示如下。 对于每一行,我想先检查c1,如果它不为null,然后检查c2。通过这种方式,找到第一个非空列并将该值存储到列结果中。

ID  c1  c2  c3  c4  result
1   a   b           a
2       cc  dd      cc
3           ee  ff  ee
4               gg  gg

我现在正在使用这种方式。但我想知道是否有更好的方法。(列名没有任何模式,这只是示例)

df["result"] = np.where(df["c1"].notnull(), df["c1"], None)
df["result"] = np.where(df["result"].notnull(), df["result"], df["c2"])
df["result"] = np.where(df["result"].notnull(), df["result"], df["c3"])
df["result"] = np.where(df["result"].notnull(), df["result"], df["c4"])
df["result"] = np.where(df["result"].notnull(), df["result"], "unknown)

当有很多列时,这种方法看起来不太好。

【问题讨论】:

标签: python pandas dataframe


【解决方案1】:

先使用回填NaNs,然后通过iloc选择第一列:

df['result'] = df[['c1','c2','c3','c4']].bfill(axis=1).iloc[:, 0].fillna('unknown')

或者:

df['result'] = df.iloc[:, 1:].bfill(axis=1).iloc[:, 0].fillna('unknown')

print (df)
   ID   c1   c2  c3   c4 result
0   1    a    b   a  NaN      a
1   2  NaN   cc  dd   cc     cc
2   3  NaN   ee  ff   ee     ee
3   4  NaN  NaN  gg   gg     gg

性能

df = pd.concat([df] * 1000, ignore_index=True)


In [220]: %timeit df['result'] = df[['c1','c2','c3','c4']].bfill(axis=1).iloc[:, 0].fillna('unknown')
100 loops, best of 3: 2.78 ms per loop

In [221]: %timeit df['result'] = df.iloc[:, 1:].bfill(axis=1).iloc[:, 0].fillna('unknown')
100 loops, best of 3: 2.7 ms per loop

#jpp solution
In [222]: %%timeit
     ...: cols = df.iloc[:, 1:].T.apply(pd.Series.first_valid_index)
     ...: 
     ...: df['result'] = [df.loc[i, cols[i]] for i in range(len(df.index))]
     ...: 
1 loop, best of 3: 180 ms per loop

#cᴏʟᴅsᴘᴇᴇᴅ'  s solution
In [223]: %timeit df['result'] = df.stack().groupby(level=0).first()
1 loop, best of 3: 606 ms per loop

【讨论】:

    【解决方案2】:

    设置

    df = df.set_index('ID') # if necessary
    df
         c1   c2  c3   c4
    ID                   
    1     a    b   a  NaN
    2   NaN   cc  dd   cc
    3   NaN   ee  ff   ee
    4   NaN  NaN  gg   gg
    

    解决方案
    stack + groupby + first
    stack 隐式丢弃 NaN,所以 groupby.first 保证给你第一个非空值如果存在。将结果分配回去将暴露缺失索引处的任何 NaN,您可以在随后的调用中 fillna

    df['result'] = df.stack().groupby(level=0).first()
    # df['result'] = df['result'].fillna('unknown') # if necessary 
    df
         c1   c2  c3   c4 result
    ID                          
    1     a    b   a  NaN      a
    2   NaN   cc  dd   cc     cc
    3   NaN   ee  ff   ee     ee
    4   NaN  NaN  gg   gg     gg
    

    (请注意,这对于较大的数据帧来说很慢,为了提高性能,您可以使用@jezrael 的解决方案)

    【讨论】:

      【解决方案3】:

      我正在使用lookup 和来自 Jpp 的数据

      df=df.set_index('ID')
      s=df.ne('').idxmax(1)
      df['Result']=df.lookup(s.index,s)
      df
      Out[492]: 
         c1  c2  c3  c4 Result
      ID                      
      1   a   b              a
      2      cc  dd         cc
      3          ee  ff     ee
      4              gg     gg
      

      【讨论】:

        【解决方案4】:

        一种方法是使用pd.DataFrame.lookuppd.Series.first_valid_index 应用于转置的数据帧:

        df = pd.DataFrame({'ID': [1, 2, 3, 4],
                           'c1': ['a', '', '', ''],
                           'c2': ['b', 'cc', '', ''],
                           'c3': ['' , 'dd', 'ee', ''],
                           'c4': ['', '', 'ff', 'gg']})
        
        df = df.replace('', np.nan)
        
        df['result'] = df.lookup(df.index, df.iloc[:, 1:].T.apply(pd.Series.first_valid_index))
        
        print(df)
        
           ID   c1   c2   c3   c4 result
        0   1    a    b  NaN  NaN      a
        1   2  NaN   cc   dd  NaN     cc
        2   3  NaN  NaN   ee   ff     ee
        3   4  NaN  NaN  NaN   gg     gg
        

        【讨论】:

          猜你喜欢
          • 2020-09-04
          • 2014-11-10
          • 2017-10-08
          • 1970-01-01
          • 2021-06-18
          • 2020-08-24
          • 2019-12-07
          相关资源
          最近更新 更多