【问题标题】:Getting most recent observation & date from several columns从多个列中获取最新的观察和日期
【发布时间】:2017-12-13 11:01:21
【问题描述】:

拿下面的玩具DataFrame:

data = np.arange(35, dtype=np.float32).reshape(7, 5)
data = pd.concat((
    pd.DataFrame(list('abcdefg'), columns=['field1']),
    pd.DataFrame(data, columns=['field2', '2014', '2015', '2016', '2017'])),
    axis=1)

data.iloc[1:4, 4:] = np.nan
data.iloc[4, 3:] = np.nan

print(data)
  field1  field2  2014  2015  2016  2017
0      a     0.0   1.0   2.0   3.0   4.0
1      b     5.0   6.0   7.0   NaN   NaN
2      c    10.0  11.0  12.0   NaN   NaN
3      d    15.0  16.0  17.0   NaN   NaN
4      e    20.0  21.0   NaN   NaN   NaN
5      f    25.0  26.0  27.0  28.0  29.0
6      g    30.0  31.0  32.0  33.0  34.0

我想将“年份”列 (2014-2017) 替换为两个字段:最近的非空观测值和该观测值的对应年份。假设field1 是唯一键。 (我不想做任何 groupby 操作,每条记录只有 1 行。)即:

  field1  field2   obs  date
0      a     0.0   4.0  2017
1      b     5.0   7.0  2015
2      c    10.0  12.0  2015
3      d    15.0  17.0  2015
4      e    20.0  21.0  2014
5      f    25.0  29.0  2017
6      g    30.0  34.0  2017

我已经走到这一步了:

pd.melt(data, id_vars=['field1', 'field2'], 
        value_vars=['2014', '2015', '2016', '2017'])\
    .dropna(subset=['value'])

   field1  field2 variable  value
0       a     0.0     2014    1.0
1       b     5.0     2014    6.0
2       c    10.0     2014   11.0
3       d    15.0     2014   16.0
4       e    20.0     2014   21.0
5       f    25.0     2014   26.0
6       g    30.0     2014   31.0
# ...

但我正在努力解决如何转回所需的格式。

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    也许:

    d2 = data.melt(id_vars=["field1", "field2"], var_name="date", value_name="obs").dropna(subset=["obs"])
    d2["date"] = d2["date"].astype(int)
    df = d2.loc[d2.groupby(["field1", "field2"])["date"].idxmax()]
    

    这给了我

       field1  field2  date   obs
    21      a     0.0  2017   4.0
    8       b     5.0  2015   7.0
    9       c    10.0  2015  12.0
    10      d    15.0  2015  17.0
    4       e    20.0  2014  21.0
    26      f    25.0  2017  29.0
    27      g    30.0  2017  34.0
    

    【讨论】:

      【解决方案2】:

      下面的方法呢:

      In [160]: df
      Out[160]:
        field1  field2  2014  2015  2016  2017
      0      a     0.0   1.0   2.0   3.0 -10.0
      1      b     5.0   6.0   7.0   NaN   NaN
      2      c    10.0  11.0  12.0   NaN   NaN
      3      d    15.0  16.0  17.0   NaN   NaN
      4      e    20.0  21.0   NaN   NaN   NaN
      5      f    25.0  26.0  27.0  28.0  29.0
      6      g    30.0  31.0  32.0  33.0  34.0
      
      In [180]: df.groupby(lambda x: 'obs' if x.isdigit() else x, axis=1) \
           ...:   .last() \
           ...:   .assign(date=df.filter(regex='^\d{4}').loc[:, ::-1].notnull().idxmax(1))
      Out[180]:
        field1  field2   obs  date
      0      a     0.0 -10.0  2017
      1      b     5.0   7.0  2015
      2      c    10.0  12.0  2015
      3      d    15.0  17.0  2015
      4      e    20.0  21.0  2014
      5      f    25.0  29.0  2017
      6      g    30.0  34.0  2017
      

      【讨论】:

      • 我不确定这个——IIUC,OP 想要最新的有效值,而不是最大值。在给定的数据集中,它们是相同的,但如果(例如)2017 年的 a 为 -10,我认为这就是我们应该返回的值。
      • @DSM,感谢您的澄清!我想如果我将max() 替换为last() 就可以了...
      • 但是现在您使用的是最后一个 obs 而是最大 date(所以是 2016 年,而不是 2017 年)。 [澄清一下,我的意思是“达到最大值的日期”,我只是懒得弄错了。]你需要相当于idxlast()(它不存在,但是YKWIM。)
      【解决方案3】:

      last_valid_index + agg('last')

      A=data.iloc[:,2:].apply(lambda x : x.last_valid_index(),1)
      B=data.groupby(['value'] * data.shape[1], 1).agg('last')
      data['date']=A
      data['obs']=B
      
      data
      Out[1326]: 
        field1  field2  2014  2015  2016  2017  date   obs
      0      a     0.0   1.0   2.0   3.0   4.0  2017   4.0
      1      b     5.0   6.0   7.0   NaN   NaN  2015   7.0
      2      c    10.0  11.0  12.0   NaN   NaN  2015  12.0
      3      d    15.0  16.0  17.0   NaN   NaN  2015  17.0
      4      e    20.0  21.0   NaN   NaN   NaN  2014  21.0
      5      f    25.0  26.0  27.0  28.0  29.0  2017  29.0
      6      g    30.0  31.0  32.0  33.0  34.0  2017  34.0
      

      通过使用assign,我们可以将它们排成一行

      data.assign(date=data.iloc[:,2:].apply(lambda x : x.last_valid_index(),1),obs=data.groupby(['value'] * data.shape[1], 1).agg('last'))
      Out[1340]: 
        field1  field2  2014  2015  2016  2017  date   obs
      0      a     0.0   1.0   2.0   3.0   4.0  2017   4.0
      1      b     5.0   6.0   7.0   NaN   NaN  2015   7.0
      2      c    10.0  11.0  12.0   NaN   NaN  2015  12.0
      3      d    15.0  16.0  17.0   NaN   NaN  2015  17.0
      4      e    20.0  21.0   NaN   NaN   NaN  2014  21.0
      5      f    25.0  26.0  27.0  28.0  29.0  2017  29.0
      6      g    30.0  31.0  32.0  33.0  34.0  2017  34.0
      

      【讨论】:

        【解决方案4】:

        使用sort_valuesdrop_duplicates 的另一种可能性:

        data.melt(id_vars=["field1", "field2"], var_name="date", 
                  value_name="obs")\
            .dropna(subset=['obs'])\
            .sort_values(['field1', 'date'], ascending=[True, False])\
            .drop_duplicates('field1', keep='first')
        

        给你

           field1  field2  date   obs
        21      a     0.0  2017   4.0
        8       b     5.0  2015   7.0
        9       c    10.0  2015  12.0
        10      d    15.0  2015  17.0
        4       e    20.0  2014  21.0
        26      f    25.0  2017  29.0
        27      g    30.0  2017  34.0
        

        【讨论】:

        • @bradsolomon,有选择的生活总是更美好
        猜你喜欢
        • 2020-03-02
        • 2020-11-11
        • 1970-01-01
        • 2020-12-12
        • 1970-01-01
        • 2022-01-08
        • 2020-02-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多