{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceType":"competition","sourceId":132373,"databundleVersionId":15871996}],"dockerImageVersionId":31328,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"\n<span style=\"font-size:20px; font-weight:bold; color:#2E86C1;\">\nKMU 데이터 사이언스 과제 — 2026.04.03\n</span>\n\n## 1. 문제 정의\n\n**[비즈니스 문제]**\n\n인당 연간 구매 빈도가 높은 이커머스 카테고리에서, 고객이 향후 30일 이내에 구매할 것인지(Purchase Prediction) 예측하는 것은 리텐션·리타겟 관점에서 매우 중요합니다.\n그로서리·리빙소품 등은 CLTV를 지속적으로 키우는 것이 목표이며, 마케팅 비용(쿠폰·할인·광고비)은 줄이고 활성 고객 수를 누적 성장시키고자 합니다.\n\n**[비즈니스 임팩트]**\n\n① **마케팅 ROI 극대화** — 구매 가능성이 높은 고객에게는 전환을 굳히는 넛지(Nudge) 마케팅,\n이탈 가능성이 높은 고객에게는 timelimit 쿠폰을 제공하는 등 타겟팅 정교화로 예산 효율 극대화\n\n- 구매 가능성 높은 고객 → 신상품 선출시 + 할인 쿠폰으로 플랫폼 경험율 제고\n- 이탈 가능성 높은 고객 → 연관 아이템 또는 고할인 쿠폰 제시로 전환율 제고 (or 쿠폰 발행 제한)\n\n② **운영 효율화** — 단기(30일) 구매 수요 예측으로 온사이트 마케팅 플랜 선기획 가능\n\n**[성공 기준]**\n\n\n- 기술적 메트릭 - ROC-AUC **85% 이상** (고객 ID별 구매 확률 0~1 연속값) \n- 비즈니스 목표 - CLTV별 마케팅 예산 계획 수립 / False Positive(불필요 쿠폰 발급) 최소화 \n\n\n\n## 2. 데이터 탐색 (EDA)\n\n\n**[피쳐 테이블]**\n\n*구매cat 누락 - 구매 빈도 \n\n*디바이스 타입 고민중 \n\n\n<style>\n  .ft { border-collapse:collapse; width:100%; font-family:sans-serif; font-size:14px; }\n  .ft th { background:#2C5F8A; color:white; padding:10px 14px; text-align:center; }\n  .ft td { padding:10px 14px; border:1px solid #C8D8E8; vertical-align:middle; }\n  .ft tr:nth-child(even) td { background:#F7F9FC; }\n  .ft tr:nth-child(odd)  td { background:#FFFFFF; }\n  .ft .fn { background:#EBF3FB; color:#185FA5; font-weight:bold; text-align:center; font-size:17px; }\n  .ft .gd { color:#5F5E5A; }\n</style>\n<table class=\"ft\">\n  <tr><th>파일</th><th>주요 컬럼</th><th>집계 방향</th></tr>\n  <tr>\n    <td class=\"fn\">customers</td>\n    <td>age, loyalty_score, email_open_rate,<br>discount_usage_rate, avg_review_score, customer_tier, credit_limit</td>\n    <td class=\"gd\">as-is (1:1) — customer_id별</td>\n  </tr>\n  <tr>\n    <td class=\"fn\">transactions</td>\n    <td>order_value, items_count, payment_method,<br>discount_applied, shipping_speed, high_value_flag</td>\n    <td class=\"gd\">고객별 집계</td>\n  </tr>\n  <tr>\n    <td class=\"fn\">sessions</td>\n    <td>session_duration, pages_viewed, cart_additions,<br>bounce_flag, traffic_source, device_type</td>\n    <td class=\"gd\">고객별 집계</td>\n  </tr>\n  <tr>\n    <td class=\"fn\">geo_data</td>\n    <td>average_income, urban_ratio,<br>internet_penetration, region_tier</td>\n    <td class=\"gd\">geo_ip_region 기준 join</td>\n  </tr>\n  <tr>\n    <td class=\"fn\">marketing</td>\n    <td>campaign_type, campaign_budget</td>\n    <td class=\"gd\">campaign_id 기준 → sessions에 join 후 집계</td>\n  </tr>\n</table>\n\n\n\n\n**[데이터 타입&결측치]**\n\n| no | 항목 | 수치 | 처리 방향 |\n|---|---|---|---|\n| ① | 데이터 shape | (200,000 × 16) | — |\n| ② | email_open_rate dtype | float64 | — |\n| ③ | email_open_rate 결측 | 45,823건 / 22.91% | 동일 고객 그룹 + 최근 구매일 + 동일 review 그룹 기준 대체 예정 |\n| ④ | discount_usage_rate 결측 | 38,756건 / 19.38% | 처리 방법 검토 중 |\n| ⑤ | 두 컬럼 동시 결측 | 12,345건 / 6.17% | — |\n\n\n\n\n## 3. 접근법  ##\n\n#### ML 문제 유형 → 이진 분류 (Binary Classification)\n\n\n\n**[Top 5 예측 변수]**\n\n| 순위 | 변수 | 선택 근거 |\n|---|---|---|\n| ① | loyalty_score | cltv가 높을 확률. 가장 직관적이고 강력한 예측력 기대 |\n| ② | recency_days | \"얼마나 최근에 구매했는가\"가 30일 내 구매 예측과 가장 연관성 높을 것으로 예상 |\n| ③ | customer_tier | 4개 카테고리 — 등급별 구매 패턴 분석 예정 |\n| ④ | avg_review_score | 만족도가 재구매 의향에 직결. 낮은 등급 고객은 재구매 의사 낮을 것으로 판단 |\n| ⑤ | shipping_speed | 배송 속도 민감 고객 파악. 만족도 높을수록 플랫폼 이탈 가능성 낮음 |\n\n\n\n**[변수 분류]**\n\n**① RFM 기반 피처** (transactions에서 생성)\n- Recency : 마지막 거래일 ~ 기준일까지 일수\n- Frequency : 고객별 총 거래 횟수\n- Monetary : 총 주문 금액 합계 → 구매 예측 모델에서 가장 강력한 예측력 예상\n\n**② 고객 행동 지표** (customers)\n- loyalty_score, email_open_rate, discount_usage_rate, avg_review_score\n- customer_tier (Bronze/Silver/Gold/Platinum) → ordinal encoding 적용\n\n**③ 지역 특성** (geo_data + customers.region join)\n- average_income, urban_ratio, internet_penetration\n- region_tier (Tier1~3) → 지역별 구매력·접근성 반영\n\n**④ 거래 세부 행동** (transactions)\n- payment_method 다양성 (crypto 사용 여부 등)\n- shipping_speed, high_value_flag, discount_applied 비율, items_count\n\n\n**[noise 컬럼 검토]**\n\n- 상관관계 아직 명확히 파악되지 않음. cardinality 높음 (약 150개 ID)\n- ID별 대략적인 그래프를 그려 추세 확인 → noise 그래프와 각 변수 그래프 간 유사한 흐름 존재하나 완전히 동일하지 않음\n- 일부 구간에서 영향 없는 것으로 확인 → **추가 검증 후 제거 여부 결정 예정**\n\n\n\n## 4. 기대 결과 & 리스크 ##\n\n**[주요 리스크]**\n\n- 클래스 불균형 | 현실 이커머스 데이터는 가입자 대비 활성 사용자가 5% 미만 가능\n   → 대부분의 target이 0 | class_weight / SMOTE / scale_pos_weight 적용 \n- 데이터 품질 - 결측치 다수 (email_open_rate 23%, discount_usage_rate 19%)\n   →커스터머 그룹 기반(평균) 대체 or missing flag 피처 생성 \n- 과적합 - 피처 수 51개, 트리 계열 모델 → Cross-validation + Early stopping\n\n\n**[6주 실행 계획]**\n\n| 주차 | 목표 |\n|---|---|\n| 1주 | 데이터 탐색 & 전처리 |\n| 2주 | 피처 엔지니어링 1 |\n| 3주 | 피처 엔지니어링 2 |\n| 4주 | 베이스라인 모델 (LightGBM) &모델 튜닝 & 피처 선택 |\n| 5주 | 앙상블 & 검증 |\n| 6주 | 발표 자료 & 최종 제출 |\n","metadata":{}},{"cell_type":"code","source":"import os\nprint(\"Input 폴더 확인\")\nprint(os.listdir('/kaggle/input'))\n\nprint(\"\\n대회 데이터 폴더 확인\")\nfor folder in os.listdir('/kaggle/input'):\n    if 'e-commerce' in folder.lower() or 'purchase' in folder.lower():\n        print(\"✅ 찾음:\", folder)\n        path = f'/kaggle/input/{folder}'\n        print(\"파일 목록:\", os.listdir(path))\n        break","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:26:43.251849Z","iopub.execute_input":"2026-04-04T08:26:43.252779Z","iopub.status.idle":"2026-04-04T08:26:43.260060Z","shell.execute_reply.started":"2026-04-04T08:26:43.252744Z","shell.execute_reply":"2026-04-04T08:26:43.258629Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport os\n\n# competitions 안에 있는 대회 폴더 찾기\ncomp_path = '/kaggle/input/competitions'\n\nprint(\"Competitions 폴더 확인\")\nprint(os.listdir(comp_path))\n\n# 대회 폴더 이름 찾기\nfor folder in os.listdir(comp_path):\n    if 'e-commerce' in folder.lower() or 'purchase' in folder.lower():\n        data_path = f'{comp_path}/{folder}/'\n        print(\"✅ 대회 폴더 찾음:\", folder)\n        print(\"파일 목록:\", os.listdir(data_path))\n        \n        # train과 test 불러오기\n        train = pd.read_csv(data_path + 'train.csv')\n        test  = pd.read_csv(data_path + 'test.csv')\n        \n        print(\"\\nTrain shape:\", train.shape)\n        print(\"Test shape :\", test.shape)\n        print(\"Train 컬럼:\", train.columns.tolist())\n        break\n        ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:27:45.460918Z","iopub.execute_input":"2026-04-04T08:27:45.461319Z","iopub.status.idle":"2026-04-04T08:27:45.550019Z","shell.execute_reply.started":"2026-04-04T08:27:45.461289Z","shell.execute_reply":"2026-04-04T08:27:45.548851Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\n# 전처리 1차 \ndata_path = '/kaggle/input/competitions/global-e-commerce-customer-purchase-prediction/'\n\n# customers.csv 불러오기\ncustomers = pd.read_csv(data_path + 'customers.csv')\n\nprint(\"customers shape:\", customers.shape)\nprint(\"customers 컬럼:\", customers.columns.tolist())\nprint(\"\\n처음 5행:\")\nprint(customers.head())\n\n# train과 customers 합치기 (customer_id 기준)\ntrain = pd.read_csv(data_path + 'train.csv')\nmerged = train.merge(customers, on='customer_id', how='left')\n\nprint(\"\\nmerged shape:\", merged.shape)\nprint(merged.head())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:29:46.473530Z","iopub.execute_input":"2026-04-04T08:29:46.474557Z","iopub.status.idle":"2026-04-04T08:29:47.911338Z","shell.execute_reply.started":"2026-04-04T08:29:46.474506Z","shell.execute_reply":"2026-04-04T08:29:47.910267Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\n#  전처리 1차 후  간단한feature 탐색\n\ndata_path = '/kaggle/input/competitions/global-e-commerce-customer-purchase-prediction/'\n\n# 파일 불러오기\ntrain = pd.read_csv(data_path + 'train.csv')\ncustomers = pd.read_csv(data_path + 'customers.csv')\ntransactions = pd.read_csv(data_path + 'transactions.csv')\nsessions = pd.read_csv(data_path + 'sessions.csv')\n\nprint(\"불러오기 완료\")\nprint(\"train:\", train.shape)\nprint(\"customers:\", customers.shape)\nprint(\"transactions:\", transactions.shape)\nprint(\"sessions:\", sessions.shape)\n\n# 1. customers + train 합치기\ndf = train.merge(customers, on='customer_id', how='left')\n\n# 2. 간단한 전처리 (noise 컬럼 제거 + 날짜 처리)\nnoise_cols = [col for col in df.columns if 'noise_customer' in col]\ndf = df.drop(columns=noise_cols, errors='ignore')\n\n# signup_date를 날짜로 변환\ndf['signup_date'] = pd.to_datetime(df['signup_date'], errors='coerce')\n\nprint(\"\\n기본 merge 후 shape:\", df.shape)\nprint(\"컬럼:\", df.columns.tolist())\nprint(df.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:30:47.703044Z","iopub.execute_input":"2026-04-04T08:30:47.703436Z","iopub.status.idle":"2026-04-04T08:30:54.619497Z","shell.execute_reply.started":"2026-04-04T08:30:47.703407Z","shell.execute_reply":"2026-04-04T08:30:54.618245Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\n\ndata_path = '/kaggle/input/competitions/global-e-commerce-customer-purchase-prediction/'\n\ntransactions = pd.read_csv(data_path + 'transactions.csv')\n\nprint(\"transactions 컬럼 목록:\")\nprint(transactions.columns.tolist())\nprint(\"\\n처음 3행:\")\nprint(transactions.head(3))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:37:57.501539Z","iopub.execute_input":"2026-04-04T08:37:57.502569Z","iopub.status.idle":"2026-04-04T08:37:58.419364Z","shell.execute_reply.started":"2026-04-04T08:37:57.502531Z","shell.execute_reply":"2026-04-04T08:37:58.418238Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nfrom datetime import datetime\n\ndata_path = '/kaggle/input/competitions/global-e-commerce-customer-purchase-prediction/'\n\ntrain = pd.read_csv(data_path + 'train.csv')\ncustomers = pd.read_csv(data_path + 'customers.csv')\ntransactions = pd.read_csv(data_path + 'transactions.csv')\n\n# 기본 merge\ndf = train.merge(customers, on='customer_id', how='left')\n\n# noise 컬럼 제거\nnoise_cols = [col for col in df.columns if 'noise' in col]\ndf = df.drop(columns=noise_cols, errors='ignore')\n\n# transaction_timestamp → datetime 변환\ntransactions['transaction_timestamp'] = pd.to_datetime(transactions['transaction_timestamp'], errors='coerce')\n\n# order_value 전처리: $ 기호와 쉼표(,) 제거 후 숫자로 변환\ntransactions['order_value'] = (\n    transactions['order_value']\n    .astype(str)\n    .str.replace('$', '', regex=False)      # $ 제거\n    .str.replace(',', '', regex=False)      # 쉼표 제거\n    .astype(float)\n)\n\n# 고객별 RFM 계산\nrfm = transactions.groupby('customer_id').agg({\n    'transaction_timestamp': lambda x: (datetime.now() - x.max()).days,   # Recency\n    'transaction_id': 'count',                                            # Frequency\n    'order_value': ['sum', 'mean']                                        # Monetary\n}).reset_index()\n\nrfm.columns = ['customer_id', 'recency', 'frequency', 'monetary_sum', 'monetary_mean']\n\n# df에 RFM 붙이기\ndf = df.merge(rfm, on='customer_id', how='left')\n\nprint(\"RFM 추가 후 shape:\", df.shape)\nprint(\"\\nRFM 앞 5행:\")\nprint(df[['recency', 'frequency', 'monetary_sum', 'monetary_mean']].head())\nprint(\"\\nRFM 요약 통계:\")\nprint(df[['recency', 'frequency', 'monetary_sum', 'monetary_mean']].describe())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:41:12.834215Z","iopub.execute_input":"2026-04-04T08:41:12.834626Z","iopub.status.idle":"2026-04-04T08:41:34.011368Z","shell.execute_reply.started":"2026-04-04T08:41:12.834589Z","shell.execute_reply":"2026-04-04T08:41:34.010365Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import lightgbm as lgb\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import roc_auc_score\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# 전처리: age와 credit_limit 숫자로 변환\ndf['age'] = pd.to_numeric(df['age'], errors='coerce')                    # unknown → NaN\ndf['credit_limit'] = df['credit_limit'].astype(str).str.replace('$', '', regex=False).str.replace(',', '', regex=False)\ndf['credit_limit'] = pd.to_numeric(df['credit_limit'], errors='coerce')\n\n# 사용할 피처 선택\nfeature_cols = ['age', 'loyalty_score', 'email_open_rate', 'discount_usage_rate', \n                'avg_review_score', 'credit_limit', 'recency', 'frequency', \n                'monetary_sum', 'monetary_mean']\n\nX = df[feature_cols]\ny = df['target']\n\n# Train / Valid split\nX_train, X_valid, y_train, y_valid = train_test_split(\n    X, y, test_size=0.2, random_state=42, stratify=y\n)\n\nprint(\"LightGBM 학습 시작...\")\n\nlgb_train = lgb.Dataset(X_train, y_train)\nlgb_valid = lgb.Dataset(X_valid, y_valid, reference=lgb_train)\n\nparams = {\n    'objective': 'binary',\n    'metric': 'auc',\n    'learning_rate': 0.05,\n    'num_leaves': 64,\n    'max_depth': -1,\n    'subsample': 0.8,\n    'colsample_bytree': 0.8,\n    'n_estimators': 1000,\n    'verbose': -1,\n    'random_state': 42,\n    'class_weight': 'balanced'\n}\n\nmodel = lgb.train(\n    params,\n    lgb_train,\n    valid_sets=[lgb_valid],\n    callbacks=[lgb.early_stopping(100), lgb.log_evaluation(100)]\n)\n\n# Valid 점수\nvalid_pred = model.predict(X_valid)\nprint(\"\\nValid AUC:\", roc_auc_score(y_valid, valid_pred))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:43:15.425551Z","iopub.execute_input":"2026-04-04T08:43:15.425881Z","iopub.status.idle":"2026-04-04T08:43:17.311787Z","shell.execute_reply.started":"2026-04-04T08:43:15.425853Z","shell.execute_reply":"2026-04-04T08:43:17.310794Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# (RFM + LightGBM)\n\nimport pandas as pd\nimport lightgbm as lgb\nfrom datetime import datetime\n\ndata_path = '/kaggle/input/competitions/global-e-commerce-customer-purchase-prediction/'\n\n# 데이터 불러오기\ntrain = pd.read_csv(data_path + 'train.csv')\ntest = pd.read_csv(data_path + 'test.csv')\ncustomers = pd.read_csv(data_path + 'customers.csv')\ntransactions = pd.read_csv(data_path + 'transactions.csv')\n\n# 1. customers merge\ntrain_df = train.merge(customers, on='customer_id', how='left')\ntest_df  = test.merge(customers, on='customer_id', how='left')\n\n# noise 제거\nnoise_cols = [col for col in train_df.columns if 'noise' in col]\ntrain_df = train_df.drop(columns=noise_cols, errors='ignore')\ntest_df  = test_df.drop(columns=noise_cols, errors='ignore')\n\n# 2. order_value 전처리 ($와 쉼표 제거)\ntransactions['order_value'] = (\n    transactions['order_value']\n    .astype(str)\n    .str.replace('$', '', regex=False)\n    .str.replace(',', '', regex=False)\n    .astype(float)\n)\n\n# transaction_timestamp 변환\ntransactions['transaction_timestamp'] = pd.to_datetime(transactions['transaction_timestamp'], errors='coerce')\n\n# 3. RFM 계산 (train + test 공통)\nrfm = transactions.groupby('customer_id').agg({\n    'transaction_timestamp': lambda x: (datetime.now() - x.max()).days,\n    'transaction_id': 'count',\n    'order_value': ['sum', 'mean']\n}).reset_index()\n\nrfm.columns = ['customer_id', 'recency', 'frequency', 'monetary_sum', 'monetary_mean']\n\n# RFM merge\ntrain_df = train_df.merge(rfm, on='customer_id', how='left')\ntest_df  = test_df.merge(rfm, on='customer_id', how='left')\n\n# 4. age, credit_limit 숫자로 변환\ntrain_df['age'] = pd.to_numeric(train_df['age'], errors='coerce')\ntest_df['age']  = pd.to_numeric(test_df['age'], errors='coerce')\n\ntrain_df['credit_limit'] = train_df['credit_limit'].astype(str).str.replace('$', '', regex=False).str.replace(',', '', regex=False)\ntest_df['credit_limit']  = test_df['credit_limit'].astype(str).str.replace('$', '', regex=False).str.replace(',', '', regex=False)\n\ntrain_df['credit_limit'] = pd.to_numeric(train_df['credit_limit'], errors='coerce')\ntest_df['credit_limit']  = pd.to_numeric(test_df['credit_limit'], errors='coerce')\n\n# 5. 학습용 피처\nfeature_cols = ['age', 'loyalty_score', 'email_open_rate', 'discount_usage_rate', \n                'avg_review_score', 'credit_limit', 'recency', 'frequency', \n                'monetary_sum', 'monetary_mean']\n\nX = train_df[feature_cols]\ny = train_df['target']\nX_test = test_df[feature_cols]\n\n# Train/Valid split\nX_train, X_valid, y_train, y_valid = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)\n\n# LightGBM 학습\nlgb_train = lgb.Dataset(X_train, y_train)\nlgb_valid = lgb.Dataset(X_valid, y_valid, reference=lgb_train)\n\nparams = {\n    'objective': 'binary',\n    'metric': 'auc',\n    'learning_rate': 0.05,\n    'num_leaves': 64,\n    'subsample': 0.8,\n    'colsample_bytree': 0.8,\n    'n_estimators': 1000,\n    'verbose': -1,\n    'random_state': 42,\n    'class_weight': 'balanced'\n}\n\nmodel = lgb.train(\n    params, lgb_train, \n    valid_sets=[lgb_valid],\n    callbacks=[lgb.early_stopping(100), lgb.log_evaluation(100)]\n)\n\n# Valid AUC\nvalid_pred = model.predict(X_valid)\nprint(\"Valid AUC:\", roc_auc_score(y_valid, valid_pred))\n\n# TEST 예측\ntest_pred = model.predict(X_test)\n\n# Submission\nsubmission = pd.DataFrame({\n    'customer_id': test['customer_id'],\n    'target': test_pred\n})\n\nsubmission.to_csv('submission_lgbm_rfm.csv', index=False)\nprint(\"Submission 파일 저장 완료!\")\nprint(submission.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:44:06.461218Z","iopub.execute_input":"2026-04-04T08:44:06.461550Z","iopub.status.idle":"2026-04-04T08:44:29.325888Z","shell.execute_reply.started":"2026-04-04T08:44:06.461522Z","shell.execute_reply":"2026-04-04T08:44:29.324630Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 제출용 파일 이름  생성 trial01\n\nsubmission = pd.DataFrame({\n    'customer_id': test['customer_id'],\n    'target': test_pred\n})\n\nsubmission.to_csv('submission.csv', index=False)\nprint(\"submission.csv 파일 생성 완료!\")\nprint(submission.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:47:28.033178Z","iopub.execute_input":"2026-04-04T08:47:28.033524Z","iopub.status.idle":"2026-04-04T08:47:28.142546Z","shell.execute_reply.started":"2026-04-04T08:47:28.033496Z","shell.execute_reply":"2026-04-04T08:47:28.141447Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nprint(\"submission.csv 파일이 제대로 만들어졌는지 확인:\")\nprint('submission.csv' in os.listdir('/kaggle/working/'))\n\n# 파일 크기 확인\nif 'submission.csv' in os.listdir('/kaggle/working/'):\n    size = os.path.getsize('/kaggle/working/submission.csv') / 1024\n    print(f\"파일 크기: {size:.1f} KB\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:48:39.610059Z","iopub.execute_input":"2026-04-04T08:48:39.610843Z","iopub.status.idle":"2026-04-04T08:48:39.617516Z","shell.execute_reply.started":"2026-04-04T08:48:39.610809Z","shell.execute_reply":"2026-04-04T08:48:39.616304Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 제출용 파일을 정확한 이름으로 다시 생성\nsubmission = pd.DataFrame({\n    'customer_id': test['customer_id'],\n    'target': test_pred\n})\n\nsubmission.to_csv('submission.csv', index=False)\n\nprint(\"submission.csv 다시 생성 완료!\")\nprint(\"파일 크기:\", round(os.path.getsize('/kaggle/working/submission.csv') / 1024, 1), \"KB\")\nprint(submission.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:51:21.312543Z","iopub.execute_input":"2026-04-04T08:51:21.312912Z","iopub.status.idle":"2026-04-04T08:51:21.418488Z","shell.execute_reply.started":"2026-04-04T08:51:21.312883Z","shell.execute_reply":"2026-04-04T08:51:21.417235Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# submission.csv를 다시 정확히 생성 (Kaggle이 인식할 수 있게)\nsubmission = pd.DataFrame({\n    'customer_id': test['customer_id'],\n    'target': test_pred\n})\n\nsubmission.to_csv('submission.csv', index=False)\n\nprint(\"submission.csv 다시 생성 완료!\")\nprint(\"파일 크기:\", round(os.path.getsize('/kaggle/working/submission.csv') / 1024, 1), \"KB\")\nprint(submission.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-04T08:55:58.982145Z","iopub.execute_input":"2026-04-04T08:55:58.983072Z","iopub.status.idle":"2026-04-04T08:55:59.089403Z","shell.execute_reply.started":"2026-04-04T08:55:58.983031Z","shell.execute_reply":"2026-04-04T08:55:59.088451Z"}},"outputs":[],"execution_count":null}]}