如何在一列上按一列分组,同时在整个数据帧上按另一列排序

  • 本文关键字:一列 数据帧 排序 python pandas
  • 更新时间 :
  • 英文 :


我有一个看起来像这样的数据帧:

id    total
1     50
1     0
1     0
2     100
2     0
2     0
3     75
3     0
3     0

但是我需要它按降序按总数排序,同时保持行按id分组。喜欢这个:

id    total
2     100
2     0
2     0
3     75
3     0
3     0
1     50
1     0
1     0

我已经尝试了一些使用分组的建议,如下所示:

grouped = df.groupby('id').apply(lambda g: g.sort_values('total', ascending=False))

看起来它正在做的是按升序对 id 进行分组和排序,然后对每个分组id中的总数进行子排序。但是我需要它对总数中的所有行进行排序,同时保持按id分组的行

任何建议将不胜感激。

您也可以通过cummax()方法、sort_values()方法和loc访问器来执行此操作:

df=df.loc[df.cummax().sort_values('id',ascending=False).index]

通过groupby()assign()方法:

df=df.assign(order=df.groupby('id').cummax()).sort_values('order',ascending=False).drop('order',1)

这是一种使用.groupby().list().explode()的方法

>>> df
id  total
0   1     50
1   1      0
2   1      0
3   2    100
4   2      0
5   2      0
6   3     75
7   3      0
8   3      0
>>> df.sort_values('total', ascending=False).groupby('id', sort=False).agg(list).explode('total').reset_index()
id total
0   2   100
1   2     0
2   2     0
3   3    75
4   3     0
5   3     0
6   1    50
7   1     0
8   1     0

这个想法首先按降序对total列进行排序。然后,按列对它进行分组id而不对分组索引进行排序。接下来,将list()函数作为参数传递给.agg()。最后,total列上使用.explode()

(1) 澄清要求

首先,让我们通过探索更复杂的数据样本的预期结果来重新审视/阐明您的要求:

id  total
0   1    100
1   1     70
2   1     68
3   1     65
4   2    100
5   2     80
6   2     50
7   3    100
8   3     75
9   3     70

根据您的要求要点:

我需要它对总数中的所有行进行排序,同时保持按id 分组的行

我会将其解释为需要逐行比较组中最大的元素与另一个组中的最大元素,如果存在平局(相同的值),我们继续比较每个组中的第二大元素,依此类推。这就像单词字典的词法顺序,但顺序相反。

对于这种解释,我希望排序的结果是:

id  total
0   2    100
1   2     80
2   2     50
3   3    100
4   3     75
5   3     70
6   1    100
7   1     70
8   1     68
9   1     65

在这里,尽管排序结果中的最后一个组(带id1)多了一个元素,并且该组的总和是所有组中最大的,但它仍然在最后一个排序,因为它与其他 2 个组的元素具有第一个最大的元素联系,而它的第二大元素是所有组中第二大元素中最少的。因此,在最后排序。

(2)接近解决方案

为了确保解决方案适用于按任意顺序显示的示例数据,让我们先对数据进行排序。 如果您的列的数据已经按降序排序total则可以自由跳过此步骤。

让我们使用您的示例数据(但按行顺序打乱):

id  total
0   3      0
1   3     75
2   2    100
3   2      0
4   1      0
5   1      0
6   1     50
7   2      0
8   3      0

然后,根据您的顺序对其进行排序:

df1 = df.sort_values(['id', 'total'], ascending=[True, False])
id  total
6   1     50
4   1      0
5   1      0
2   2    100
3   2      0
7   2      0
1   3     75
0   3      0
8   3      0

将解决方案应用于示例数据:

df_sorted = (df1.set_index('id')
.loc[
np.argsort(df1.groupby('id')['total'].agg(list))
.sort_values(ascending=False)
.index
]
).reset_index()
print(df_sorted)

id  total
0   2    100
1   2      0
2   2      0
3   3     75
4   3      0
5   3      0
6   1     50
7   1      0
8   1      0

这是您的预期结果。

将解决方案应用于更复杂的数据:

让我们把复杂的数据也打乱:

id  total
0   1     65
1   1     70
2   2    100
3   2     50
4   3    100
5   3     75
6   1     68
7   1    100
8   2     80
9   3     70

然后,根据您的顺序对其进行排序:

df1 = df.sort_values(['id', 'total'], ascending=[True, False])
id  total
7   1    100
1   1     70
6   1     68
0   1     65
2   2    100
8   2     80
3   2     50
4   3    100
5   3     75
9   3     70

然后,应用解决方案:

df_sorted = (df1.set_index('id')
.loc[
np.argsort(df1.groupby('id')['total'].agg(list))
.sort_values(ascending=False)
.index
]
).reset_index()
print(df_sorted)

id  total
0   2    100
1   2     80
2   2     50
3   3    100
4   3     75
5   3     70
6   1    100
7   1     70
8   1     68
9   1     65

在这里,我们得到了需求澄清中显示的预期结果。

(3) 方法说明

让我们详细剖析一下步骤:

(1)首先,我们对id执行.groupby(),并将列total聚合为列表:

df1.groupby('id')['total'].agg(list)
id
1    [100, 70, 68, 65]
2        [100, 80, 50]
3        [100, 75, 70]
Name: total, dtype: object

我们得到了每个组的列表,列表条目按降序排序。 此排序顺序归因于我们主要处理之前的排序步骤。

(2)然后,我们使用这个聚合序列上的np.argsort()来得到the indices that would sort an array

np.argsort(df1.groupby('id')['total'].agg(list))

id
1    0
2    2
3    1
Name: total, dtype: int64

np.argsort()的帮助下,我们获得了排序序列,可以在最后一步对列表进行排序。 由于我们希望按降对组进行排序,因此我们进一步按降序对结果进行排序,如下所示:

np.argsort(df1.groupby('id')['total'].agg(list)).sort_values(ascending=False)

id
2    2
3    1
1    0
Name: total, dtype: int64

现在,我们已经得出了具有id序列的组的正确排序2 3 1。 其余步骤是将此序列带回整个数据帧,并以正确的顺序显示组。

(3) 获取正确的索引序列以表示整个组序列:

我们按.index获取id组的索引,然后通过以下方式呈现给整个数据帧:

df1.set_index('id').loc[]           

由于我们从上一步获得的索引是id索引,因此我们对id进行.set_index()以匹配索引。 进一步.loc,我们得到:

total
id       
2     100
2      80
2      50
3     100
3      75
3      70
1     100
1      70
1      68
1      65

这里id是行索引。 为了将id从行索引还原到数据列,我们执行最后一步.reset_index()以获得最终结果:

id  total
0   2    100
1   2     80
2   2     50
3   3    100
4   3     75
5   3     70
6   1    100
7   1     70
8   1     68
9   1     65

您可以使用sort_values,但首先reset_index,然后set_index:

#simplier aggregation
total_by_id_df = df.groupby(["id"])['total'].sum()
total_by_id_df.reset_index().sort_values(['total']).set_index(['id','total']))

最新更新