【问题标题】:Pandas dataframe get value of last nonzero column熊猫数据框获取最后一个非零列的值
【发布时间】:2019-04-07 13:27:21
【问题描述】:

我有一个 pandas 数据框,其中包含 3 列,每列都包含用户在会话期间访问过的网站。

在某些情况下,用户可能在一个会话中没有访问过 3 个网站。这由 0 表示,表示没有访问过任何站点。

import pandas as pd

df = pd.DataFrame(data=[[5, 8, 1],[8,0,0],[1,17,0]], 
                  columns=['site1', 'site2', 'site3'])
print(df)

   site1  site2  site3
0      5      8      1
1      8      0      0
2      1     17      0

在上面的示例中,用户 0 访问了站点 5、8 和 1。用户 1 仅访问了站点 8,用户 2 访问了站点 1 和 17。

我想创建一个新列last_site,它显示用户在该会话中访问的最后一个站点。

我想要的结果是这样的:

   site1  site2  site3  last_site
0      5      8      1          1
1      8      0      0          8
2      1     17      0         17

如何使用 pandas 以简洁的方式做到这一点?

【问题讨论】:

    标签: python pandas dataframe


    【解决方案1】:

    使用通过替换0 值创建的错误值的前向填充,然后用iloc 选择最后一列:

    df['last'] = df.replace(0, np.nan).ffill(axis=1).iloc[:, -1].astype(int)
    print (df)
       site1  site2  site3  last
    0      5      8      1     1
    1      8      0      0     8
    2      1     17      0    17
    

    如果性能很重要,可以使用numpy

    a = df.values
    m = a != 0
    
    df['last'] = a[np.arange(m.shape[0]), m.shape[1]-m[:,::-1].argmax(1)-1]
    print (df)
       site1  site2  site3  last
    0      5      8      1     1
    1      8      0      0     8
    2      1     17      0    17
    

    【讨论】:

    • 这里的前向填充逻辑在各行中都非常出色:) +1
    • 是的,跨行正向填充是开箱即用的想法
    • 确实是伟大而即时的逻辑:-)。
    【解决方案2】:

    代码:

    df['last_site'] = df.apply(lambda x: x.iloc[x.nonzero()].iloc[-1], axis=1)
    

    输出:

       site1  site2  site3  last_site
    0      5      8      1          1
    1      8      0      0          8
    2      1     17      0         17
    

    【讨论】:

    • 好一个@Vishnudev +1!
    【解决方案3】:

    mask + ffill

    “纯熊猫”解决方案:

    df['last'] = df.mask(df.eq(0)).ffill(1).iloc[:, -1].astype(int)
    

    numba

    为了提高大量行/列的效率,numba 可以提供帮助。要了解为什么它比 argmax 更有效,请参阅 Efficiently return the index of the first value satisfying condition in array

    from numba import njit
    
    @njit
    def get_last_val(A):
        m, n = A.shape
        res = A[:, -1]
        for i in range(m):
            for j in range(n):
                if A[i, j] == 0:
                    res[i] = A[i, max(0, j-1)]
                    break
        return res
    
    df['last'] = get_last_val(df.values)
    

    【讨论】:

      猜你喜欢
      • 2017-03-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-30
      • 2021-02-04
      相关资源
      最近更新 更多