{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":115104,"databundleVersionId":13748315,"sourceType":"competition"}],"dockerImageVersionId":31153,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# ML библиотеки\nfrom sklearn.ensemble import RandomForestRegressor\nfrom sklearn.metrics import mean_squared_error, ndcg_score\nimport lightgbm as lgb\n\n# Визуализация\nimport matplotlib.pyplot as plt\n\n# Утилиты\nimport os\nimport glob\n\n# =============================================================================\n# КОНСТАНТЫ И НАСТРОЙКИ\n# =============================================================================\n\nRANDOM_STATE = 42\nnp.random.seed(RANDOM_STATE)\n\n# =============================================================================\n# ФУНКЦИИ МЕТРИК\n# =============================================================================\n\ndef compute_ndcg_per_session(relevance_scores, predicted_scores, k=20):\n    \"\"\"Вычисляет NDCG@K для одной сессии\"\"\"\n    if len(relevance_scores) == 0 or len(predicted_scores) == 0:\n        return 0.0\n    \n    min_len = min(len(relevance_scores), len(predicted_scores))\n    relevance_scores = np.array(relevance_scores[:min_len])\n    predicted_scores = np.array(predicted_scores[:min_len])\n    \n    y_true = relevance_scores.reshape(1, -1)\n    y_score = predicted_scores.reshape(1, -1)\n    \n    return ndcg_score(y_true, y_score, k=k)\n\n# =============================================================================\n# ПРЕДОБРАБОТКА ДАННЫХ\n# =============================================================================\n\ndef create_session_id(df):\n    \"\"\"Создает session_id из user_number и search_number\"\"\"\n    df = df.copy()\n    df['session_id'] = df['user_number'].astype(str) + '_' + df['search_number'].astype(str)\n    return df\n\ndef preprocess_data(df):\n    \"\"\"Предобработка данных\"\"\"\n    df = df.copy()\n    \n    # Создаем session_id\n    df = create_session_id(df)\n    \n    # Обработка NaN и Inf значений\n    if 'relevance' in df.columns:\n        df['relevance'] = df['relevance'].fillna(0)\n        df['relevance'] = df['relevance'].replace([np.inf, -np.inf], 0)\n    \n    return df\n\n# =============================================================================\n# ГЕНЕРАЦИЯ ФИЧЕЙ (УПРОЩЕННАЯ)\n# =============================================================================\n\ndef generate_features(df):\n    \"\"\"Генерирует основные фичи\"\"\"\n    df = df.copy()\n    \n    # Основные фичи\n    df['cnt_products_in_session'] = df.groupby('session_id')['product_id'].transform('count')\n    df['position_in_session'] = df.groupby('session_id')['product_position'].rank(method='dense')\n    \n    # Популярность товара\n    product_views = df.groupby('product_id').size()\n    df['product_popularity'] = df['product_id'].map(product_views)\n    df['log_product_popularity'] = np.log1p(df['product_popularity'])\n    \n    # Фичи цены\n    if 'price' in df.columns:\n        df['log_price'] = np.log1p(df['price'])\n        df['price_rank_in_session'] = df.groupby('session_id')['price'].rank(method='dense')\n    \n    # Фичи скидки\n    if 'price_discount' in df.columns:\n        df['has_discount'] = (df['price_discount'] > 0).astype(int)\n    \n    # Фичи устройства\n    if 'device' in df.columns:\n        df['is_mobile'] = (df['device'] == 'mobile').astype(int)\n    \n    # Временные фичи\n    if 'date' in df.columns:\n        df['date'] = pd.to_datetime(df['date'])\n        df['day_of_week'] = df['date'].dt.dayofweek\n        df['is_weekend'] = (df['day_of_week'] >= 5).astype(int)\n    \n    return df\n\n# =============================================================================\n# ЗАГРУЗКА ДАННЫХ\n# =============================================================================\n\ndef load_data():\n    \"\"\"Загружает данные соревнования\"\"\"\n    csv_files = glob.glob('/kaggle/input/**/*.csv', recursive=True)\n    \n    train_file = None\n    test_file = None\n    \n    for file in csv_files:\n        filename = file.lower().split('/')[-1]\n        if 'train' in filename:\n            train_file = file\n        elif 'test' in filename:\n            test_file = file\n    \n    if train_file and test_file:\n        train_df = pd.read_csv(train_file)\n        test_df = pd.read_csv(test_file)\n        print(f\"✅ Данные загружены: Train {train_df.shape}, Test {test_df.shape}\")\n        return train_df, test_df\n    else:\n        print(\"❌ Файлы не найдены\")\n        return None, None\n\n# =============================================================================\n# ОСНОВНОЙ ПАЙПЛАЙН\n# =============================================================================\n\ndef main():\n    \"\"\"Основная функция - БЫСТРАЯ ВЕРСИЯ\"\"\"\n    \n    print(\"=\"*60)\n    print(\"РЕШЕНИЕ ЗАДАЧИ РАНЖИРОВАНИЯ 21VEK.BY (БЫСТРАЯ ВЕРСИЯ)\")\n    print(\"=\"*60)\n    \n    # 1. ЗАГРУЗКА ДАННЫХ\n    print(\"\\n1. ЗАГРУЗКА ДАННЫХ\")\n    print(\"-\" * 30)\n    \n    train_df, test_df = load_data()\n    if train_df is None:\n        return\n    \n    # Берем только часть данных для скорости\n    print(\"Используем только 10% данных для скорости...\")\n    train_df = train_df.sample(frac=0.1, random_state=RANDOM_STATE)\n    print(f\"Train данные (10%): {train_df.shape}\")\n    \n    # 2. ПРЕДОБРАБОТКА\n    print(\"\\n2. ПРЕДОБРАБОТКА ДАННЫХ\")\n    print(\"-\" * 30)\n    \n    train_df = preprocess_data(train_df)\n    test_df = preprocess_data(test_df)\n    \n    # 3. ГЕНЕРАЦИЯ ФИЧЕЙ\n    print(\"\\n3. ГЕНЕРАЦИЯ ФИЧЕЙ\")\n    print(\"-\" * 30)\n    \n    train_df = generate_features(train_df)\n    test_df = generate_features(test_df)\n    \n    # Получаем фичи\n    feature_columns = [col for col in train_df.columns \n                      if col not in ['session_id', 'product_id', 'relevance', 'date', 'query', \n                                   'user_number', 'search_number', 'device']]\n    \n    print(f\"Количество фичей: {len(feature_columns)}\")\n    \n    # 4. ОБУЧЕНИЕ МОДЕЛИ\n    print(\"\\n4. ОБУЧЕНИЕ МОДЕЛИ\")\n    print(\"-\" * 30)\n    \n    # Простая модель для скорости\n    model = lgb.LGBMRegressor(\n        n_estimators=100,  # Меньше итераций\n        learning_rate=0.1,\n        max_depth=4,       # Меньше глубина\n        random_state=RANDOM_STATE,\n        verbose=-1\n    )\n    \n    X_train = train_df[feature_columns].values\n    y_train = train_df['relevance'].values\n    \n    print(\"Обучаем модель...\")\n    model.fit(X_train, y_train)\n    print(\"✅ Модель обучена!\")\n    \n    # 5. ПРЕДСКАЗАНИЯ\n    print(\"\\n5. ПРЕДСКАЗАНИЯ\")\n    print(\"-\" * 30)\n    \n    X_test = test_df[feature_columns].values\n    test_predictions = model.predict(X_test)\n    \n    print(f\"Сгенерировано {len(test_predictions)} предсказаний\")\n    \n    # 6. СОЗДАНИЕ SUBMISSION\n    print(\"\\n6. СОЗДАНИЕ SUBMISSION\")\n    print(\"-\" * 30)\n    \n    # Создаем submission\n    submission_df = test_df[['session_id', 'product_id']].copy()\n    submission_df['session_product_id'] = (\n        submission_df['session_id'].astype(str) + '_' + \n        submission_df['product_id'].astype(str)\n    )\n    submission_df['relevance'] = test_predictions\n    \n    # Нормализуем предсказания внутри сессий\n    submission_df['relevance'] = submission_df.groupby('session_id')['relevance'].transform(\n        lambda x: (x - x.min()) / (x.max() - x.min() + 1e-8)\n    )\n    \n    # Финальный submission\n    final_submission = submission_df[['session_product_id', 'relevance']].copy()\n    final_submission = final_submission.sort_values('session_product_id')\n    \n    # Сохраняем\n    final_submission.to_csv('submission.csv', index=False)\n    \n    print(f\"✅ Submission сохранен: submission.csv\")\n    print(f\"Размер: {final_submission.shape}\")\n    print(f\"Пример:\")\n    print(final_submission.head(10))\n    \n    # 7. АНАЛИЗ РЕЗУЛЬТАТОВ\n    print(\"\\n7. АНАЛИЗ РЕЗУЛЬТАТОВ\")\n    print(\"-\" * 30)\n    \n    print(f\"Статистики предсказаний:\")\n    print(f\"  Среднее: {final_submission['relevance'].mean():.4f}\")\n    print(f\"  Медиана: {final_submission['relevance'].median():.4f}\")\n    print(f\"  Минимум: {final_submission['relevance'].min():.4f}\")\n    print(f\"  Максимум: {final_submission['relevance'].max():.4f}\")\n    \n    # Важность фичей\n    if hasattr(model, 'feature_importances_'):\n        feature_importance = pd.DataFrame({\n            'feature': feature_columns,\n            'importance': model.feature_importances_\n        }).sort_values('importance', ascending=False)\n        \n        print(f\"\\nТоп-10 важных фичей:\")\n        print(feature_importance.head(10))\n    \n    print(\"\\n\" + \"=\"*60)\n    print(\"ЗАДАЧА ВЫПОЛНЕНА УСПЕШНО! (БЫСТРАЯ ВЕРСИЯ)\")\n    print(\"=\"*60)\n    print(\"Файл submission.csv готов для отправки!\")\n\n# =============================================================================\n# ЗАПУСК\n# =============================================================================\n\nif __name__ == \"__main__\":\n    main()\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-10-25T15:33:56.101395Z","iopub.execute_input":"2025-10-25T15:33:56.102104Z","iopub.status.idle":"2025-10-25T15:34:38.830809Z","shell.execute_reply.started":"2025-10-25T15:33:56.102075Z","shell.execute_reply":"2025-10-25T15:34:38.829829Z"}},"outputs":[],"execution_count":null}]}