У меня есть два уникальных имени столбца ширины фреймов данных pandas, за исключением:
Я хотел бы объединить эти три столбца (год, Student_id, Student_name), однако иногда имя студента написано с ошибкой (или имеет другое написание). По сути, я хотел бы объединить год и столбец, сохранив при этом столбец Student_name.
Что касается изменения имя_студента, мне безразлично, какое имя_студента выбрано (хотя было бы неплохо, если бы оно выбирало более часто встречающееся имя_студента, но я не хочу требовать слишком многого). Я бы предпочел, чтобы объединенный/окончательный фрейм данных использовал только ОДНУ версию Student_name (для каждого Student_id), но я готов согласиться в противном случае :)
Пример:
>>> df_A
year student_id student_name exam_A
0 2023 12345 Chris P. Bacon 80
1 2024 12345 Chris Bacon 90
2 2024 33333 Noah Buddy 90
3 2021 55555 Faye Kipperson 99
4 2024 11111 Beau Gusman 75
>>> df_B
year student_id student_name exam_B exam_C
0 2024 12345 Chris P. Bacon 90 75
1 2024 33333 Noah Buddy 88 77
2 2020 88888 Saul Goodman 86 88
3 2023 88888 Saul Goodman 99 79
4 2024 55555 Fay Kipperson 82 75
5 2024 11111 Beau Gusman 80 99
Я хочу вот этого =>
year student_id student_name exam_A exam_B exam_C
0 2020 88888 Saul Goodman NaN 86.0 88.0
1 2021 55555 Faye Kipperson 99.0 NaN NaN
2 2023 12345 Chris P. Bacon 80.0 NaN NaN
3 2023 88888 Saul Goodman NaN 99.0 79.0
4 2024 11111 Beau Gusman 75.0 80.0 99.0
5 2024 12345 Chris Bacon 90.0 90.0 75.0
6 2024 33333 Noah Buddy 90.0 88.0 77.0
7 2024 55555 Faye Kipperson NaN 82.0 75.0
Я НЕ ХОЧУ несколько столбцов Student_name. В настоящее время я объединяю один фрейм данных за раз, а затем возвращаюсь и заполняю нулевые ячейки Student_name.
Вопрос № 2. На самом деле мне нужно объединить около 33 фреймов данных (все с уникальными именами столбцов, кроме года, Student_id, Student_name), есть ли способ объединить их таким же образом одновременно (в отличие от объединения каждого в индивидуально)?
Большое спасибо!!!
Я пытался:
>>> pd.merge(df_A, df_B, on=[ 'year', 'student_id', ], how='outer')
year student_id student_name_x exam_A student_name_y exam_B exam_C
0 2020 88888 NaN NaN Saul Goodman 86.0 88.0
1 2021 55555 Faye Kipperson 99.0 NaN NaN NaN
2 2023 12345 Chris P. Bacon 80.0 NaN NaN NaN
3 2023 88888 NaN NaN Saul Goodman 99.0 79.0
4 2024 11111 Beau Gusman 75.0 Beau Gusman 80.0 99.0
5 2024 12345 Chris Bacon 90.0 Chris P. Bacon 90.0 75.0
6 2024 33333 Noah Buddy 90.0 Noah Buddy 88.0 77.0
7 2024 55555 NaN NaN Fay Kipperson 82.0 75.0
>>> pd.merge(df_A, df_B.drop(columns=[ 'student_name' ]), on=[ 'year', 'student_id', ], how='outer')
year student_id student_name exam_A exam_B exam_C
0 2020 88888 NaN NaN 86.0 88.0
1 2021 55555 Faye Kipperson 99.0 NaN NaN
2 2023 12345 Chris P. Bacon 80.0 NaN NaN
3 2023 88888 NaN NaN 99.0 79.0
4 2024 11111 Beau Gusman 75.0 80.0 99.0
5 2024 12345 Chris Bacon 90.0 90.0 75.0
6 2024 33333 Noah Buddy 90.0 88.0 77.0
7 2024 55555 NaN NaN 82.0 75.0
'student_id'
и объедините с mode
.'student_name'
.В качестве примера, включая также df_C
:
year student_id student_name exam_X exam_Z
0 2024 12345 Chris P. Bacon 90 75
1 2024 33333 Noah Buddy 88 77
2 2020 88888 Saul Goodman 86 88
3 2023 88888 Saul Goodman 99 79
4 2024 55555 Fay Kipperson 82 75
5 2024 11111 Beau Gusman 80 99
Вы можете использовать что-то вроде этого:
def merge_grades(dfs: list[pd.DataFrame]):
df_students = (
pd.concat([df[["student_id", "student_name"]] for df in dfs])
.groupby("student_id", as_index=False)
.agg(lambda x: x.mode()[0])
)
df_left = dfs[0].drop(columns = "student_name")
for df_right in dfs[1:]:
df_left = pd.merge(
df_left,
df_right.drop(columns = "student_name"),
how = "outer",
)
return df_left.merge(df_students)
df = merge_grades([df_A, df_B, df_C])
year student_id exam_A exam_B exam_C exam_X exam_Z student_name
0 2020 88888 NaN 86.0 88.0 86.0 88.0 Saul Goodman
1 2021 55555 99.0 NaN NaN NaN NaN Fay Kipperson
2 2023 12345 80.0 NaN NaN NaN NaN Chris P. Bacon
3 2023 88888 NaN 99.0 79.0 99.0 79.0 Saul Goodman
4 2024 11111 75.0 80.0 99.0 80.0 99.0 Beau Gusman
5 2024 12345 90.0 90.0 75.0 90.0 75.0 Chris P. Bacon
6 2024 33333 90.0 88.0 77.0 88.0 77.0 Noah Buddy
7 2024 55555 NaN 82.0 75.0 82.0 75.0 Fay Kipperson
Вы можете использовать merge()
, filter()
, Counter()
и most_common()
:
import pandas as pd
from collections import Counter
def _merge(frames):
res = frames[0]
for df in frames[1:]:
res = pd.merge(res, df, on=['year', 'student_id'], how='outer')
students = res.filter(like='student_name')
res['student_name'] = students.apply(
lambda row: Counter(row.dropna().values).most_common(1)[0][0] if len(row.dropna()) > 0 else None,
axis=1
)
res = res.drop(columns=[col for col in res.columns if 'student_name_' in col])
return res
df_A = pd.DataFrame({
'year': [2023, 2024, 2024, 2021, 2024],
'student_id': [12345, 12345, 33333, 55555, 11111],
'student_name': ['Chris P. Bacon', 'Chris Bacon', 'Noah Buddy', 'Faye Kipperson', 'Beau Gusman'],
'exam_A': [80, 90, 90, 99, 75]
})
df_B = pd.DataFrame({
'year': [2024, 2024, 2020, 2023, 2024, 2024],
'student_id': [12345, 33333, 88888, 88888, 55555, 11111],
'student_name': ['Chris P. Bacon', 'Noah Buddy', 'Saul Goodman', 'Saul Goodman', 'Fay Kipperson', 'Beau Gusman'],
'exam_B': [90, 88, 86, 99, 82, 80],
'exam_C': [75, 77, 88, 79, 75, 99]
})
print(_merge([df_A, df_B]))
year student_id student_name exam_A exam_B exam_C
0 2020 88888 Saul Goodman NaN 86.0 88.0
1 2021 55555 Faye Kipperson 99.0 NaN NaN
2 2023 12345 Chris P. Bacon 80.0 NaN NaN
3 2023 88888 Saul Goodman NaN 99.0 79.0
4 2024 11111 Beau Gusman 75.0 80.0 99.0
5 2024 12345 Chris Bacon 90.0 90.0 75.0
6 2024 33333 Noah Buddy 90.0 88.0 77.0
7 2024 55555 Fay Kipperson NaN 82.0 75.0