我有一个看起来像这样的数据帧:
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
在这里,尽管排序结果中的最后一个组(带id
1
)多了一个元素,并且该组的总和是所有组中最大的,但它仍然在最后一个排序,因为它与其他 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']))