【问题标题】:First column name with non null value by row pandas具有非空值的第一列名称按行熊猫
【发布时间】:2016-03-15 23:08:29
【问题描述】:

我想知道第一年各种项目的收入。

给定以下数据框:

ID  Y1      Y2      Y3
0   NaN     8       4
1   NaN     NaN     1
2   NaN     NaN     NaN
3   5       3       NaN

我想逐行返回具有非空值的第一列的名称。

在这种情况下,我想返回:

['Y2','Y3',NaN,'Y1']

我的目标是将此作为列添加到原始数据框中。

以下代码大部分都有效,但确实很笨重。

import pandas as pd
import numpy as np

df = pd.DataFrame({'Y1':[np.nan, np.nan, np.nan, 5],'Y2':[8, np.nan, np.nan, 3], 'Y3':[4, 1, np.nan, np.nan]})
df['first'] = np.nan

for ID in df.index:
row = df.loc[ID,]
for i in range(0,len(row)):
    if (~pd.isnull(row[i])):
        df.loc[ID,'first'] = row.index[i]
        break

返回:

   Y1  Y2  Y3  first
0 NaN  8   4   Y2   
1 NaN NaN  1   Y3   
2 NaN NaN NaN  first
3  5   3  NaN  Y1   

有人知道更优雅的解决方案吗?

【问题讨论】:

    标签: python numpy pandas


    【解决方案1】:

    您可以使用带有 axis=1 的 lambda 表达式将 first_valid_index 应用于数据框中的每一行以指定行。

    >>> df.apply(lambda row: row.first_valid_index(), axis=1)
    ID
    0      Y2
    1      Y3
    2    None
    3      Y1
    dtype: object
    

    将其应用于您的数据框:

    df = df.assign(first = df.apply(lambda row: row.first_valid_index(), axis=1))
    
    >>> df
        Y1  Y2  Y3 first
    ID                  
    0  NaN   8   4    Y2
    1  NaN NaN   1    Y3
    2  NaN NaN NaN  None
    3    5   3 NaN    Y1
    

    【讨论】:

      【解决方案2】:

      最好避免使用apply,因为它不是矢量化的。以下是矢量化的。已使用 Pandas 1.1 进行了测试。

      设置

      import numpy as np
      import pandas as pd
      
      df = pd.DataFrame({'Y1':[np.nan, np.nan, np.nan, 5],'Y2':[8, np.nan, np.nan, 3], 'Y3':[4, 1, np.nan, np.nan]})
      
      # df.dropna(how='all', inplace=True)  # Optional but cleaner
      
      # For ranking only:
      col_ranks = pd.DataFrame(index=df.columns, data=np.arange(1, 1 + len(df.columns)), columns=['first_notna_rank'], dtype='UInt8') # UInt8 supports max value of 255.
      

      查找第一个非空列的名称

      df['first_notna_name'] = df.dropna(how='all').notna().idxmax(axis=1).astype('string')
      

      如果保证df 没有全为空的行,则可以选择删除上面的.dropna 操作。

      然后找到第一个非空值

      使用bfill
      df['first_notna_value'] = df[df.columns.difference(['first_notna_name'])].bfill(axis=1).iloc[:, 0]
      
      使用melt
      df['first_notna_value'] = df.melt(id_vars='first_notna_name', value_vars=df.columns.difference(['first_notna_name']), ignore_index=False).query('first_notna_name == variable').merge(df[[]], how='right', left_index=True, right_index=True).loc[df.index, 'value']
      

      如果保证df 没有全为空的行,则可以选择删除上面的.merge 操作。

      对名称进行排名

      df = df.merge(col_ranks, how='left', left_on='first_notna_name', right_index=True)
      

      有没有更好的办法?

      输出

          Y1   Y2   Y3 first_notna_name  first_notna_value  first_notna_rank
      0  NaN  8.0  4.0               Y2                8.0                 2
      1  NaN  NaN  1.0               Y3                1.0                 3
      2  NaN  NaN  NaN             <NA>                NaN              <NA>
      3  5.0  3.0  NaN               Y1                5.0                 1
      

      部分归功于:mepiRSquaredAndy 的回答

      【讨论】:

        【解决方案3】:

        将此代码应用于只有一行的数据框,以返回该行中包含空值的第一列。

        row.columns[~(row.loc[:].isna()).all()][-1]

        【讨论】:

        • 这不是这个问题要问的。
        猜你喜欢
        • 2020-03-21
        • 1970-01-01
        • 1970-01-01
        • 2023-03-21
        • 1970-01-01
        • 1970-01-01
        • 2017-02-20
        • 2022-11-02
        相关资源
        最近更新 更多