我想知道第一年有各种项目的收入。
鉴于以下内容,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
有谁知道更优雅的解决方案?
答案 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
部分功劳:piRSquared和Andy的答案
答案 2 :(得分:-1)
将此代码应用于只有一行的数据框,以返回该行中包含空值的第一列。
row.columns[~(row.loc[:].isna()).all()][-1]