Python 查找每行中的最后一列匹配模式

Python 查找每行中的最后一列匹配模式,python,regex,pandas,Python,Regex,Pandas,我有一个包含几列“操作”的数据框。如何找到与模式匹配的最后一个操作并返回其列索引或标签 我的数据: name action_1 action_2 action_3 bill referred referred bob introduced referred referred mary introduced june introduced referred dale referred do

我有一个包含几列“操作”的数据框。如何找到与模式匹配的最后一个操作并返回其列索引或标签

我的数据:

name    action_1    action_2    action_3
bill    referred    referred    
bob     introduced  referred    referred
mary    introduced      
june    introduced  referred    
dale    referred        
donna   introduced
我想要的是:

name    action_1    action_2    action_3    last_referred
bill    referred    referred                action_2
bob     introduced  referred    referred    action_3
mary    introduced                          NA
june    introduced  referred                action_2
dale    referred                            action_1
donna   introduced                          NA

只需沿
轴=1使用
apply
函数,并将
pattern
参数作为附加参数传递给该函数即可

In [3]: def func(row, pattern):
            referrer = np.nan
            for key in row.index:
                if row[key] == pattern:
                    referrer = key
            return referrer
        df['last_referred'] = df.apply(func, pattern='referred', axis=1)
        df
Out[3]:     name    action_1  action_2  action_3 last_referred
        0   bill    referred  referred      None      action_2
        1    bob  introduced  referred  referred      action_3
        2   mary  introduced                               NaN
        3   june  introduced  referred                action_2
        4   dale    referred                          action_1
        5  donna  introduced                               NaN

您可以使用熊猫。熔化
和群比:

In [123]: molten = pd.melt(df, id_vars='name', var_name='last_referred')

In [124]: molten
Out[124]:
     name last_referred       value
0    bill      action_1    referred
1     bob      action_1  introduced
2    mary      action_1  introduced
3    june      action_1  introduced
4    dale      action_1    referred
5   donna      action_1  introduced
6    bill      action_2    referred
7     bob      action_2    referred
8    mary      action_2         NaN
9    june      action_2    referred
10   dale      action_2         NaN
11  donna      action_2         NaN
12   bill      action_3         NaN
13    bob      action_3    referred
14   mary      action_3         NaN
15   june      action_3         NaN
16   dale      action_3         NaN
17  donna      action_3         NaN

In [125]: gb = molten.groupby('name')

In [126]: col = gb.apply(lambda x: x[x.value == 'referred'].tail(1)).last_referred

In [127]: col.index = col.index.droplevel(1)

In [128]: col
Out[128]:
name
bill    action_2
bob     action_3
dale    action_1
june    action_2
Name: last_referred, dtype: object

In [129]: newdf = df.join(col, on='name')

In [130]: newdf
Out[130]:
    name    action_1  action_2  action_3 last_referred
0   bill    referred  referred       NaN      action_2
1    bob  introduced  referred  referred      action_3
2   mary  introduced       NaN       NaN           NaN
3   june  introduced  referred       NaN      action_2
4   dale    referred       NaN       NaN      action_1
5  donna  introduced       NaN       NaN           NaN

还可以使用idxmax,它返回最大值的第一个索引,否则返回第一个索引。这确实需要添加一个额外的“NA”列,因此有点混乱

revcols = df.columns.values.tolist()
revcols.reverse()
tmpdf = df=='referred'
tmpdf['NA'] = False
lastrefer = tmpdf[['NA']+revcols].idxmax(axis=1)

矢量化方法,使用
arange
查找最后一个索引
max
,并进行连接:

df['last_referred'] = np.r_[[np.NaN], df.columns][
        ((df == 'referred') * (np.arange(df.shape[1]) + 1)).max(axis=1).values]

说明:

我们希望在每一行中找到最右边的单元格,该单元格的值为
“referenced”

>>> df == 'referred'
    name action_1 action_2 action_3
0  False     True     True    False
1  False    False     True     True
2  False    False    False    False
3  False    False     True    False
4  False     True    False    False
5  False    False    False    False
一个选项是,但它给出了第一个(即最左边的)实例。然而,假设我们可以用它们的列索引替换
True
值,我们可以只使用normal
max
。由于
True
1
False
0
,因此我们可以通过与整数范围相乘来实现这一点
[0,1,2,…]
垂直广播:

>>> np.arange(df.shape[1])
array([0, 1, 2, 3])
>>> (df == 'referred') * np.arange(df.shape[1])
   name  action_1  action_2  action_3
0     0         1         2         0
1     0         0         2         3
2     0         0         0         0
3     0         0         2         0
4     0         1         0         0
5     0         0         0         0
>>> ((df == 'referred') * np.arange(df.shape[1])).max(axis=1)
0    2
1    3
2    0
3    2
4    1
5    0
dtype: int32
但有一个问题:我们无法区分“名称”列中的
“引用的”
和根本不发生的区别。容易修复;只需从1开始整数范围:

>>> ((df == 'referred') * (np.arange(df.shape[1]) + 1)).max(axis=1)
0    3
1    4
2    0
3    3
4    2
5    0
dtype: int32
现在只需使用此数组索引到列名中:

>>> df.columns[((df == 'referred') * (np.arange(df.shape[1]) + 1)).max(axis=1).values]
IndexError: index 4 is out of bounds for size 4
哎呀!我们需要将
0
显示为
NaN
,并将其余的列转换过来。我们可以使用连接数组的
np.r\uu
来实现这一点:

>>> np.r_[[np.NaN], df.columns]
array([nan, 'name', 'action_1', 'action_2', 'action_3'], dtype=object)
>>> np.r_[[np.NaN], df.columns][
        ((df == 'referred') * (np.arange(df.shape[1]) + 1)).max(axis=1).values]
array(['action_2', 'action_3', nan, 'action_2', 'action_1', nan], dtype=object)

当我开始理解融化后,我就更多地使用它了!这是一个相当大的锤子:)就像regexp;)我同意,我只是总是给OP提供最简单、可预测的速度解决方案,让你体验异国情调:)
apply
应该总是
O(N)
,如果我没有错的话。我甚至不知道如何计算
melt
groupby
的行为<代码>O(?):)我的解决方案对于这种情况来说可能太复杂了……我总是处于
模式@就渐近性能而言,ecatmur的解决方案将是最容易分析的。我刚刚在一个巨大的数据帧上计时:ecatmur:222ms,你的:1.33s,我的5.97s。很好!OP当然应该和@ecatmur一起使用。我想你可以用几种不同的方式进行矢量化。我很欣赏@ecatmur答案的速度,但我不明白。我不是在处理一个庞大的数据集(应该在我的原始问题中注意到这一点),所以我欣赏这里更直接的方法。如果你评估@ecatmur的答案,你会很快找到它,这篇文章写得太简洁了。这绝对是最快的选择:)如果你能理解的话:D这篇文章最好能添加一些解释。+1很好。仅供参考,在pandas 0.12中,您必须访问
[]
中的
值。我实际上正在考虑使用cumsum,但这很好!哇,太快了!仅供参考,在master中我得到了
索引器错误:不支持的迭代器索引
:(@AndyHayden是的,就像@PhillipCloud说你需要
。值
。我已经在上面修复了它,因为它也适用于旧版本。