在新数据帧中返回第一个匹配的值/列名称

时间:2016-12-11 19:40:10

标签: python pandas

import pandas as pd
import numpy as np
rng = pd.date_range('1/1/2011', periods=6, freq='H')
df = pd.DataFrame({'A': [0, 1, 2, 3, 4,5],
                   'B': [0, 1, 2, 3, 4,5],
                   'C': [0, 1, 2, 3, 4,5],
                   'D': [0, 1, 2, 3, 4,5],
                   'E': [1, 2, 3, 3, 7,6],
                   'F': [1, 1, 3, 3, 7,6],
                   'G': [0, 0, 1, 0, 0,0]

                  },
                 index=rng)

一个简单的数据框,可以帮我解释一下:

df


                    A   B   C   D   E   F   G
2011-01-01 00:00:00 0   0   0   0   1   1   0
2011-01-01 01:00:00 1   1   1   1   2   1   0
2011-01-01 02:00:00 2   2   2   2   3   3   1
2011-01-01 03:00:00 3   3   3   3   3   3   0
2011-01-01 04:00:00 4   4   4   4   7   7   0
2011-01-01 05:00:00 5   5   5   5   6   6   0

当我过滤大于2的值时,我得到以下输出:

df[df >= 2]

                     A  B   C   D   E   F   G
2011-01-01 00:00:00 NaN NaN NaN NaN NaN NaN NaN
2011-01-01 01:00:00 NaN NaN NaN NaN 2.0 NaN NaN
2011-01-01 02:00:00 2.0 2.0 2.0 2.0 3.0 3.0 NaN
2011-01-01 03:00:00 3.0 3.0 3.0 3.0 3.0 3.0 NaN
2011-01-01 04:00:00 4.0 4.0 4.0 4.0 7.0 7.0 NaN
2011-01-01 05:00:00 5.0 5.0 5.0 5.0 6.0 6.0 NaN

对于每一行,我想知道哪一列首先具有匹配值(从左到右工作)。所以在2011-01-01 01:00:00的行上,它是E行,而值是2.0。

enter image description here

期望的输出:

我想得到的是一个新的数据框,其中第一个匹配值位于名为“Value”的列中,另一列名为“From Col”,它捕获了来自的列名称。

如果没有看到匹配则从最后一列输出(在这种情况下为G)。谢谢你的帮助。

                       "Value" "From Col"   
    2011-01-01 00:00:00    NaN  G
    2011-01-01 01:00:00    2    E
    2011-01-01 02:00:00    2    A
    2011-01-01 03:00:00    3    A
    2011-01-01 04:00:00    4    A
    2011-01-01 05:00:00    5    A

3 个答案:

答案 0 :(得分:2)

试试这个:

def get_first_valid(ser):
    if len(ser) == 0:
        return pd.Series([np.nan,np.nan])

    mask = pd.isnull(ser.values)
    i = mask.argmin()
    if mask[i]:
        return pd.Series([np.nan, ser.index[-1]])
    else:
        return pd.Series([ser[i], ser.index[i]])


In [113]: df[df >= 2].apply(get_first_valid, axis=1)
Out[113]:
                       0  1
2011-01-01 00:00:00  NaN  G
2011-01-01 01:00:00  2.0  E
2011-01-01 02:00:00  2.0  A
2011-01-01 03:00:00  3.0  A
2011-01-01 04:00:00  4.0  A
2011-01-01 05:00:00  5.0  A

或:

In [114]: df[df >= 2].T.apply(get_first_valid).T
Out[114]:
                       0  1
2011-01-01 00:00:00  NaN  G
2011-01-01 01:00:00    2  E
2011-01-01 02:00:00    2  A
2011-01-01 03:00:00    3  A
2011-01-01 04:00:00    4  A
2011-01-01 05:00:00    5  A

PS我拿了一个Series.first_valid_index()函数的源代码,然后把它弄脏了......

说明:

In [221]: ser = pd.Series([np.nan, np.nan, 5, 7, np.nan])

In [222]: ser
Out[222]:
0    NaN
1    NaN
2    5.0
3    7.0
4    NaN
dtype: float64

In [223]: mask = pd.isnull(ser.values)

In [224]: mask
Out[224]: array([ True,  True, False, False,  True], dtype=bool)

In [225]: i = mask.argmin()

In [226]: i
Out[226]: 2

In [227]: ser.index[i]
Out[227]: 2

In [228]: ser[i]
Out[228]: 5.0

答案 1 :(得分:2)

首先,根据条件过滤值并删除包含所有NaNs的行。然后,使用idxmax返回True条件的第一次出现。这类似于我们的第一个系列。

要创建第二个系列,请迭代第一个系列的(索引,值)元组对,同时附加原始DF中的这些位置。

ser1 = (df[df.ge(2)].dropna(how='all').ge(2)).idxmax(1)
ser2 = pd.concat([pd.Series(df.loc[i,r], pd.Index([i])) for i, r in ser1.iteritems()])

创建一个新的DF,其索引与原始DF的索引相关,并用 From Col 中的缺失值填充其最后一列名称

req_df = pd.DataFrame({"From Col": ser1, "Value": ser2}, index=df.index)
req_df['From Col'].fillna(df.columns[-1], inplace=True)
req_df

enter image description here

答案 2 :(得分:0)

我不使用pandas,所以这可以被视为一个脚注,但在纯python中,也有可能使用None找到第一个非reduce索引。

>>> a
[None, None, None, None, 6, None, None, None, 3, None]

>>> print( reduce(lambda x, y: (x or y[1] and y[0]), enumerate(a), None))
4