{"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":96164,"databundleVersionId":12993472}],"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,"execution":{"iopub.status.busy":"2026-05-03T13:57:29.568721Z","iopub.execute_input":"2026-05-03T13:57:29.569048Z","iopub.status.idle":"2026-05-03T13:57:30.602918Z","shell.execute_reply.started":"2026-05-03T13:57:29.569012Z","shell.execute_reply":"2026-05-03T13:57:30.601945Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-03T13:57:30.604680Z","iopub.execute_input":"2026-05-03T13:57:30.605268Z","iopub.status.idle":"2026-05-03T13:57:30.609741Z","shell.execute_reply.started":"2026-05-03T13:57:30.605234Z","shell.execute_reply":"2026-05-03T13:57:30.608770Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Bandysime sukonstruoti nepersimokantį tiesinį prediktorių $\\hat{y}_n=\\alpha +\\mathbf{x}_n^T\\beta, \\; n=1,\\dots,N$ (arba matriciniu pavidalu: $\\hat{Y} = \\alpha + \\mathbf{X}\\beta$).","metadata":{}},{"cell_type":"markdown","source":"Nuskaitome train duomenis kaip np.array, susidedantį iš $S=12$ chronologiškai išsidėsčiusių sample'ų. Kadangi train duomenys apima metus, tai kiekvienas sample maždaug atitinka vieną mėnesį.","metadata":{}},{"cell_type":"code","source":"S = 12\n\nprint(\"reading train data...\")\ndf = pd.read_parquet('/kaggle/input/competitions/drw-crypto-market-prediction/train.parquet', engine='pyarrow')\n\nprint(\"preparing train X, y...\")\ny = df['label'].values\nN = len(y) # eilučių (duomenų) skaičius\nX = df.drop('label', axis=1)\nX = X.to_numpy()\nK = X.shape[1] # stulpelių skaičius\ny = np.array_split(y, S)\nX = np.array_split(X, S)\n\n# DEBUG: ar np.array_split suveikė korektiškai\ndata_ok = True\nfor s in range(S):\n    if (X[s].shape[0] != y[s].shape[0]):\n        data_ok = False\nprint('Data ok: ', data_ok)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-03T13:57:30.611129Z","iopub.execute_input":"2026-05-03T13:57:30.611479Z","iopub.status.idle":"2026-05-03T13:57:47.067982Z","shell.execute_reply.started":"2026-05-03T13:57:30.611436Z","shell.execute_reply":"2026-05-03T13:57:47.067012Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Ištrinam df, kad sutaupytume RAM - duomenis turime np.array X, y.","metadata":{}},{"cell_type":"code","source":"del df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-03T13:57:47.069889Z","iopub.execute_input":"2026-05-03T13:57:47.070229Z","iopub.status.idle":"2026-05-03T13:57:47.076496Z","shell.execute_reply.started":"2026-05-03T13:57:47.070196Z","shell.execute_reply":"2026-05-03T13:57:47.075375Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Padalinam train duomenis į 2 grupes: valid sample'ai apima pirmus devynis mėnesius, train sample'ai apima paskutinius tris mėnesius. Idėja tokia: jeigu iš train modelio galim gerai prognozuoti praeitį (valid duomenis), tai potencialiai galėsim prognozuoti ir ateitį.","metadata":{}},{"cell_type":"code","source":"# pirmus šešis mėnesius naudosim validacijai\nvalid_samples = range(9)\n# paskutinis šešis mėnesius naudosim trainingui\ntrain_samples = range(9,S)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-03T13:57:47.077558Z","iopub.execute_input":"2026-05-03T13:57:47.077863Z","iopub.status.idle":"2026-05-03T13:57:47.095150Z","shell.execute_reply.started":"2026-05-03T13:57:47.077830Z","shell.execute_reply":"2026-05-03T13:57:47.094189Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Suskaičiuojam train set'o vidurkius ir standartinius nuokrypius.","metadata":{}},{"cell_type":"code","source":"X_sum = np.zeros(K)\nX2_sum = np.zeros(K)\nN_train = np.zeros(K)\nfor s in train_samples:\n    X_sum = X_sum + np.sum(X[s], axis=0)\n    X2_sum = X2_sum + np.sum(np.square(X[s]), axis=0)\n    N_train = N_train + X[s].shape[0]\nX_mu = X_sum / N_train\nX2_mu = X2_sum / N_train\nX_sd = np.sqrt(X2_mu - np.square(X_mu))\n\nplt.subplot(2, 1, 1)\nplt.hist(X_mu[5:], bins='auto')\nplt.title(\"Histogram of means\")\nplt.subplot(2, 1, 2)\nplt.hist(X_sd[5:], bins='auto')\nplt.title(\"Histogram of sds\")\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-03T13:57:47.096313Z","iopub.execute_input":"2026-05-03T13:57:47.096608Z","iopub.status.idle":"2026-05-03T13:57:48.020680Z","shell.execute_reply.started":"2026-05-03T13:57:47.096567Z","shell.execute_reply":"2026-05-03T13:57:48.019383Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Standartizuojam duomenis, kad train set'e vidurkiai būtų lygūs 0, o std. nuokrypiai lygūs 1.","metadata":{}},{"cell_type":"code","source":"for s in range(S):\n    X[s] = (X[s]-X_mu) / X_sd","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-03T13:57:48.021921Z","iopub.execute_input":"2026-05-03T13:57:48.022810Z","iopub.status.idle":"2026-05-03T13:57:50.221197Z","shell.execute_reply.started":"2026-05-03T13:57:48.022772Z","shell.execute_reply":"2026-05-03T13:57:50.220054Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Pagalbinės funkcijos skaičiuoti vidurkiui ir standartiniam nuokrypiui. Vidurkis skaičiuojamas nurodytiems samples (gali būti train arba valid).","metadata":{}},{"cell_type":"code","source":"def DataMean(y, samples):\n    y_sum = 0.0\n    N_total = 0\n    for s in samples:\n        y_sum = y_sum + np.sum(y[s])\n        N_total = N_total + y[s].shape[0]\n    return(y_sum / N_total)\n\ndef DataSqMean(y, samples):\n    y2_sum = 0.0\n    N_total = 0\n    for s in samples:\n        y2_sum = y2_sum + np.sum(np.square(y[s]))\n        N_total = N_total + y[s].shape[0]\n    return(y2_sum / N_total)\n\ndef DataSd(y, samples):\n    mean_y = DataMean(y, samples)\n    mean_y_sq = DataSqMean(y, samples)\n    return(np.sqrt(mean_y_sq - mean_y*mean_y))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-03T13:57:50.222424Z","iopub.execute_input":"2026-05-03T13:57:50.222836Z","iopub.status.idle":"2026-05-03T13:57:50.231169Z","shell.execute_reply.started":"2026-05-03T13:57:50.222800Z","shell.execute_reply":"2026-05-03T13:57:50.230136Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Randam y vidurkius train ir valid set'uose.","metadata":{}},{"cell_type":"code","source":"mu_train = DataMean(y, train_samples)\nmu_valid = DataMean(y, valid_samples)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-03T13:57:50.232478Z","iopub.execute_input":"2026-05-03T13:57:50.232788Z","iopub.status.idle":"2026-05-03T13:57:50.252393Z","shell.execute_reply.started":"2026-05-03T13:57:50.232755Z","shell.execute_reply":"2026-05-03T13:57:50.251350Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Pirmoji y prognozė - vidurkis. Nors valid duomenims taip pat reikėtų priskirti mu_train, mums įdomus tik koreliacijos koeficientas, o jis nepriklauso nuo priskirtos pradinės (vidurkinės) reikšmės. Pasirinkimas priskirti mu_valid padeda vertinti R-squared statistiką. Pavyzdžiui, tarkime kad turime $Y=\\left\\{y_n, n=1,\\dots,N\\right\\}$ prognozes $\\hat{Y}=\\left\\{\\hat{y}_n, n=1,\\dots,N\\right\\}$. Apibrėžkime residuals $r_n := y_n-\\hat{y}_n$. Tuomet R-squared statistika lygi $$R^2 = 1 - \\frac{\\sum_{n=1}^N r_n^2}{\\sum_{n=1}^N y_n^2}.$$ Jeigu prognozės $\\hat{y}_n$ idealios, tai $r_n\\equiv 0$ ir $R^2=1$. Galima parodyti, kad galioja lygybė $$|\\textbf{cor}(Y, \\hat{Y})| \\geq \\sqrt{\\max\\{0, R^2\\}}.$$","metadata":{}},{"cell_type":"code","source":"pred = [None] * S\nfor s in train_samples:\n    N_s = y[s].shape[0]\n    pred[s] = np.zeros(N_s) + mu_train\n\nfor s in valid_samples:\n    N_s = y[s].shape[0]\n    pred[s] = np.zeros(N_s) + mu_valid","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-03T13:57:50.255427Z","iopub.execute_input":"2026-05-03T13:57:50.255818Z","iopub.status.idle":"2026-05-03T13:57:50.271727Z","shell.execute_reply.started":"2026-05-03T13:57:50.255770Z","shell.execute_reply":"2026-05-03T13:57:50.270671Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Pagalbinės funkcijos skaičiuoti residual sum-of-squares (funkcijos ModelSS0, ModelSS) ir koreliacijos koeficientą (ModelCor) nurodytiems samples. Pavyzdžiui, ModelSS0 kaip input duomenis gauna $Y$, vidurkį $\\mu$ ir sample aibę $\\mathbf{S}:=S_1 \\cup \\dots \\cup S_M$. Funkcija grąžina $$\\sum_{n\\in \\mathbf{S}} (y_n-\\mu)^2.$$\nFunkcija ModelSS skaičiuoja $$\\sum_{n\\in \\mathbf{S}} (y_n-\\hat{y}_n)^2.$$","metadata":{}},{"cell_type":"code","source":"def ModelSS0(y, mu, samples):\n    SS0 = 0.0\n    for s in samples:\n        res = y[s] - mu\n        SS0 = SS0 + np.sum(np.square(res))\n    return SS0\n    \ndef ModelSS(y, pred, samples):\n    SS = 0.0\n    for s in samples:\n        res = y[s] - pred[s]\n        SS = SS + np.sum(np.square(res))\n    return SS\n\ndef ModelCor(y, pred, samples):\n    y_s = [None] * len(samples)\n    pred_s = [None] * len(samples)\n    for i in range(len(samples)):\n        s = samples[i]\n        y_s[i] = y[s]\n        pred_s[i] = pred[s]\n    y_s = np.concatenate(y_s)\n    pred_s = np.concatenate(pred_s)\n    sd_y = np.std(y_s)\n    sd_pred = np.std(pred_s)\n    return np.corrcoef(y_s, pred_s)[0,1]\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-03T13:57:50.273061Z","iopub.execute_input":"2026-05-03T13:57:50.273483Z","iopub.status.idle":"2026-05-03T13:57:50.287648Z","shell.execute_reply.started":"2026-05-03T13:57:50.273441Z","shell.execute_reply":"2026-05-03T13:57:50.286183Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Pagalbinės funkcijos skaičiuoti latent component koeficientus. Randamas veiktorius $\\beta\\in\\mathbb{R}^K$, kad latent komponentas, apibrėžiamas kaip $X\\beta$, gerai ,,prognozuotų`` residual vektorių $R:=Y-\\hat{Y}$.\n\n*PLSLatentComponent* nepasiteisino. Idėja pasiimta iš **PLS** (projection  to latent structures arba partial least squares algoritmo). Jeigu įdomu, latentinį vektorių ji skaičiuoja maždaug šitaip: turime duomenis (stulpelius) $X_1,\\dots,X_K$, kurių vidurkiai $\\mathbb{E}X_k=\\mu_k=0$ ir standartiniai nuokrypiai $\\sqrt{\\mathbb{D}X_k}=\\sigma_k=1$. Taip pat turime vektorių $Y$, kurį norim prognozuoti, ir dabartines prognozes $\\hat{Y}$, kurias norim pagerinti. Apibrėžkim residual vektorių $R=Y-\\hat{Y}$. Kadangi $\\mu_k=0$ ir $\\sigma_k=1$, tai $$w_k := \\textbf{cor}(X_k, R) = \\frac{\\mathbb{E}(X_kR)-\\mathbb{E}(R)\\mu_k}{\\sigma_k\\sqrt{\\mathbb{D}R}} \\propto \\mathbb{E}(X_kR) \\propto (X_k)^T R.$$ Taigi kodas apibrėžia latentinį vektorių $$Z\\propto\\sum_{k=1}^K w_k X_k.$$ Tuomet randama vektoriaus $R$ projekcija į vektorių $Z$, kuri lygi $$\\gamma Z, \\text{ čia } \\gamma = \\frac{Z^T R}{Z^T Z}.$$ Galiausiai grąžinamas $\\mathbb{R}^K$ vektorius $\\gamma w$, kur $w=(w_1,\\dots,w_K)$.\n\n*MaxAbsCovLatentComponent* gali atrodyti sudėtingai parašyta, tačiau viskas, ką ji daro, tai randa geriausiai su residual vektoriumi $R$ koreliuojantį stulpelį $X_{best}$, paskaičiuoja $R$ projekcijos į $X_{best}$ koeficientą $$\\gamma:=\\frac{X_{best}^TR}{X_{best}^TX_{best}}$$ ir grąžina vektorių $w\\in\\mathbb{R}^K$, kuris visur lygus $0$, išskyrus pozicijoje $best$, kur jis lygus $\\gamma$.","metadata":{}},{"cell_type":"code","source":"def PLSLatentComponent(y, X, pred, train_samples):\n    N_s = len(train_samples)\n    beta = np.zeros((N_s,K))\n    for i in range(N_s):\n        s = train_samples[i]\n        res_s = y[s] - pred[s]\n        w = (X[s].T).dot(res_s)\n        z = X[s].dot(w)\n        gamma = np.sum(z*res_s)/np.sum(z*z)\n        beta[i] = w * gamma\n    return np.median(beta, axis=0)\n\ndef MaxAbsCovLatentComponent(y, X, pred, train_samples):\n    cov = np.zeros(K)\n    N_s = len(train_samples)\n    for i in range(N_s):\n        s = train_samples[i]\n        res_s = y[s] - pred[s]\n        cov = cov + (X[s].T).dot(res_s)\n    w = np.zeros(K)\n    w[np.argmax(np.absolute(cov))] = 1\n    cov_rz = 0.0\n    var_z = 0.0\n    for i in range(N_s):\n        s = train_samples[i]\n        res_s = y[s] - pred[s]\n        z = X[s].dot(w)\n        cov_rz = cov_rz + np.sum(res_s*z)\n        var_z = var_z + np.sum(z*z)\n    gamma = cov_rz/var_z\n    return gamma*w","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-03T13:57:50.288895Z","iopub.execute_input":"2026-05-03T13:57:50.289280Z","iopub.status.idle":"2026-05-03T13:57:50.313380Z","shell.execute_reply.started":"2026-05-03T13:57:50.289246Z","shell.execute_reply":"2026-05-03T13:57:50.312246Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Modelio mokymas. ","metadata":{}},{"cell_type":"code","source":"L = 100 # iteracijų skaičius \nshr = 0.1 # parametras, kontroliuojantis mokymosi greitį\n\nalpha = np.zeros((L,K))\n\n# randam train ir valid y duomenų vidurkius\nmu_train = DataMean(y, train_samples)\nmu_valid = DataMean(y, valid_samples)\n\n# pradinės prognozės - vidurkiai\npred = [None] * S\nfor s in train_samples:\n    N_s = y[s].shape[0]\n    pred[s] = np.zeros(N_s) + mu_train\n\nfor s in valid_samples:\n    N_s = y[s].shape[0]\n    pred[s] = np.zeros(N_s) + mu_valid\n\n# pradiniai residual sum of squares, kai bazinis prediktorius - vidurkinė reikšmė\nSS_0_train = ModelSS0(y, mu_train, train_samples)\nSS_0_valid = ModelSS0(y, mu_valid, valid_samples)\n\n# seksime residual sum of squares kitimą kiekviename žingsnyje\nSS_train = np.zeros(L)\nSS_valid = np.zeros(L)\n# seksime koreliacijos koeficientą kiekviename žingsnyje\ncor_train = np.zeros(L)\ncor_valid = np.zeros(L)\n\nfor l in range(L):\n    print(\"iteration:\", l+1, \"/\", L)\n    \n    # randam latent komponentą, kuris prognozuoja residual y-pred\n    alpha[l] = MaxAbsCovLatentComponent(y, X, pred, train_samples)\n    #alpha[l] = PLSLatentComponent(y, X, pred, train_samples)\n\n    # priskiriame pred_i = pred_i+ shr*pred_res_i \n    for s in range(S):\n        pred_res = X[s].dot(alpha[l])\n        pred[s] = pred[s] + shr*pred_res\n    \n    # išsisaugome iteracijos residual-sum-of-squares ir cor vertes\n    SS_train[l] = ModelSS(y, pred, train_samples)\n    SS_valid[l] = ModelSS(y, pred, valid_samples)\n    cor_train[l] = ModelCor(y, pred, train_samples)\n    cor_valid[l] = ModelCor(y, pred, valid_samples)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-03T13:57:50.314591Z","iopub.execute_input":"2026-05-03T13:57:50.314969Z","iopub.status.idle":"2026-05-03T13:58:13.812425Z","shell.execute_reply.started":"2026-05-03T13:57:50.314920Z","shell.execute_reply":"2026-05-03T13:58:13.811569Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Piešiame $\\sqrt{R^2}$ statistikas train (mėlyna) ir valid (oranžinė) duomenims. Taip pat pilkai nupiešti kiekvienos iteracijos koreliacijos koeficientai. Visada turi būti $\\textbf{cor}_l \\geq \\sqrt{R^2_l}$ (tiek train, tiek valid set'ams).","metadata":{}},{"cell_type":"code","source":"R2_train = 1-SS_train/SS_0_train\nR2_valid = 1-SS_valid/SS_0_valid\n\nx = range(1,L+1)\nplt.ylim(bottom=0)\nplt.ylim(top=0.2)\nplt.scatter(x, cor_train, color='lightgray')\nplt.plot(x, np.sqrt(np.maximum(R2_train,0)))\nplt.scatter(x, cor_valid, color='lightgray')\nplt.plot(x, np.sqrt(np.maximum(R2_valid,0)))\nplt.show()\n\nprint(\"DEBUG\")\ncor_lower_bound = np.sqrt(np.maximum(R2_train,0))\nprint(np.min(cor_train-cor_lower_bound)>0)\ncor_lower_bound = np.sqrt(np.maximum(R2_valid,0))\nprint(np.min(cor_valid-cor_lower_bound)>0)\nprint(np.max(cor_lower_bound))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-03T13:58:13.813351Z","iopub.execute_input":"2026-05-03T13:58:13.813667Z","iopub.status.idle":"2026-05-03T13:58:13.976166Z","shell.execute_reply.started":"2026-05-03T13:58:13.813634Z","shell.execute_reply":"2026-05-03T13:58:13.975140Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# randame indeksą kur valid set'ui gaunama geriausia prognozė\nbest_idx = np.argmax(R2_valid)\n\n# randam prognozės hat{y} = beta0 + np.sum(beta*X) koeficientus\nalpha_best = alpha[:][:(best_idx+1)]\nbeta = shr*np.sum(alpha_best, axis=0)\nbeta = beta/X_sd\nbeta0 = mu_train - np.sum(X_mu*beta)\n\n# DEBUG: įvairūs tikrinimai, ar neįvėliau klaidų (pradžioj buvau įvėlęs...)\nprint(\"DEBUG\")\npred = [None] * S\nfor s in range(S):\n    N_s = y[s].shape[0]\n    pred[s] = (X[s]*X_sd+X_mu).dot(beta) + beta0\n\nprint(ModelCor(y, pred, train_samples))\nprint(cor_train[best_idx])\nprint(ModelCor(y, pred, valid_samples))\nprint(cor_valid[best_idx])\nprint(np.sqrt(1-(ModelSS(y, pred, train_samples)/ModelSS0(y, mu_train, train_samples))))\nprint(np.sqrt(R2_train[best_idx]))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-03T13:58:13.977353Z","iopub.execute_input":"2026-05-03T13:58:13.977750Z","iopub.status.idle":"2026-05-03T13:58:16.365756Z","shell.execute_reply.started":"2026-05-03T13:58:13.977718Z","shell.execute_reply":"2026-05-03T13:58:16.365070Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Atliekam submission.","metadata":{}},{"cell_type":"code","source":"print(\"loading test parquet...\")\ndf = pd.read_parquet('/kaggle/input/competitions/drw-crypto-market-prediction/test.parquet', engine='pyarrow')\n\nprint(\"converting to numpy...\")\ndf = df.drop('label', axis=1)\nX_test = df.to_numpy()\n\ndel df\n\nprint(\"predicting...\")\npred_test = X_test.dot(beta) + beta0\n\nplt.hist(pred_test, bins='auto')\nplt.title(\"Histogram of preds\")\nplt.show()\n\nID = np.array(range(len(pred_test)))+1\ndf = pd.DataFrame({'ID':ID, 'prediction':pred_test})\ndf.to_csv(\"/kaggle/working/submission.csv\", index=False)\nprint(\"completed.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-03T13:58:16.366641Z","iopub.execute_input":"2026-05-03T13:58:16.366936Z","iopub.status.idle":"2026-05-03T13:58:26.210056Z","shell.execute_reply.started":"2026-05-03T13:58:16.366894Z","shell.execute_reply":"2026-05-03T13:58:26.208829Z"}},"outputs":[],"execution_count":null}]}