代码之家  ›  专栏  ›  技术社区  ›  Mohamed Thasin ah

如何使用panda获取每行的最后n个值

  •  3
  • Mohamed Thasin ah  · 技术社区  · 6 年前

    我有一个df,它和下面的非常相似。它有许多列,其中一些包含NaN。我想从每行中得到最后n个元素,不包括nan。其中n在这里表示3。

    输入:

       col1  col2  col3  col4   col5  col6    col7  col8   col9   col10  col11  \
    0   NaN   NaN  23.0    23   23.0   NaN    23.0  23.0  123.0     NaN    NaN   
    1   NaN   NaN   NaN    45   12.0  23.0    23.0   NaN    NaN     NaN    NaN   
    2  45.0  56.0  34.0    23  323.0  12.0     NaN   NaN    NaN     NaN    NaN   
    3   NaN   NaN  34.0    65    NaN  65.0  2343.0   NaN    NaN  2344.0    2.0   
    4   NaN   NaN   NaN     5  675.0  34.0    34.0  34.0    NaN     NaN    NaN   
    5  34.0  45.0  45.0    45    NaN   NaN     NaN   NaN    NaN     NaN    NaN   
    
       col12  col13   I  
    0    NaN    NaN  r1  
    1    NaN    NaN  r2  
    2    NaN    NaN  r3  
    3  324.0  234.0  r4  
    4    NaN    NaN  r5  
    5    NaN    NaN  r6 
    

    输出:

       col1  col2  col3  col4   col5  col6    col7  col8   col9   col10  col11  \
    0   NaN   NaN  23.0    23   23.0   NaN    23.0  23.0  123.0     NaN    NaN   
    1   NaN   NaN   NaN    45   12.0  23.0    23.0   NaN    NaN     NaN    NaN   
    2  45.0  56.0  34.0    23  323.0  12.0     NaN   NaN    NaN     NaN    NaN   
    3   NaN   NaN  34.0    65    NaN  65.0  2343.0   NaN    NaN  2344.0    2.0   
    4   NaN   NaN   NaN     5  675.0  34.0    34.0  34.0    NaN     NaN    NaN   
    5  34.0  45.0  45.0    45    NaN   NaN     NaN   NaN    NaN     NaN    NaN   
    
       col12  col13   I                 res1  
    0    NaN    NaN  r1  [23.0, 23.0, 123.0]  
    1    NaN    NaN  r2   [12.0, 23.0, 23.0]  
    2    NaN    NaN  r3    [23, 323.0, 12.0]  
    3  324.0  234.0  r4  [2.0, 324.0, 234.0]  
    4    NaN    NaN  r5   [34.0, 34.0, 34.0]  
    5    NaN    NaN  r6     [45.0, 45.0, 45] 
    

    到目前为止,我使用下面的代码得到了解决方案。

    df['res1']=df.apply(lambda x:x.dropna().values.tolist()[len(x.dropna().values.tolist())-4:len(x.dropna().values.tolist())-1],axis=1)
    

    我的解决方案看起来非常无效,首先我使用lambda将代码性能降低,并重复相同的方法来获取索引。

    我希望为这个问题得到明确的性能解决方案。

    输入数据帧文件是 here

    df=pd.read_csv('s1.csv')#code to reproduce input
    
    4 回复  |  直到 6 年前
        1
  •  4
  •   jezrael    6 年前

    解决方案:如果每行有更多非丢失行,如treshold:

    使用麻木 justify 功能:

    df['res1'] = justify(df.iloc[:, :-1].values, invalid_val=np.nan, side='right')[:, -3:].tolist()
    print (df)
       col1  col2  col3  col4   col5  col6    col7  col8   col9   col10  col11  \
    0   NaN   NaN  23.0    23   23.0   NaN    23.0  23.0  123.0     NaN    NaN   
    1   NaN   NaN   NaN    45   12.0  23.0    23.0   NaN    NaN     NaN    NaN   
    2  45.0  56.0  34.0    23  323.0  12.0     NaN   NaN    NaN     NaN    NaN   
    3   NaN   NaN  34.0    65    NaN  65.0  2343.0   NaN    NaN  2344.0    2.0   
    4   NaN   NaN   NaN     5  675.0  34.0    34.0  34.0    NaN     NaN    NaN   
    5  34.0  45.0  45.0    45    NaN   NaN     NaN   NaN    NaN     NaN    NaN   
    
       col12  col13   I                 res1  
    0    NaN    NaN  r1  [23.0, 23.0, 123.0]  
    1    NaN    NaN  r2   [12.0, 23.0, 23.0]  
    2    NaN    NaN  r3  [23.0, 323.0, 12.0]  
    3  324.0  234.0  r4  [2.0, 324.0, 234.0]  
    4    NaN    NaN  r5   [34.0, 34.0, 34.0]  
    5    NaN    NaN  r6   [45.0, 45.0, 45.0]
    

    如果没有,需要循环:

    #changed a bit https://stackoverflow.com/a/40835254
    def loop_compr_based(a, last):
        mask = ~np.isnan(a)
        stop = mask.sum(1).cumsum()
        start = np.append(0,stop[:-1])
        am = a[mask].tolist()
        out = np.array([am[start[i]:stop[i]][-last:] for i  in range(len(start))])
        return out
    
    df['res1'] = loop_compr_based(df.iloc[:, :-1].values, 5).tolist()
    print (df)
       col1  col2  col3  col4   col5  col6    col7  col8   col9   col10  col11  \
    0   NaN   NaN  23.0    23   23.0   NaN    23.0  23.0  123.0     NaN    NaN   
    1   NaN   NaN   NaN    45   12.0  23.0    23.0   NaN    NaN     NaN    NaN   
    2  45.0  56.0  34.0    23  323.0  12.0     NaN   NaN    NaN     NaN    NaN   
    3   NaN   NaN  34.0    65    NaN  65.0  2343.0   NaN    NaN  2344.0    2.0   
    4   NaN   NaN   NaN     5  675.0  34.0    34.0  34.0    NaN     NaN    NaN   
    5  34.0  45.0  45.0    45    NaN   NaN     NaN   NaN    NaN     NaN    NaN   
    
       col12  col13   I                                 res1  
    0    NaN    NaN  r1      [23.0, 23.0, 23.0, 23.0, 123.0]  
    1    NaN    NaN  r2             [45.0, 12.0, 23.0, 23.0]  
    2    NaN    NaN  r3      [56.0, 34.0, 23.0, 323.0, 12.0]  
    3  324.0  234.0  r4  [2343.0, 2344.0, 2.0, 324.0, 234.0]  
    4    NaN    NaN  r5       [5.0, 675.0, 34.0, 34.0, 34.0]  
    5    NaN    NaN  r6             [34.0, 45.0, 45.0, 45.0]  
    
        2
  •  3
  •   BENY    6 年前

    使用 melt 具有 groupby

    df['res1']=df.melt('I').dropna().groupby('I')['value'].apply(lambda x : x.tolist()[-3:]).tolist() 
    # melt the data , then drop nan , since you want the not nan values of last 3 , then we groupby slice the last three. 
    df
       col1  col2  col3  col4   col5  col6    col7  col8   col9   col10  col11  \
    0   NaN   NaN  23.0    23   23.0   NaN    23.0  23.0  123.0     NaN    NaN   
    1   NaN   NaN   NaN    45   12.0  23.0    23.0   NaN    NaN     NaN    NaN   
    2  45.0  56.0  34.0    23  323.0  12.0     NaN   NaN    NaN     NaN    NaN   
    3   NaN   NaN  34.0    65    NaN  65.0  2343.0   NaN    NaN  2344.0    2.0   
    4   NaN   NaN   NaN     5  675.0  34.0    34.0  34.0    NaN     NaN    NaN   
    5  34.0  45.0  45.0    45    NaN   NaN     NaN   NaN    NaN     NaN    NaN   
       col12  col13   I                 res1  
    0    NaN    NaN  r1  [23.0, 23.0, 123.0]  
    1    NaN    NaN  r2   [12.0, 23.0, 23.0]  
    2    NaN    NaN  r3  [23.0, 323.0, 12.0]  
    3  324.0  234.0  r4  [2.0, 324.0, 234.0]  
    4    NaN    NaN  r5   [34.0, 34.0, 34.0]  
    5    NaN    NaN  r6   [45.0, 45.0, 45.0]  
    
        3
  •  3
  •   Space Impact    6 年前

    使用 apply + boolean index :

    df['res1'] = df.filter(like='col').apply(lambda x: x[x.notnull()].values[-3:].tolist(), 1)
    
    print(df)
       col1  col2  col3  col4   col5  col6    col7  col8   col9   col10  col11  \
    0   NaN   NaN  23.0    23   23.0   NaN    23.0  23.0  123.0     NaN    NaN   
    1   NaN   NaN   NaN    45   12.0  23.0    23.0   NaN    NaN     NaN    NaN   
    2  45.0  56.0  34.0    23  323.0  12.0     NaN   NaN    NaN     NaN    NaN   
    3   NaN   NaN  34.0    65    NaN  65.0  2343.0   NaN    NaN  2344.0    2.0   
    4   NaN   NaN   NaN     5  675.0  34.0    34.0  34.0    NaN     NaN    NaN   
    5  34.0  45.0  45.0    45    NaN   NaN     NaN   NaN    NaN     NaN    NaN   
    
       col12  col13   I                 res1  
    0    NaN    NaN  r1  [23.0, 23.0, 123.0]  
    1    NaN    NaN  r2   [12.0, 23.0, 23.0]  
    2    NaN    NaN  r3  [23.0, 323.0, 12.0]  
    3  324.0  234.0  r4  [2.0, 324.0, 234.0]  
    4    NaN    NaN  r5   [34.0, 34.0, 34.0]  
    5    NaN    NaN  r6   [45.0, 45.0, 45.0]  
    
        4
  •  1
  •   pathankhan.salman    6 年前

    快速而肮脏的方式:

    import pandas as pd
    import numpy as np
    
    df = pd.DataFrame()
    df['A'] = [1,2,3]
    df['B'] = [2,np.nan,np.nan]
    df['C'] = [3,4,5]
    df['D'] = [4,5,np.nan]
    df['E'] = [np.nan,6,np.nan]
    res_list = []
    
    for i, row in df.iterrows():
        res_list.append([x for x in list(sorted(row)) if not np.isnan(x)][0:3])
    
    df['res'] = res_list
    print(df)
    

    输出:

       A    B  C    D    E              res
    0  1  2.0  3  4.0  NaN  [1.0, 2.0, 3.0]
    1  2  NaN  4  5.0  6.0  [2.0, 4.0, 5.0]
    2  3  NaN  5  NaN  NaN       [3.0, 5.0]