【问题标题】:How to subtract values within a hierarchical/multi-level index in Pandas within the same column如何在同一列中的 Pandas 中减去层次/多级索引中的值
【发布时间】:2021-10-20 16:18:24
【问题描述】:

我正在尝试查找特定会话(我的索引)中的时间变化——我的数据框如下所示:

                        time
sess_id     vis_id      

id1         vis_id1      t_0
            vis_id1      t_1
            vis_id1      t_2

id2         vis_id2      t_0
            vis_id2      t_1
            vis_id2      t_2

我想创建一个名为delta_t(时间变化)的列,它递归地减去时间戳——其中每个会话的最后一次包含填充字符,如破折号或其他内容

                        time      delta_t
sess_id     vis_id      

id1         vis_id1      t_0     (t_1 - t_0) 
            vis_id1      t_1     (t_2 - t_1)
            vis_id1      t_2         - 

id2         vis_id2      t_3     (t_4 - t_3)
            vis_id2      t_4     (t_5 - t_4)
            vis_id2      t_5         -

    
    

【问题讨论】:

  • 请提供具有预期输出的可重现示例

标签: python pandas dataframe data-analysis multi-index


【解决方案1】:

我们可以groupby shift 相对于level=0level="sess_id" 得到下一行的值,并从time 中减去:

df['delta_t'] = df.groupby(level='sess_id')['time'].shift(-1) - df['time']

示例 DataFrame 和输出:

                               time         delta_t
sess_id vis_id                                     
id1     vis_id1 2021-01-11 00:00:00 6 days 04:27:31
        vis_id1 2021-01-17 04:27:31 4 days 03:45:26
        vis_id1 2021-01-21 08:12:57             NaT
id2     vis_id2 2021-01-28 15:18:32 7 days 17:57:56
        vis_id2 2021-02-05 09:16:28 4 days 01:41:58
        vis_id2 2021-02-09 10:58:26             NaT

我们可以 groupby diff 然后groupby shift 但这涉及到 2 个 groupbys:

df['delta_t'] = (
    df.groupby(level='sess_id')['time'].diff()
        .groupby(level='sess_id').shift(-1)
)

如果在NaT 上需要'-',可以使用np.where 将Timedelta 转换为字符串并用'-' 填充:

# Calculate Delta
df['delta_t'] = df.groupby(level='sess_id')['time'].shift(-1) - df['time']
# Change dtype and add in '-'
df['delta_t'] = np.where(df['delta_t'].notna(), df['time'].astype(str), '-')

或者可以用“-”转换为strreplace“NaT”:

# Calculate Delta, convert to String, replace "NaT" with "-"
df['delta_t'] = (
        df.groupby(level='sess_id')['time'].shift(-1) - df['time']
).astype(str).replace('NaT', '-')

df:

                               time          delta_t
sess_id vis_id                                      
id1     vis_id1 2021-01-11 00:00:00  6 days 04:27:31
        vis_id1 2021-01-17 04:27:31  4 days 03:45:26
        vis_id1 2021-01-21 08:12:57                -
id2     vis_id2 2021-01-28 15:18:32  7 days 17:57:56
        vis_id2 2021-02-05 09:16:28  4 days 01:41:58
        vis_id2 2021-02-09 10:58:26                -

DataFrame 构造函数和导入:

import pandas as pd

df = pd.DataFrame(
    {'time': pd.to_datetime(['2021-01-11 00:00:00', '2021-01-17 04:27:31',
                             '2021-01-21 08:12:57', '2021-01-28 15:18:32',
                             '2021-02-05 09:16:28', '2021-02-09 10:58:26'])},
    index=pd.MultiIndex.from_arrays((['id1'] * 3 + ['id2'] * 3,
                                     ['vis_id1'] * 3 + ['vis_id2'] * 3),
                                    names=['sess_id', 'vis_id'])
)

【讨论】:

  • 哇!感谢您的迅速回复。非常感谢
  • 到目前为止运行良好..我得到负 delta ts 所以我想我需要按时间排序 有没有一种特定的方式我应该对这些值进行排序?我当时正在考虑,但我也在每个会话 id 的最后一些 roes 上获得了 delta t 的值
  • 如果您正在排序以避免负增量,您应该在减去之前进行排序df = df.sort_values('time') 减法前,df = df.sort_index() 减法后按顺序返回。
  • df['delta_t'] = df.sort_values(by = 'time').groupby(level = 'sid')['time'].shift(-1) - df['time'] 给我以下错误:无法处理非唯一的多索引!编辑:我试试你上面的代码 rn
  • 嘿!我知道这是超级随机的,但我想知道您是否可以帮助我解决我最近发布的问题?似乎就在你的胡同/类似问题上。
【解决方案2】:

@Henry Ecker 打败了我,但这是我的答案:

t = [
    {'idx1': 'id1', 'idx2':         'vis_id1',    'val':  1},
    {'idx1': 'id1', 'idx2':         'vis_id2',    'val':  2},
    {'idx1': 'id1', 'idx2':         'vis_id3',    'val':  3},
    {'idx1': 'id2', 'idx2':         'vis_id4',    'val':  4},
    {'idx1': 'id2', 'idx2':         'vis_id5',    'val':  5},
    {'idx1': 'id2', 'idx2':         'vis_id6',    'val':  6},
]
df = pd.DataFrame(t).set_index(['idx1', 'idx2'])

(df
 .reset_index()
 .groupby('idx1')
 .apply(lambda df: df
        .set_index('idx2')
        .sort_index()
        .pipe(lambda df: 
              pd.Series(
                  data=df['val'].pipe(lambda s: s.iloc[1:].values - s.iloc[:-1].values).tolist() + ['-'], 
                  index=df.index
              )
       )
))

这是输出:

idx1  idx2   
id1   vis_id1    1
      vis_id2    1
      vis_id3     -
id2   vis_id4    1
      vis_id5    1
      vis_id6     -

不过,我认为 Henry 的更完整,因为如果第二个中的 id 与您的示例相同,我的就不起作用。

但是,我要指出,从数据索引的角度来看,使用相同(多)id 索引多行是不好的做法。

【讨论】:

    猜你喜欢
    • 2016-09-02
    • 2018-09-11
    • 2016-09-02
    • 1970-01-01
    • 2020-12-05
    • 2021-04-14
    • 1970-01-01
    • 2021-12-01
    • 2018-07-07
    相关资源
    最近更新 更多