计算 pandas 中分组元素之间的差异

Calculate difference between grouped elements in pandas

我有一个如下所示的数据框:

df = pd.DataFrame({'patient_id': ['p1', 'p2', 'p3', 'p1', 'p2', 'p3'],
              'treatment_time': ['pre', 'pre', 'pre', 'post', 'post', 'post'],
              'val1': [1, 4, 9, 2, 6, 10],
              'val2': [3, 5, 11, 1, 4, 9],
              'val3': [2, 4, 6, 3, 5, 7],
              })
print(df)

  patient_id treatment_time  val1  val2  val3
0         p1            pre     1     3     2
1         p2            pre     4     5     4
2         p3            pre     9    11     6
3         p1           post     2     1     3
4         p2           post     6     4     5
5         p3           post    10     9     7

我的目标是按 patient_idtreatment_time 对数据进行分组,并计算治疗前和 post-治疗值之间的差异。预期输出应如下所示:

  patient_id  val1  val2  val3
0         p1   1.0  -2.0   1.0
1         p2   2.0  -1.0   1.0
2         p3   1.0  -2.0   1.0

使用DataFrame.set_index with subtract in MultiIndex DataFrame selected by DataFrame.xs:

df1 = df.set_index(['patient_id','treatment_time'])
print (df1)
                           val1  val2  val3
patient_id treatment_time                  
p1         pre                1     3     2
p2         pre                4     5     4
p3         pre                9    11     6
p1         post               2     1     3
p2         post               6     4     5
p3         post              10     9     7

df = df1.xs('post', level=1).sub(df1.xs('pre', level=1)).reset_index()
print (df)
  patient_id  val1  val2  val3
0         p1     1    -2     1
1         p2     2    -1     1
2         p3     1    -2     1

如果每个 patient_id 总是只有一个 'pre' 和 'post',并且行被排序为总是先有前,后有 post,那么一个简单的技巧就是是:

cols = df.filter(like='val').columns
out = df.groupby('patient_id')[cols].diff().dropna(how='all')

输出:

>>> out
   val1  val2  val3
3   1.0  -2.0   1.0
4   2.0  -1.0   1.0
5   1.0  -2.0   1.0
对行进行排序(如果需要)
df = df.sort_values(by='treatment_time', ascending=False)

创建一个 MultiIndex 数据帧,通过 pd.pivot 并从 post 中减去 pre:

temp = df.pivot('patient_id', 'treatment_time').swaplevel(axis=1)

temp['post'] - temp['pre']

             val1  val2  val3
patient_id                  
p1             1    -2     1
p2             2    -1     1
p3             1    -2     1