第一列名称,行大熊猫非空值

时间:2016-03-15 23:08:30

标签: python numpy pandas

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

鉴于以下内容,dataframe:

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   

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

3 个答案:

答案 0 :(得分:6)

您可以使用带有轴= 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

答案 1 :(得分:1)

避免使用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(how='all)

然后查找第一个非空值

如果df没有全为空的行:

df['first_notna_value'] = df.lookup(row_labels=df.index, col_labels=df['first_notna_name'])

如果df的行可能全为空:(低效

df['first_notna_value'] = df.drop(columns='first_notna_name').bfill(axis=1).iloc[:, 0]

要排名

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

部分功劳:piRSquaredAndy的答案

答案 2 :(得分:-1)

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

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