如何使用列 header 作为填充值将 Panda Dataframe 从 5 列转换为 1 列?

How do I transform a Panda Dataframe from 5 columns into 1 column using column header as value to populate?

这是从调查中收集的数据,其中有一个单选按钮 select 从 5 个选项中选出 1 个。存储在该列中的是一个简单的 1 作为标志,表示它是 selected.

我想以 headers 列作为值的单个列结束。有人建议在我的数据帧上使用 IDXMAX 方法,但当我查看文档时,我无法真正弄清楚如何应用它。看起来它确实对此很有用...

我有一个数据框:

 old = pd.DataFrame({'a FINSEC_SA' : [1,'NaN','NaN','NaN','NaN',1,'NaN'],
 'b FINSEC_A' : ['NaN',1,'NaN','NaN','NaN','NaN','NaN'],
 'c FINSEC_NO' : ['NaN','NaN',1,'NaN','NaN','NaN','NaN'],
 'd FINSEC_D' : ['NaN','NaN','NaN',1,'NaN','NaN',1],
 'e FINSEC_SD' : ['NaN','NaN','NaN','NaN',1,'NaN','NaN']})

我希望得到这样的数据框:

new = pd.DataFrame({'Financial Security':['a FINSEC_SA','b FINSEC_A',
'c FINSEC_NO','d FINSEC_D','e FINSEC_SD','a FINSEC_SA','d FINSEC_D']})

我只有大约 65k 行数据,因此性能对我来说不是最重要的。我最感兴趣的是学习一种好的方法来做到这一点——希望它相当简单。如果 idxmax 很容易做到这一点,那就太好了。

您可以直接使用 idxmax 后跟 reset_index 来实现。

df = old.idxmax(axis=1).reset_index().drop('index', axis=1).rename(columns={0:'Financial'})

print(df)

      Financial
0   a FINSEC_SA
1   b FINSEC_A
2   c FINSEC_NO
3   d FINSEC_D
4   e FINSEC_SD
5   a FINSEC_SA
6   d FINSEC_D

解释:
1. idxmax select 最大。跨列逐行赋值。
2. drop 删除不需要的列,然后删除 duplicate 个值。
3. 最后,我们根据需要 rename 列。

idxmax 只能用于数字。所以首先,我们需要将 'NaN'(字符串)转换为 np.NaN(数值)。然后我们可以将每一列转换成一个数值系列:

old = old.replace('NaN', np.NaN)
old = old.apply(pd.to_numeric)

或者,您可以在一行中执行此操作:

old = old.apply(pd.to_numeric, errors='coerce')

最后,我们可以运行 idxmax。您所要做的就是指定轴。 axis=1获取每行1(最大值)的位置,axis=0获取每列1的位置

new = old.idxmax(axis=1)

你可以运行一行代码(如果你不需要旧的副本):

new = old.apply(pd.to_numeric, errors='coerce').idxmax(axis=1)

在下面的代码中,我创建了一个单独检查 NaN 的函数,因为我认为在实际数据中你会有 np.NaN 而不是 'NaN' (字符串)。您可以相应地修改字符串

def isNaN(num):
    return num == 'NaN'

def getval(x):
    if not isNaN(x['a FINSEC_SA']) : return 'a FINSEC_SA'
    if not isNaN(x['b FINSEC_A']) : return 'b FINSEC_A'
    if not isNaN(x['c FINSEC_NO']) : return 'c FINSEC_NO'
    if not isNaN(x['d FINSEC_D']) : return 'd FINSEC_D'
    if not isNaN(x['e FINSEC_SD']) : return 'e FINSEC_SD'


old.apply(getval, axis=1)

这是可读但效率不高的答案。可以使用熔化功能以更有效的方式获得相同的答案 -

old['id'] = old.index
new = pd.melt(old, id_vars= 'id', var_name = 'Financial')
new = new[new['value'] != 'NaN'].drop('value', axis=1).sort_index(axis=0)