Я хочу упорядочить этот DataFrame по заданному полю столбца и количеству записей, которые у меня есть для этого заданного поля.

Допустим, у меня есть очень простой фрейм данных, который выглядит примерно так:

      name  age
0     Paul   12
1     Ryan   17
2  Michael  100
3     Paul   36
4     Paul   66
5  Michael   45

Что я хочу в результате это что-то вроде

      name  age
0     Paul   12
1     Paul   36
2     Paul   66
3  Michael  100
4  Michael   45
5     Ryan   17

Таким образом, у меня есть 3 Пола, поэтому они подходят сначала, затем 2 Майкла и, наконец, только 1 Райан.

0
Pierre Avérous 7 Июл 2019 в 02:49

3 ответа

Лучший ответ

Один из вариантов: использовать value_counts, чтобы получить наиболее частые имена, затем установить, отсортировать и сбросить индекс:

x = list(df['name'].value_counts().index)
df.set_index('name').loc[x].reset_index()

Возвращает

      name  age
0     Paul   12
1     Paul   36
2     Paul   66
3  Michael  100
4  Michael   45
5     Ryan   17
4
Brendan 7 Июл 2019 в 00:01

Нужно создать вспомогательный столбец для сортировки, в этом случае size групп имен. Добавьте .reset_index(drop=True), если вы предпочитаете новый RangeIndex, или оставьте как есть, если полезен оригинальный индекс.

Сортировка не меняет порядок в пределах одинаковых значений, поэтому первая строка 'Paul' всегда будет появляться первой внутри 'Paul'

(df.assign(s = df.groupby('name').name.transform('size'))
   .sort_values('s', ascending=False)
   .drop(columns='s'))

Выход

      name  age
0     Paul   12
3     Paul   36
4     Paul   66
2  Michael  100
5  Michael   45
1     Ryan   17

Чтобы снять опасения, высказанные в комментариях, этот метод является эффективным. Гораздо больше, чем вышеупомянутый метод. Кроме того, вы не портите свой начальный индекс.

import numpy as np
np.random.seed(42)
N = 10**6
df = pd.DataFrame({'name': np.random.randint(1, 10000, N),
                   'age': np.random.normal(0, 1, N)})

%%timeit 
(df.assign(s = df.groupby('name').name.transform('size'))
   .sort_values('s', ascending=False)
   .drop(columns='s'))
#500 ms ± 31.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%%timeit 
x = list(df['name'].value_counts().index)
df.set_index('name').loc[x].reset_index()
#2.67 s ± 166 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
3
ALollz 7 Июл 2019 в 00:23

Единственное изменение, которое я добавил, - это возможность сортировки по количеству имен и по возрасту.

    df['name_count'] = df['name'].map(df['name'].value_counts())
    df = df.sort_values(by=['name_count', 'age'], 
                        ascending=[False,True]).drop('name_count', axis=1)
    df.reset_index(drop=True)


        name    age
      0 Paul    12
      1 Paul    36
      2 Paul    66
      3 Michael 45
      4 Michael 100
      5 Ryan    17
0
Gustavo Gradvohl 7 Июл 2019 в 01:18