Как отсортировать значение по группам?

У меня есть таблица следующего формата:
Кусок таблицы
64834d8f3fa6b324324440.png

Это фрагмент учебного плана одной специальности. В нем в 1 и 2 семестре есть дисциплины которые повторяются в данном пример это (ОГСЭ. 02, 04, 05). Мне необходимо отсортировать значения таким образом что бы одинаковые значения (ОГСЭ. 02, 04, 05). 1 и 2 семестров были рядом. Но при этом не менялся порядок остальных дисциплин.
Можно сказать что каждая дисциплина имеет принадлежность к определенной группе:
  1. ОГСЭ = Общий гуманитарный и социально-экономический учебный цикл
  2. ЕН = Математический и общий естественнонаучный учебный цикл
  3. ОПЦ = Общепрофессиональный цикл
  4. МДК, ПМ, ПП, УП = Профессиональный цикл

И порядок следования этих групп мне нужно сохранять.
Как хочу
64835907423cb269701570.png


Я пытался встроенными фильтрами в Excel добиться нужного мне результата, отсортировал по шифру и казалось бы получил нужный результат, но у меня поменялся порядок дисциплин.
Сортировка Excel
648352cc25127677814275.png


Изначально все эти данные у меня имеются в pd.DataFrame, что я пытался сделать:
Попытка 1
import pandas as pd

# Создаем DataFrame с данными
data = {'Шифр дисциплины': ['ОГСЭ.02', 'ОГСЭ.03', 'ОГСЭ.04', 'ОГСЭ.05', 'ЕН.01', 'ОПЦ.01', 'ОПЦ.02', 'ОПЦ.03', 'ОПЦ.08', 'ОГСЭ.01', 'ОГСЭ.02', 'ОГСЭ.04', 'ОГСЭ.05', 'ЕН.02', 'ОПЦ.04', 'ОПЦ.11', 'ОПЦ.13', 'МДК.01.01', 'ПМ.02.01(К)', 'МДК.02.01', 'МДК.02.02', 'МДК.02.03', 'УП.02.01', 'ПП.02.01'],
        'Курс': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
        'Семестр': [1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]}
df = pd.DataFrame(data)

# сортируем DataFrame по столбцам "Шифр дисциплины", "Курс", "Семестр"
df_sorted = df.sort_values(by=['Шифр дисциплины', 'Курс', 'Семестр'], kind='mergesort')

print(df_sorted)


Результат 1
Шифр дисциплины  Курс  Семестр
4            ЕН.01     1        1
13           ЕН.02     1        2
17       МДК.01.01     1        2
19       МДК.02.01     1        2
20       МДК.02.02     1        2
21       МДК.02.03     1        2
9          ОГСЭ.01     1        2
0          ОГСЭ.02     1        1
10         ОГСЭ.02     1        2
1          ОГСЭ.03     1        1
2          ОГСЭ.04     1        1
11         ОГСЭ.04     1        2
3          ОГСЭ.05     1        1
12         ОГСЭ.05     1        2
5           ОПЦ.01     1        1
6           ОПЦ.02     1        1
7           ОПЦ.03     1        1
14          ОПЦ.04     1        2
8           ОПЦ.08     1        1
15          ОПЦ.11     1        2
16          ОПЦ.13     1        2
18     ПМ.02.01(К)     1        2
23        ПП.02.01     1        2
22        УП.02.01     1        2

Попытка 2
import pandas as pd

data = {'Шифр дисциплины': ['ОГСЭ.02', 'ОГСЭ.03', 'ОГСЭ.04', 'ОГСЭ.05', 'ЕН.01', 'ОПЦ.01', 'ОПЦ.02', 'ОПЦ.03', 'ОПЦ.08', 'ОГСЭ.01', 'ОГСЭ.02', 'ОГСЭ.04', 'ОГСЭ.05', 'ЕН.02', 'ОПЦ.04', 'ОПЦ.11', 'ОПЦ.13', 'МДК.01.01', 'ПМ.02.01(К)', 'МДК.02.01', 'МДК.02.02', 'МДК.02.03', 'УП.02.01', 'ПП.02.01'],
        'Курс': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
        'Семестр': [1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]}
df = pd.DataFrame(data)

# Получаем список и порядок уникальных значений шифров
order = list(df['Шифр дисциплины'].str.split('.').str.get(0).unique())
categories = pd.CategoricalDtype(categories=order, ordered=True)
df['temp'] = df['Шифр дисциплины'].str.extract('(\w+)', expand=False).astype(categories)
df = df.sort_values(by='temp')
df = df.drop(columns='temp')
print(df)

Результат 2
Шифр дисциплины  Курс  Семестр
0          ОГСЭ.02     1        1
12         ОГСЭ.05     1        2
10         ОГСЭ.02     1        2
9          ОГСЭ.01     1        2
11         ОГСЭ.04     1        2
3          ОГСЭ.05     1        1
2          ОГСЭ.04     1        1
1          ОГСЭ.03     1        1
4            ЕН.01     1        1
13           ЕН.02     1        2
5           ОПЦ.01     1        1
16          ОПЦ.13     1        2
15          ОПЦ.11     1        2
14          ОПЦ.04     1        2
8           ОПЦ.08     1        1
7           ОПЦ.03     1        1
6           ОПЦ.02     1        1
17       МДК.01.01     1        2
19       МДК.02.01     1        2
20       МДК.02.02     1        2
21       МДК.02.03     1        2
18     ПМ.02.01(К)     1        2
22        УП.02.01     1        2
23        ПП.02.01     1        2

И второй вариант это почти то что нужно, но одинаковые дисциплины не идут друг за другом, что я еще могу сделать?
  • Вопрос задан
  • 61 просмотр
Решения вопроса 1
Maksim_64
@Maksim_64
Data Analyst
Ну смотри скажу прямо задачка для общего кейса безнадежная надо менять вводные, потому что с одной стороны мы можем создать категории например ОГСЭ но затем у них еще и индексы 01, 02 и т.д., для того куска что ты дал я сделал, ну как общее решение это надо на уровне дизайна проблемы решать.
cat = pd.Categorical(df['Шифр дисциплины'].str.split('.').str[0], 
categories=['ОГСЭ','ЕН','ОПЦ','МДК','УП','ПМ','ПП'])
print(df
 .groupby(cat)
 .apply(lambda x: x.sort_values('Шифр дисциплины', key = lambda x: x.str.split('.').str[1]))
 .reset_index(drop=True)
)

По решению, трюк следующий, создаем категории, они имеют порядок индекс. Когда, мы группируем groupby он СОРТИРУЕТ по умолчанию, и затем мы сортируем снова. То есть хитрость в том что бы впихнуть двойную сортировку, сначала по категориям а потом по цифрам внутри каждой категории.

Кстати имей ввиду во второй своей попытке ты был на правильном направлении и задачка вполне себе типичная, только вместо твоего кода надо писать вот так
cat = pd.Categorical(df['Шифр дисциплины'].str.split('.').str[0], 
categories=['ОГСЭ','ЕН','ОПЦ','МДК','УП','ПМ','ПП'])
df.sort_values(by=['Шифр дисциплины'],key= lambda x: cat)
Результат будет, как у тебя только кода меньше и без всяких созданий временных колонок.

Но повторюсь, для общего кейса на уровне дизайна надо работать, такие сложные парсинги внутри колонок это плохо. Pandas он конечно может все (что помещается в память) но лучше для полезных задач его использовать.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы