Условие для двух и более последовательных строк pandas (не только сгруппированные вычисления)

У меня есть df с именем студента, его/ее счетом, названием класса и датой экзамена. Мне нужно добавить столбец, как показано на рисунке, который будет обозначать, улучшилась ли оценка ученика или нет (3-4 условные отметки, такие как «оценка увеличилась», «оценка уменьшилась», «равная» или «начальная оценка»). Я отсортировал df в соответствии с этим, теперь нужно сравнить некоторые условия в строке и в следующем, и если все верно, должно возвращаться отметка. Есть ли эффективный способ сделать это (моя фактическая таблица будет состоять из 1 млн строк, поэтому она не должна потреблять память)? Заранее спасибо?

df=pd.DataFrame({"score":[10,20,15,10,20,30],
                   "student":['John', 'Alex', "John", "John", "Alex", "John"],
                   "class":['english', 'math', "english",'math','math', 'english'],
                 "date":['01/01/2022','02/01/2022', '05/01/2022', '17/02/2022', '02/01/2022', '03/01/2022']})

df=df.sort_values(['student','class', 'date'])

Условие для двух и более последовательных строк pandas (не только сгруппированные вычисления)

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
0
20
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Получите изменение оценок, используя groupby и diff(), а затем присвойте значения, используя numpy.select:

import numpy as np

changes = df.groupby(["student","class"], sort=False)["score"].diff()
df["progress"] = np.select([changes.eq(0),changes.gt(0),changes.lt(0)],
                           ["equal score","score increased","score decreased"], 
                           "initial")

>>> df
   score student    class        date         progress
1     20    Alex     math  02/01/2022          initial
4     20    Alex     math  02/01/2022      equal score
0     10    John  english  01/01/2022          initial
5     30    John  english  03/01/2022  score increased
2     15    John  english  05/01/2022  score decreased
3     10    John     math  17/02/2022          initial

Обратите внимание, что студенты должны быть сгруппированы для этого подхода (AAABBB), если они смешанные (AABABB), это, вероятно, не удастся, поскольку numpy.select не обрабатывает выравнивание индекса;)

mozway 16.05.2022 23:03

OP уже показывает DataFrame, отсортированный с помощью sort_values

not_speshal 17.05.2022 00:01

Вы можете использовать groupby.diff для вычисления разницы, затем numpy.sign для получения знака и map для нужных вам текстов. Используйте fillna по умолчанию («начальный»):

df['progress'] = (np.sign(df.groupby(['student', 'class'])
                            ['score'].diff())
                    .map({0: 'equal', 1: 'increases', -1: 'decreases'})
                    .fillna('initial')
                  )

Выход:

   score student    class        date   progress
1     20    Alex     math  02/01/2022    initial
4     20    Alex     math  02/01/2022      equal
0     10    John  english  01/01/2022    initial
5     30    John  english  03/01/2022  increases
2     15    John  english  05/01/2022  decreases
3     10    John     math  17/02/2022    initial

Это прогрессивный подход, который я использовал

df['RN'] = df.sort_values(['date'], ascending=[True]).groupby(['student', 'class']).cumcount() + 1
#df.sort_values(['student', 'RN']) #To visually see progress of student before changes
df['Progress'] = df['RN'].apply(lambda x : str(x).replace('1', 'initial'))
df = df.sort_values(['student', 'RN'])
df['score_shift'] = df['score'].shift()
df['score_shift'].fillna(0, inplace = True)
df['score_shift'] = df['score_shift'].astype(int)
condlist = [df['Progress'] == 'initial', df['score_shift'] == df['score'], df['score_shift'] > df['score'], df['score_shift'] < df['score']]
choicelist = ['initial', 'equal', 'decrease', 'increase']
df['Progress'] = np.select(condlist, choicelist)
df

Другие вопросы по теме