{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":96164,"databundleVersionId":12993472,"sourceType":"competition"}],"dockerImageVersionId":31040,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"Thanks to share features\nhttps://www.kaggle.com/code/seowoohyeon/reboot-drw-verison-2","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport polars as pl\nfrom datetime import datetime\nimport os\nfrom sklearn.model_selection import KFold\nfrom xgboost import XGBRegressor\nfrom sklearn.metrics import mean_absolute_error\nimport gc\nfrom tqdm import tqdm\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nfrom matplotlib import cycler\nfrom collections import Counter\nfrom matplotlib.colors import LinearSegmentedColormap\ncolors = [\"#068D9D\", \"#53599A\", \"#607BB0\", \"#6D9DC5\", \"#77BECF\", \"#80DED9\", \"#AEECEF\"]\nplt.rc('axes', facecolor='#E6E6E6', edgecolor='none', axisbelow=True, grid=True, prop_cycle=cycler('color', colors))\nSEED=42","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-11T18:07:27.727931Z","iopub.execute_input":"2025-07-11T18:07:27.728253Z","iopub.status.idle":"2025-07-11T18:07:32.566802Z","shell.execute_reply.started":"2025-07-11T18:07:27.728220Z","shell.execute_reply":"2025-07-11T18:07:32.565783Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def reduce_mem_usage(dataframe,dataset):\n    print(\"Reducing memory usage fo:\",dataset)\n    initial_mem_usage=dataframe.memory_usage().sum()/1024**2\n    for col in dataframe.columns:\n        col_type=dataframe[col].dtype\n        c_min=dataframe[col].min()\n        c_max=dataframe[col].max()\n        if str(col_type)[:3]=='int':\n            if c_min>np.iinfo(np.int8).min and c_max<np.iinfo(np.int8).max:\n                dataframe[col]=dataframe[col].astype(np.int8)\n            elif c_min>np.iinfo(np.int16).min and c_max<np.iinfo(np.int16).max:\n                dataframe[col]=dataframe[col].astype(np.int16)\n            elif c_min>np.iinfo(np.int32).min and c_max<np.iinfo(np.int32).max:\n                dataframe[col]=fataframe[col].astype(np.int32)\n            elif c_min>np.iinfo(np.int64).min and c_max<np.iinfo(np.int64).max:\n                dataframe[col]=dataframe[col].astype(np.int64)\n        else:\n            if c_min>np.finfo(np.float16).min and c_min<np.finfo(np.float16).max:\n                dataframe[col]=dataframe[col].astype(np.float16)\n            elif c_min>np.finfo(np.float32).min and c_min<np.finfo(np.float32).max():\n                dataframe[col]=dataframe[col].astype(np.float32)\n            else:\n                dataframe[col]=dataframe[col].astype(np.float64)\n    final_mem_usage=dataframe.memory_usage().sum()/1024**2\n    print(\"--memory usage before: {:.2f}MB\".format(initial_mem_usage))\n    print(\"--memory usage after: {:.2f}MB\".format(final_mem_usage))\n    print(\"--decreased memory usage by {:.2f}MB%\\n\".format(100*(initial_mem_usage-final_mem_usage)/initial_mem_usage))\n    return dataframe\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-11T18:07:32.569074Z","iopub.execute_input":"2025-07-11T18:07:32.569518Z","iopub.status.idle":"2025-07-11T18:07:32.580589Z","shell.execute_reply.started":"2025-07-11T18:07:32.569493Z","shell.execute_reply":"2025-07-11T18:07:32.579495Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df = pd.read_parquet('/kaggle/input/drw-crypto-market-prediction/train.parquet')\ntrain_df=reduce_mem_usage(train_df,\"train\")\ntrain_df=train_df.reset_index()\ntest_df=pd.read_parquet('/kaggle/input/drw-crypto-market-prediction/test.parquet')\nTARGET=train_df['label']","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-11T18:07:32.581793Z","iopub.execute_input":"2025-07-11T18:07:32.582137Z","iopub.status.idle":"2025-07-11T18:08:19.255683Z","shell.execute_reply.started":"2025-07-11T18:07:32.582094Z","shell.execute_reply":"2025-07-11T18:08:19.254045Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def plot_feature_trend_by_split_range_from_df(df, subset_features, target='label',\n                                              top_n=200, start_split=6, end_split=10, min_freq=4):\n    \"\"\"\n    Given a DataFrame, finds features with high correlation to the target in each split,\n    and visualizes features that appear at least `min_freq` times within the specified split range.\n    \n    Parameters:\n    - df: The entire DataFrame\n    - subset_features: List of features to use (e.g., ['X1', 'X2', ...])\n    - target: Name of the target variable\n    - top_n: Number of top features to select based on correlation in each split\n    - start_split: Starting split number\n    - end_split: Ending split number\n    - min_freq: Minimum number of appearances within the split range\n    \n    Returns:\n    - List of features that meet the specified condition\n    \"\"\"\n    from collections import Counter\n    import matplotlib.pyplot as plt\n    import numpy as np\n    import pandas as pd\n\n    n_splits = 10\n    split_size = len(df) // n_splits\n    split_corr_dict = {}\n\n    # Split별로 상관계수 높은 top_n 피처 계산\n    for i in range(n_splits):\n        start_idx = i * split_size\n        if i == n_splits - 1:\n            df_split = df.iloc[start_idx:]\n        else:\n            df_split = df.iloc[start_idx:start_idx + split_size]\n\n        corr_list = []\n        for col in subset_features:\n            if df_split[col].nunique() > 1:\n                corr = df_split[[col, target]].corr().iloc[0, 1]\n                corr_list.append((col, abs(corr)))\n            else:\n                corr_list.append((col, np.nan))\n\n        corr_series = pd.Series({k: v for k, v in corr_list if not np.isnan(v)})\n        top_features = corr_series.sort_values(ascending=False).head(top_n)\n        split_corr_dict[f'Split_{i+1}'] = top_features\n\n    # 전체 피처 목록\n    all_top_features = set()\n    for s in split_corr_dict.values():\n        all_top_features.update(s.index.tolist())\n    all_top_features = list(all_top_features)\n\n    # Split x Feature 상관계수 테이블\n    corr_trend_df = pd.DataFrame(index=[f'Split_{i+1}' for i in range(n_splits)], columns=all_top_features)\n    for split_name, corr_series in split_corr_dict.items():\n        for feature in all_top_features:\n            corr_trend_df.loc[split_name, feature] = corr_series.get(feature, np.nan)\n    corr_trend_df = corr_trend_df.astype(float)\n\n    # Step 1: 지정된 split 범위 리스트 생성\n    target_splits = [f'Split_{i}' for i in range(start_split, end_split + 1)]\n\n    # Step 2: 피처 등장 횟수 계산\n    partial_counter = Counter()\n    for split_name in target_splits:\n        partial_counter.update(split_corr_dict[split_name].index.tolist())\n\n    # Step 3: 조건에 맞는 피처 필터링\n    selected_features = [f for f, count in partial_counter.items() if count >= min_freq]\n\n    # 결과 출력\n    print(f\"\\n📌 Splits {start_split}~{end_split}에서 {min_freq}번 이상 등장한 피처 수: {len(selected_features)}\")\n    print(\" 예시 피처들:\", selected_features[:])\n\n    # Step 4: 시각화\n    x = list(range(start_split, end_split + 1))\n    plt.figure(figsize=(15, 8))\n    for feature in selected_features:\n        plt.plot(x, corr_trend_df.loc[[f'Split_{i}' for i in x], feature], marker='o', label=feature)\n\n    plt.xticks(x, [f'Split_{i}' for i in x], rotation=45)\n    plt.xlabel('Data Split')\n    plt.ylabel('Absolute Correlation with Target')\n    plt.title(f'Feature Correlation with Target (Splits {start_split} to {end_split})\\n(Features appeared ≥ {min_freq} times)')\n    #plt.legend(loc='best', fontsize='small')\n    plt.grid(True)\n    plt.tight_layout()\n    plt.show()\n    \n    del corr_trend_df\n    del split_corr_dict\n    del all_top_features\n    del target_splits\n    del partial_counter\n    gc.collect()\n\n\n    return selected_features\n\ndef remove_highly_correlated_features(df, features, threshold=0.9):\n    \"\"\"\n    Removes features that are highly correlated with others based on the specified threshold.\n\n    Parameters:\n    - df: The input DataFrame containing the features.\n    - features: A list of feature column names to evaluate.\n    - threshold: Correlation threshold above which one of the features will be removed (default is 0.9).\n\n    Returns:\n    - A list of selected features with high-correlation features removed.\n    \"\"\"\n    # Compute absolute correlation matrix\n    corr_matrix = df[features].corr().abs()\n\n    # Get the upper triangle of the correlation matrix (excluding self-correlations)\n    upper = corr_matrix.where(np.triu(np.ones(corr_matrix.shape), k=1).astype(bool))\n\n    # Identify features to drop: any feature with correlation > threshold\n    to_drop = [column for column in upper.columns if any(upper[column] > threshold)]\n\n    print(f\"🧹 Removed {len(to_drop)} features due to high correlation (>{threshold})\")\n\n    # Explicitly delete large objects\n    del corr_matrix, upper\n    gc.collect()\n\n    # Return filtered feature list\n    return [f for f in features if f not in to_drop]\n    \ndef generate_interaction_features(df, base_features, selected_features, eps=1e-3):\n    \"\"\"\n    Create derived interaction features between selected_features and base_features using:\n    - Multiplication\n    - Division (with epsilon to avoid division by zero)\n    - Addition\n    - Subtraction\n\n    Parameters:\n    - df: Input DataFrame\n    - base_features: List of all base features (e.g., X1 ~ X780)\n    - selected_features: List of important features to combine with others\n    - eps: Small constant to prevent division by zero (default=1e-3)\n\n    Returns:\n    - df_new: DataFrame containing the new derived features only\n    \"\"\"\n    \n    new_feature_dict = {}\n\n    for sel in selected_features:\n        for base in base_features:\n            if sel == base:\n                continue\n\n            # Define new feature names\n            new_feature_dict[f'{sel}_mul_{base}'] = df[sel] * df[base]\n            #new_feature_dict[f'{sel}_div_{base}'] = df[sel] / (df[base] + eps)\n            #new_feature_dict[f'{sel}_add_{base}'] = df[sel] + df[base]\n\n    # Combine all columns at once\n    df_new = pd.concat(new_feature_dict, axis=1)\n\n    print(f\" Generated {df_new.shape[1]} features from {len(selected_features)} × {len(base_features)} combinations.\")\n    return df_new\n\ndef create_interaction_features(df, feature_list, eps=1e-3):\n    \"\"\"\n    Given a DataFrame and a list of interaction feature names like 'X219_mul_X751',\n    create these features by performing the indicated operations on the columns.\n\n    Parameters:\n    - df: Input DataFrame\n    - feature_list: List of interaction feature names (e.g. 'X219_mul_X751')\n    - eps: Small constant to avoid division by zero\n\n    Returns:\n    - DataFrame with the new interaction features\n    \"\"\"\n    import numpy as np\n    df_new = pd.DataFrame(index=df.index)\n\n    for feat in feature_list:\n        if '_mul_' in feat:\n            left, right = feat.split('_mul_')\n            if left in df.columns and right in df.columns:\n                df_new[feat] = df[left] * df[right]\n\n        elif '_div_' in feat:\n            left, right = feat.split('_div_')\n            if left in df.columns and right in df.columns:\n                df_new[feat] = df[left] / (df[right] + eps)\n\n        elif '_add_' in feat:\n            left, right = feat.split('_add_')\n            if left in df.columns and right in df.columns:\n                df_new[feat] = df[left] + df[right]\n\n        elif '_sub_' in feat:\n            left, right = feat.split('_sub_')\n            if left in df.columns and right in df.columns:\n                df_new[feat] = df[left] - df[right]\n\n    return df_new","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-11T18:08:19.273275Z","iopub.execute_input":"2025-07-11T18:08:19.273771Z","iopub.status.idle":"2025-07-11T18:08:19.301724Z","shell.execute_reply.started":"2025-07-11T18:08:19.273735Z","shell.execute_reply":"2025-07-11T18:08:19.300633Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tqdm import tqdm\n\nsubset_features = [f\"X{i}\" for i in range(1, 781)]\nbatch_size = 100\n\n# 배치별로 제거 후 살아남은 피처 저장\nkept_features_total = []\n\nfor i in tqdm(range(0, len(subset_features), batch_size)):\n    batch_feats = subset_features[i:i+batch_size]\n    kept_feats = remove_highly_correlated_features(train_df, batch_feats, 0.95)\n    kept_features_total.extend(kept_feats)\n\n# 최종 피처들로 DataFrame 구성\ntrain_df_filtered = train_df[kept_features_total + ['label']]  # 타겟 포함\nprint(f\" 최종 사용 피처 수: {len(kept_features_total)}\")\nprint(train_df_filtered.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-11T18:08:19.328368Z","iopub.execute_input":"2025-07-11T18:08:19.328720Z","iopub.status.idle":"2025-07-11T18:10:15.484303Z","shell.execute_reply.started":"2025-07-11T18:08:19.328693Z","shell.execute_reply":"2025-07-11T18:10:15.483242Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"subset_features = [col for col in train_df_filtered.columns if col.startswith('X')]\n\nselected_features = plot_feature_trend_by_split_range_from_df(\n    df=train_df_filtered,\n    subset_features=subset_features,\n    target='label',\n    top_n=120,\n    start_split=6,\n    end_split=10,\n    min_freq=4\n)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-07-11T18:10:15.485243Z","iopub.execute_input":"2025-07-11T18:10:15.485489Z","iopub.status.idle":"2025-07-11T18:11:42.072498Z","shell.execute_reply.started":"2025-07-11T18:10:15.485469Z","shell.execute_reply":"2025-07-11T18:11:42.071548Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"selected_features2 = plot_feature_trend_by_split_range_from_df(\n    df=train_df_filtered,\n    subset_features=subset_features,\n    target='label',\n    top_n=180,\n    start_split=1,\n    end_split=10,\n    min_freq=9\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-11T18:11:42.073315Z","iopub.execute_input":"2025-07-11T18:11:42.073550Z","iopub.status.idle":"2025-07-11T18:13:09.954781Z","shell.execute_reply.started":"2025-07-11T18:11:42.073533Z","shell.execute_reply":"2025-07-11T18:13:09.953701Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"Features=list(set(selected_features+selected_features2))\nbase_feature=remove_highly_correlated_features(train_df_filtered, Features)\ntest_df_filtered = test_df[base_feature].copy()\ndf_new_features = generate_interaction_features(\n    train_df_filtered,              \n    subset_features[::4],          \n    base_feature                   \n)\ndf_new_features = pd.concat([df_new_features, TARGET], axis=1, join='inner')\nsubset_features = [col for col in df_new_features.columns if col.startswith('X')]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-11T18:13:09.963515Z","iopub.execute_input":"2025-07-11T18:13:09.963882Z","iopub.status.idle":"2025-07-11T18:13:17.792690Z","shell.execute_reply.started":"2025-07-11T18:13:09.963843Z","shell.execute_reply":"2025-07-11T18:13:17.791625Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"subset_features_4_multiple = df_new_features.columns[::4].tolist()\n\nselected_features3 = plot_feature_trend_by_split_range_from_df(\n    df=df_new_features,\n    subset_features=subset_features_4_multiple,\n    target='label',\n    top_n=150,\n    start_split=1,\n    end_split=10,\n    min_freq=8\n)\nselected_features","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-11T18:13:58.338424Z","iopub.execute_input":"2025-07-11T18:13:58.338782Z","iopub.status.idle":"2025-07-11T18:20:17.308794Z","shell.execute_reply.started":"2025-07-11T18:13:58.338755Z","shell.execute_reply":"2025-07-11T18:20:17.307819Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"derived_feature=remove_highly_correlated_features(df_new_features, selected_features3, 0.95)\ndf_new_test_features = create_interaction_features(test_df, derived_feature)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-11T18:20:17.583221Z","iopub.execute_input":"2025-07-11T18:20:17.583464Z","iopub.status.idle":"2025-07-11T18:20:19.004127Z","shell.execute_reply.started":"2025-07-11T18:20:17.583445Z","shell.execute_reply":"2025-07-11T18:20:19.002966Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"additional_features = [\n    \"bid_qty\", \"ask_qty\", \"buy_qty\", \"sell_qty\", \"volume\",\n    \"X752\", \"X287\", \"X298\", \"X759\", \"X302\", \"X55\", \"X56\",\n    \"X52\", \"X303\", \"X51\", \"X598\", \"X385\", \"X603\", \"X674\",\n    \"X415\", \"X345\", \"X174\", \"X178\", \"X168\", \"X612\",\n]\n\n# 기존 리스트 + 추가 리스트를 합친 후 set으로 중복 제거\nbase_feature = list(set(base_feature + additional_features))\ntest_df = pd.concat([test_df[base_feature], df_new_test_features], axis=1)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-11T18:20:19.080273Z","iopub.execute_input":"2025-07-11T18:20:19.080520Z","iopub.status.idle":"2025-07-11T18:20:19.085954Z","shell.execute_reply.started":"2025-07-11T18:20:19.080501Z","shell.execute_reply":"2025-07-11T18:20:19.085039Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import KFold\nfrom sklearn.metrics import mean_absolute_error\nfrom xgboost import XGBRegressor\nfrom scipy.stats import pearsonr\nimport numpy as np\nimport pandas as pd\n\n# 2. 3-Fold Cross Validation 학습 및 평가 (Pearson correlation 사용)\nkf = KFold(n_splits=3, shuffle=False)\npearson_list = []\nfold = 1\n\nfor train_idx, val_idx in kf.split(train_df):\n    X_train = pd.concat([\n        train_df.iloc[train_idx][base_feature].reset_index(drop=True),\n        df_new_features.iloc[train_idx][derived_feature].reset_index(drop=True)\n    ], axis=1)\n    y_train = TARGET.iloc[train_idx].reset_index(drop=True)\n\n    X_val = pd.concat([\n        train_df.iloc[val_idx][base_feature].reset_index(drop=True),\n        df_new_features.iloc[val_idx][derived_feature].reset_index(drop=True)\n    ], axis=1)\n    y_val = TARGET.iloc[val_idx].reset_index(drop=True)\n\n    model = XGBRegressor(\n        n_estimators=500,\n        learning_rate=0.03,\n        max_depth=6,\n        min_child_weight=3,\n        gamma=1,\n        subsample=0.2,\n        colsample_bytree=0.7,\n        reg_alpha=20,\n        reg_lambda=20,\n        random_state=42,\n        tree_method='hist',\n        verbosity=0\n    )\n    model.fit(X_train, y_train)\n\n    val_preds = model.predict(X_val)\n    pearson_corr, _ = pearsonr(y_val, val_preds)\n    print(f\"Fold {fold} Pearson Correlation: {pearson_corr:.4f}\")\n    pearson_list.append(pearson_corr)\n    fold += 1\n\nprint(f\"\\nAverage Pearson Correlation over 3 folds: {np.mean(pearson_list):.4f}\")\n\n# 3. 최종 모델 전체 데이터로 학습\nX_full = pd.concat([\n    train_df[base_feature].reset_index(drop=True),\n    df_new_features[derived_feature].reset_index(drop=True)\n], axis=1)\ny_full = TARGET.reset_index(drop=True)\n\nfinal_model = XGBRegressor(\n    n_estimators=500,\n    learning_rate=0.03,\n    max_depth=6,\n    min_child_weight=3,\n    gamma=1,\n    subsample=0.2,\n    colsample_bytree=0.7,\n    reg_alpha=20,\n    reg_lambda=20,\n    random_state=42,\n    tree_method='hist',\n    verbosity=0\n)\nfinal_model.fit(X_full, y_full)\n\n\n# 5. 예측 및 제출 파일 생성\ntest_preds = final_model.predict(test_df)\n\nsubmission = pd.DataFrame({\n    'ID': test_df.index,  # 인덱스를 id로 사용\n    'prediction': test_preds\n})\nsubmission.to_csv('submission.csv', index=False)\nprint(\"✅ submission.csv saved!\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-11T18:20:20.131145Z","iopub.execute_input":"2025-07-11T18:20:20.131400Z","iopub.status.idle":"2025-07-11T18:23:39.595208Z","shell.execute_reply.started":"2025-07-11T18:20:20.131379Z","shell.execute_reply":"2025-07-11T18:23:39.594226Z"}},"outputs":[],"execution_count":null}]}