/
Nik_voz
/
rgpu_python_analysis
Обзор
Документация
Войти
/
Nik_voz
/
rgpu_python_analysis
Код
Запросы
1
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
python_analysis/second_semestr/pract4-1.py
270 строк
8 KB
Nick-voz
another commit.
23 дек 2025, 17:37
23 дек 2025, 17:37
e0763c4
Код
Авторство
О чём код?
import os import matplotlib.pyplot as plt import numpy as np import pandas as pd import seaborn as sns from constants import Columns from dotenv.main import load_dotenv load_dotenv() if (DATASET_PATH := os.environ.get("DATASET_PATH")) is None: raise RuntimeWarning("DATASET_PATH is not set in environment") data = pd.read_csv(DATASET_PATH, sep=";") BASE_FONT = 6 HEADER_FONT = 8 TITLE_FONT = 12 g_by_gender = ( data.groupby(Columns.GENDER)[Columns.COUNT_GRADUATE] .sum() .sort_values(ascending=False) ) g_by_region = ( data.groupby(Columns.OBJECT_NAME)[Columns.COUNT_GRADUATE] .sum() .sort_values(ascending=False) ) g_by_edu = ( data.groupby(Columns.EDUCATION_LEVEL)[Columns.COUNT_GRADUATE] .sum() .sort_values(ascending=False) ) g_by_year = ( data.groupby(Columns.YEAR)[Columns.COUNT_GRADUATE].sum().sort_index() ) combine_share = ( data[Columns.COUNT_GRADUATE] * data[Columns.PERCENT_COMBINE_WORK_AND_STUDY].fillna(0) / 100.0 ) total_students = data[Columns.COUNT_GRADUATE].sum() n_combined = combine_share.sum() n_not_combined = max(total_students - n_combined, 0) g_by_combine = pd.Series( {"Совмещали": n_combined, "Не совмещали": n_not_combined} ).sort_values(ascending=False) def to_topN_with_other( s: pd.Series, top_n: int = 8, other_name: str = "Прочие" ): if len(s) <= top_n: return s top = s.iloc[:top_n] other = s.iloc[top_n:].sum() return pd.concat([top, pd.Series({other_name: other})]) plt.figure(figsize=(25, 15)) plt.subplot(2, 3, 1) plt.title("Распределение по полу", fontsize=HEADER_FONT) g_by_gender.plot.pie( autopct="%.2f%%", fontsize=BASE_FONT, startangle=90, counterclock=False ) plt.ylabel("") plt.subplot(2, 3, 2) plt.title( "Распределение выпускников по регионам (топ-8 + прочие)", fontsize=HEADER_FONT, ) to_topN_with_other(g_by_region, top_n=8).plot.pie( autopct="%.2f%%", fontsize=BASE_FONT, startangle=90, counterclock=False ) plt.ylabel("") plt.subplot(2, 3, 3) plt.title("Распределение по уровню образования", fontsize=HEADER_FONT) g_by_edu.plot.pie( autopct="%.2f%%", fontsize=BASE_FONT, startangle=90, counterclock=False ) plt.ylabel("") plt.subplot(2, 3, 4) plt.title("Распределение по годам выпуска", fontsize=HEADER_FONT) to_topN_with_other(g_by_year, top_n=8).plot.pie( autopct="%.2f%%", fontsize=BASE_FONT, startangle=90, counterclock=False ) plt.ylabel("") plt.subplot(2, 3, 5) plt.title("Доля совмещавших работу и учебу", fontsize=HEADER_FONT) g_by_combine.plot.pie( autopct="%.2f%%", fontsize=BASE_FONT, startangle=90, counterclock=False ) plt.ylabel("") plt.tight_layout() plt.show() men = int(g_by_gender.get("Мужской", 0)) women = int(g_by_gender.get("Женский", 0)) total_gender = men + women pct_men = 100 * men / total_gender if total_gender else 0 pct_women = 100 * women / total_gender if total_gender else 0 top_region, top_region_n = ( (g_by_region.index[0], int(g_by_region.iloc[0])) if len(g_by_region) else ("—", 0) ) low_region, low_region_n = ( (g_by_region.index[-1], int(g_by_region.iloc[-1])) if len(g_by_region) else ("—", 0) ) top_edu, top_edu_n = ( (g_by_edu.index[0], int(g_by_edu.iloc[0])) if len(g_by_edu) else ("—", 0) ) pct_combined = 100 * n_combined / total_students if total_students else 0 pct_not_combined = 100 - pct_combined if total_students else 0 print( f"1) В датасете женщин: {women} ({pct_women:.1f}%), мужчин: {men} ({pct_men:.1f}%)." ) print( f"2) Больше всего выпускников из региона: {top_region} ({top_region_n}), меньше всего — {low_region} ({low_region_n})." ) print( f"3) Большинство — уровень образования: {top_edu} ({top_edu_n} выпускников)." ) print( f"4) Совмещали работу и учебу (оценка): {int(n_combined):,} из {int(total_students):,} (~{pct_combined:.1f}%).".replace( ",", " " ) ) print( f"5) Не совмещали — ~{pct_not_combined:.1f}%; {'вдвое больше' if pct_not_combined >= 2*pct_combined else 'не вдвое больше'} тех, кто совмещал." ) plt.rcParams.update({"figure.max_open_warning": 0}) salary_cols = [ Columns.PERCENT_EMPLOYED, Columns.AVERAGE_SALARY_FACT_AVG, Columns.AVERAGE_SALARY_FACT_MED, Columns.AVERAGE_SALARY_NORM_AVG, Columns.AVERAGE_SALARY_NORM_MED, ] top_regions = g_by_region.iloc[:12].index df_plot = data[data[Columns.OBJECT_NAME].isin(top_regions)].copy() def weighted_agg(df: pd.DataFrame, value_col: str): w = df[Columns.COUNT_GRADUATE].clip(lower=0).astype(float) v = pd.to_numeric(df[value_col], errors="coerce").astype(float) num = ( (v * w) .groupby([df[Columns.OBJECT_NAME], df[Columns.EDUCATION_LEVEL]]) .sum() ) den = w.groupby( [df[Columns.OBJECT_NAME], df[Columns.EDUCATION_LEVEL]] ).sum() res = (num / den).reset_index(name="value") res["metric"] = value_col return res agg_frames = [weighted_agg(df_plot, c) for c in salary_cols] agg = pd.concat(agg_frames, ignore_index=True) titles = { Columns.PERCENT_EMPLOYED: "Доля трудоустроенных, %", Columns.AVERAGE_SALARY_FACT_AVG: "Средняя фактическая зарплата (ср.)", Columns.AVERAGE_SALARY_FACT_MED: "Медианная фактическая зарплата", Columns.AVERAGE_SALARY_NORM_AVG: "Средняя нормированная зарплата (ср.)", Columns.AVERAGE_SALARY_NORM_MED: "Медианная нормированная зарплата", } for col in salary_cols: sub = agg[agg["metric"] == col].copy() if sub.empty: print(f"Нет данных для метрики: {col}") continue plt.figure(figsize=(12, 6)) sns.barplot( x=Columns.OBJECT_NAME, y="value", hue=Columns.EDUCATION_LEVEL, data=sub, edgecolor="none", ) plt.title(titles.get(col, col), fontsize=TITLE_FONT) plt.xticks(rotation=90) plt.xlabel("") plt.ylabel("") plt.legend(title="", bbox_to_anchor=(1.02, 1), loc="upper left") plt.tight_layout() plt.show() def top3_by_metric(col): sub = agg[agg["metric"] == col] t = ( sub.groupby(Columns.OBJECT_NAME)["value"] .mean() .sort_values(ascending=False) .head(3) ) return ", ".join([f"{idx} ({val:.1f})" for idx, val in t.items()]) print("Наблюдения по barplot:") print( f"- Регионы-лидеры по доле трудоустроенных: {top3_by_metric(Columns.PERCENT_EMPLOYED)}" ) print( f"- Регионы-лидеры по средней факт. зарплате: {top3_by_metric(Columns.AVERAGE_SALARY_FACT_AVG)}" ) print( "- В разрезе уровней образования заметны различия: бак/спец, магистратура и СПО часто различаются на десятки процентов/тыс. рублей (см. столбцы)." ) total_metric = ( pd.to_numeric( data[Columns.AVERAGE_SALARY_FACT_AVG], errors="coerce" ).fillna(0) + pd.to_numeric( data[Columns.AVERAGE_SALARY_FACT_MED], errors="coerce" ).fillna(0) + pd.to_numeric( data[Columns.AVERAGE_SALARY_NORM_AVG], errors="coerce" ).fillna(0) ) threshold = 230000.0 order = np.argsort(total_metric.values) plt.figure(figsize=(18, 6)) plt.title("Суммарный зарплатный показатель (3 метрики) и порог", fontsize=16) plt.plot( range(len(order)), total_metric.values[order], color="tab:blue", linewidth=1.5, label="Сумма 3 метрик", ) plt.axhline( threshold, color="tab:red", linestyle="--", linewidth=2, label=f"Порог = {int(threshold)}", ) plt.xlabel("Наблюдения (от меньших к большим)") plt.ylabel("Сумма, руб.") plt.legend(loc="best") plt.tight_layout() plt.show() share_pass = (total_metric >= threshold).mean() * 100 print( f"Долю наблюдений выше порога можно считать 'прошедшими': {share_pass:.1f}% (порог {int(threshold)})." )