{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Introduction:\n\nThis last competition tested most of the kagglers in terms of data engineering skills, which proved to be the worst obstacle in terms of calculation performance, and also in terms of data signal extrapolation, which is important to understand what should or should not be done in data preprocessing.\n\n# Objective:\n\nProvide the less experienced with a detailed analysis (I hope) of the AMEX data preprocessing that was used for this competition. The data engineering was more or less extrapolated from the GM Kagglers, but I have also added my own function.\n\n# What I did:\n\n* I loaded the very useful database in parquet format from [Raddar](https://www.kaggle.com/datasets/raddar/amex-data-integer-dtypes-parquet-format).\n\n* I explained step by step the preprocessing of data on a DEMO dataset. Skip it if you're not interested.\n\n* I created the custom functions. Some come from the notebook of [MARTIN KOVACEVIC BUVINIC](https://www.kaggle.com/code/ragnar123/amex-lgbm-dart-cv-0-7977#Comments:). Compared to the latter I added a function to remove noise from the data and a loop to remove categorical columns with only ONE value.\n\n* I preprocessed not all the columns (about 190) but separately according to the type of variables: payment variables, risk variables, balance variables, delinquency variables, and spend variables. In total, we have 5 preprocessing functions. I did this because running preprocessing on the entire dataset would have crashed the kernel due to RAM limitations. In addition, to speed up some calculations, I used **cuDF** (which works on GPU).\n\n* I saved the files on disk in parquet format. These can be used to experiment with different models.\n\n# Next experiments:\n\n* Applying preprocessing on data grouped by month.","metadata":{}},{"cell_type":"markdown","source":"# Importing Libraries","metadata":{}},{"cell_type":"code","source":"import gc\nimport numpy as np\nimport pandas as pd\nfrom tqdm.auto import tqdm\nimport cudf\nimport time\n\n\nimport warnings \nwarnings.filterwarnings('ignore')","metadata":{"execution":{"iopub.status.busy":"2022-09-08T21:09:17.829220Z","iopub.execute_input":"2022-09-08T21:09:17.830796Z","iopub.status.idle":"2022-09-08T21:09:22.145885Z","shell.execute_reply.started":"2022-09-08T21:09:17.830687Z","shell.execute_reply":"2022-09-08T21:09:22.144838Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Loding demo dataset","metadata":{}},{"cell_type":"code","source":"start = time.time()\ndemo_df = cudf.read_parquet('../input/amex-data-integer-dtypes-parquet-format/train.parquet', columns = [\"customer_ID\",\"P_2\",\"P_3\",\"B_30\",\"B_38\"])\nprint(\"no. of rows in the demo_df\", demo_df.shape[0])\nprint(\"no. of columns in the demo_df\", demo_df.shape[1])\nend = time.time()\nprint(\"GPU time= \", end-start)","metadata":{"execution":{"iopub.status.busy":"2022-09-08T21:09:30.614710Z","iopub.execute_input":"2022-09-08T21:09:30.615959Z","iopub.status.idle":"2022-09-08T21:09:34.599711Z","shell.execute_reply.started":"2022-09-08T21:09:30.615914Z","shell.execute_reply":"2022-09-08T21:09:34.597908Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**cuDF data type supported**\n\n![image.png](attachment:6a81436f-06bd-4c80-b81f-705af9e435d6.png)\n\nsource: [RAPIDS](https://docs.rapids.ai/api/cudf/stable/user_guide/data-types.html)","metadata":{},"attachments":{"6a81436f-06bd-4c80-b81f-705af9e435d6.png":{"image/png":"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7Tj6XjtG4xflnNrN/Jb4SHpHn9OMzf71Pcw4r8X6Hmac+zC8ueuBHYQh3ej9GrrSdVeo72HG8UVz89MCREREdl7JxsbGRuSHn/F99OFel0Ukq/1wnS5aj3tr6J1J/iMZ0Uk6z/QTSAqmBbDXc350Cy0e9iOsNj8nWUREZCcVt0ZVRApn/9WoLM3+22bX2ub9FAzij4tr6G1XSBURkV2loCriAovDRvyPVbSNbLGryhaZj2yzn62aR9n+1U+ANi647c8gi4jIW09N//LG0HUqIiKyv6hGVURERERcSUFVRERERFxJQVVEREREXElBVURERERcSUFVRERERFxJQVVEREREXElBVURERERcSUFVRERERFxJQVVEREREXElBVURERERcSUFVRERERFxJQVVEREREXKlkY2NjI/LDz3tdDhERERGRJKpRFRERERFXKrX/4/vow70sh0hOkR9+1nUqIiKyj6hGVURERERcSUFVRERERFxJQVVEREREXElBVURERERcSUFVRERERFxJQVVEREREXElBVURERERcSUFVRERERFxJQVVEREREXElBVURERERcSUFVRERERFxJQVVEREREXElBVURERERcSUFVRERERFxJQVVEREREXGlbQTU21YlhGI6fTiajjhmik3SmvrbrFhk0DAZD6afGpjoxLk8S28IaY6HFLc0vIiIiIltXWuiCi8MG/m97mAkO4bFfDA1inDGIjATpqgW8rQwFW4tS0J3iaRki2JL//LGpTpofNDEzXLNzhRIRERGRQmtUF5m9DW3+1kRIBajtYvQ8jP9nsRhlEyme8ATLjSM8XUkzbeUh0cYBotO7WPWfrTw5RXl6ZYDlm+G0U9duDrDcOMHatgpYPGs3B1i+8pD1gpbeR/tqXYextLtqHYeCj2OxZT8vIiLFsq2m/8jS5gbwmstBgpet2sZNTf9mM7zdVaBzajKpWd5uhl9M6lIwSHLsjTF52cgyPXUbkaz7kNz0b3cTSF1HLD5vc18A5vtpdu5XdJJOZxeI4ZSgnjJ9cCpNl4jQYFI3Cnub8TIOT8b32zlN8vTB+5QUeZXr0yMZPqitD/FG+ydNIN1Webwc/LTghTMwy5w+JFn72pgpKCXvb+o6Sj8u30a5dmJfw8QyfEkwg2+mfUnez3RfbLa1rxVeDha+dAaZ99WcPJHY3zTn1j7vm6+L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he5rbl6OtlexfCl1gJGzf6q1L47ztvZvc/DWq/hybB60ltb29jX5+FuD91IGVWWWaV+9HP2/tfF+1GBe97vzBAMRkfyVbGxsbER++BnfRx/uweYXGTRu4bs35Bg1vw9EJ+k8E+FCzkd6idPeXae7aZHBYegqJKi+aaKTDH59nK5CguqbZj/tq4hIEW3rgf9bEhrESHnI/eKwn/H6Jo6/xSF1c39eqzvC+SaFVNksNEvkk/3xfIfY17OQ9xM23mz7aV9FRIqpqKP+s6rtYqY3c/P228rTMsToYyPxnFmA8wU27cpbL7YETX99m98RCU8e+2hq2etS7I79tK8iIsW0x03/IvnTdSoiIrK/7F7Tv4iIiIjIFiioioiIiIgrKaiKiIiIiCspqIqIiIiIKymoioiIiIgrKaiKiIiIiCspqIqIiIiIKymoioiIiIgrKaiKiIiIiCspqIqIiIiIKymoioiIiIgrKaiKiIiIiCspqIqIiIiIK5VsbGxsRH74ea/LISIiIiKSpNT+j++jD/eyHCI5RX74WdepiIjIPqKmfxERERFxJQVVEREREXElBVURERERcSUFVRERERFxJQVVEREREXElBVURERERcSUFVRERERFxJQVVEREREXElBVURERERcSUFVRERERFxJQVVEREREXElBVURERERcSUFVRERERFxJQVVEREREXElBVURERERcaUCg+oig4bBYCjdtBiTlw06p2LbKVdibVOdGJcnKc7aChSdpNPoZDKafvLisIExvLiFFcZYDO3pHomIiIi4XuleFyAXT8sQwZa9LkV2NZeDBPOeO8bk5WZmT84wVLuDhRIRERF5w6npX/aH8ATLjSM8XdnqglGeXhlg+cpD1neiXFtmledmuLDFdRxEROQNsuNB1W66X5zqxDAM62eQpIby0KBjWnIz+qam/+gknY55B6eczfJ2lwTzX3ueTd0QUtaxqdk+dRtfZ9/HpKZ/u5tAKGUdIbBrU/vnIdDXnLxfKccgtcyxlOM3ualLhNnlItMxXhw26JyajB+X9N023mIfvE/Jbm4vPJEh1FkBq9H+SQmNKw+JZpoGgJeDn26jXLt9HAgTyxKM16dHEsciTei0p8c2TdrmcRARkTfC7jT9z/dz6+QMwaAHO6z5h5sIXq4xg11HhJ57QVq9YIZNP4N/C9K1qWl8kcEz/dA7Q7DF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iIiIJCm1/+P76MO9LIdITpEfftZ1KiIiso+o6V9EREREXElBVURERERcSUFVRERERFxJQVVEREREXElBVURERERcSUFVRERERFxJQVVEREREXElBVURERERcSUFVRERERFxJQVVEREREXElBVURERERcSUFVRERERFxJQVVEREREXElBVURERERcSUFVRERERFypwKC6yKBhYGT4GQwl5jH/XywxFkOxpDIUd/07IDpJp9HJZHQLy4QWWdyxAomIiIi8GUq3s3DbSJCu2mIVJZcYk5ebmT05w1AtQA1dweBubbxw3laGgq35zx8axOiA0WDNzpVJRERE5A2wraAq8sYIT7B8aZVDdzs4WrF58trNAZ7fsX45d4rKi9XWMkvJr7nc2s0Bnn9Xy3vXT3BkqwuvPCR6NsTBG9140u2q83hQxeG5LyizltkAqCtwu7stx7UgIiLusbtBNTSI0TEe/7Whd4ahFk/898VhA/9t5wIN9NwbotVr1qb2zwPzzRgPepgZ9jFm+MGq1Y1NddL8oInRk7P4+wLW8m2MBruI101GJ+k80098am8Pkb5Zmu4N0epNV+BFBg0/9PYQ6XMsl1qTnLJezo8SvFzjmJbYxuKwgZ9RRvEn9rW+h5nhVjyO4+M3xne5xvot98H7lLCaflp4wgx3cykhq/oLKudgfXqEZ98XszBhYo0LHEgTlJICM1CaFBqjPL0yxosF87eSq+14TydfuKUfl8N3BRarwsvBbGW+tMqhu93JZa44gXfuhBn+xgrcblrmvv7RniE0EybWeJ9XAJSnCZ3W9HThOdu1ICIirrJrQTU21UlzH/TcC5qhMDpJ55lmOjHDamyqE/+3PcwEWzGjqxVO/znJ8eFWWodnwG76b/FAul6c8/3cOjlDMOiJL+8fbrJC4yKDZ/qhd4agtfyg4SdAA005yj7eN0vPvSBDXqyw3YnPDrdWuGwbCVpdEsz1GjjCaqrbfmZHggQvJ+bvmzrOUEsXwRGspn9HwJYdtf7jKnz6F1fUBJZd7KbsovVLeILlsYesW0FrfXqKl5+3U3nda9V+TvHU2KVawZUorynnHTfUQDpqfivThtgoT6/ch3NVhYd2ERFxhW0F1fEOg/GU11JrSU0xHj0I0NA7k6i59LbS2ztL84NHxFpa8bQMEWxxLuPh+MkGeLCVErVxIb5te/kIMWpg6hbj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"}}},{"cell_type":"markdown","source":"# DATA PREPROCESSING (demo dataset)","metadata":{}},{"cell_type":"markdown","source":"In the [RADDAR](https://www.kaggle.com/datasets/raddar/amex-data-integer-dtypes-parquet-format) dataset *int* columns do not support NA values, so NA was converted with -1","metadata":{}},{"cell_type":"markdown","source":"**(1) Select only 1000 rows and read data information.**","metadata":{}},{"cell_type":"code","source":"demo_df = demo_df.loc[0:1000,:]\nprint(demo_df.info(memory_usage='deep'))","metadata":{"execution":{"iopub.status.busy":"2022-09-08T21:09:35.830716Z","iopub.execute_input":"2022-09-08T21:09:35.831072Z","iopub.status.idle":"2022-09-08T21:09:35.865714Z","shell.execute_reply.started":"2022-09-08T21:09:35.831043Z","shell.execute_reply":"2022-09-08T21:09:35.864825Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"int8: nteger type that uses 1 byte per data element and can store values in the range -127 .. 127. Most arithmetic operations involving this type will produce a result of type int32, which follows the convention of the C language.","metadata":{}},{"cell_type":"markdown","source":"**(2) Select categorical and numerical features.**","metadata":{}},{"cell_type":"code","source":"features = demo_df.drop(\"customer_ID\",axis = 1).columns.to_list()\ncat_features = [\"B_30\",\"B_38\"]\nnum_features = [col for col in features if col not in cat_features] #extracts only column names","metadata":{"execution":{"iopub.status.busy":"2022-09-08T21:09:39.308294Z","iopub.execute_input":"2022-09-08T21:09:39.309351Z","iopub.status.idle":"2022-09-08T21:09:39.317314Z","shell.execute_reply.started":"2022-09-08T21:09:39.309308Z","shell.execute_reply":"2022-09-08T21:09:39.316268Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**(3) Aggregation using one or more operations on the specified axis of the NUMERICAL FEATURES. This step is useful for adding new features to our dataset, so that we can add some information to our model.**\n\nFor this operation we will use **cuDF**\n\n**NOTE:** Setting **sort=True** will produce Pandas-like output, but with some performance penalty:","metadata":{}},{"cell_type":"code","source":"# Adding functions of interest (numerical features)\nnum_function = ['mean', 'std', 'min', 'max', 'last']\ndemo_df_num_agg = demo_df.groupby(\"customer_ID\", sort = True)[num_features].agg(num_function)\ndemo_df_num_agg.head()","metadata":{"execution":{"iopub.status.busy":"2022-09-08T21:09:54.683002Z","iopub.execute_input":"2022-09-08T21:09:54.683375Z","iopub.status.idle":"2022-09-08T21:09:54.786433Z","shell.execute_reply.started":"2022-09-08T21:09:54.683343Z","shell.execute_reply":"2022-09-08T21:09:54.785496Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**(4) Join the name of the main column (P_2 and P_3) to the respective names of the measures (mean, std etc.)**","metadata":{}},{"cell_type":"code","source":"demo_df_num_agg.columns = ['_'.join(x) for x in demo_df_num_agg.columns]\ndemo_df_num_agg.reset_index(inplace = True)\ndemo_df_num_agg.head()","metadata":{"execution":{"iopub.status.busy":"2022-09-08T21:10:03.212301Z","iopub.execute_input":"2022-09-08T21:10:03.214750Z","iopub.status.idle":"2022-09-08T21:10:03.251958Z","shell.execute_reply.started":"2022-09-08T21:10:03.214716Z","shell.execute_reply":"2022-09-08T21:10:03.251050Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**(5) Aggregation using one or more operations on the specified axis of the CATEGORICAL FEATURES. This step is useful for adding new features to our dataset, so that we can add some information to our model**\n\nFor this operation we will use **cuDF**\n\n**NOTE:** Setting **sort=True** will produce Pandas-like output, but with some performance penalty:","metadata":{}},{"cell_type":"code","source":"# Adding functions of interest (categorical features)\ncat_function = ['count', 'last', 'nunique']\ndemo_df_cat_agg = demo_df.groupby(\"customer_ID\", sort = True)[cat_features].agg(cat_function)\ndemo_df_cat_agg.head()","metadata":{"execution":{"iopub.status.busy":"2022-09-08T21:10:07.584726Z","iopub.execute_input":"2022-09-08T21:10:07.585093Z","iopub.status.idle":"2022-09-08T21:10:07.637193Z","shell.execute_reply.started":"2022-09-08T21:10:07.585064Z","shell.execute_reply":"2022-09-08T21:10:07.636245Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**(6) Join the name of the main column (B_30 and B_38) to the respective names of the measures (count, last etc.)**","metadata":{}},{"cell_type":"code","source":"demo_df_cat_agg.columns = ['_'.join(x) for x in demo_df_cat_agg.columns]\ndemo_df_cat_agg.reset_index(inplace = True)\ndemo_df_cat_agg.head()","metadata":{"execution":{"iopub.status.busy":"2022-09-08T21:10:11.398578Z","iopub.execute_input":"2022-09-08T21:10:11.399021Z","iopub.status.idle":"2022-09-08T21:10:11.434485Z","shell.execute_reply.started":"2022-09-08T21:10:11.398988Z","shell.execute_reply":"2022-09-08T21:10:11.433272Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Remove columns with ONE single value as it would be useless for the model training**","metadata":{}},{"cell_type":"code","source":"# Remove columns with ONE single value\nfor col in demo_df_cat_agg.drop(\"customer_ID\", axis = 1).columns:\n    if len(demo_df_cat_agg[col].unique()) == 1:\n        demo_df_cat_agg.drop(col,inplace=True,axis=1)\n        \ndemo_df_cat_agg.head()        ","metadata":{"execution":{"iopub.status.busy":"2022-09-08T21:10:14.613027Z","iopub.execute_input":"2022-09-08T21:10:14.613995Z","iopub.status.idle":"2022-09-08T21:10:14.653632Z","shell.execute_reply.started":"2022-09-08T21:10:14.613957Z","shell.execute_reply":"2022-09-08T21:10:14.652283Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**(7) Convert float64 columns to float32, and int64 or int32 to int16. Usually some data types remain unchanged when we perform the calculations, but other times this does not happen. However, tqdm is a library in Python which is used for creating Progress Meters or Progress Bars** ","metadata":{}},{"cell_type":"code","source":"print(demo_df_num_agg.info(memory_usage='deep'))\nprint(demo_df_cat_agg.info(memory_usage='deep'))","metadata":{"execution":{"iopub.status.busy":"2022-09-08T21:10:18.109216Z","iopub.execute_input":"2022-09-08T21:10:18.109570Z","iopub.status.idle":"2022-09-08T21:10:18.131502Z","shell.execute_reply.started":"2022-09-08T21:10:18.109538Z","shell.execute_reply":"2022-09-08T21:10:18.130428Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**TABLE:**\n\n![image.png](attachment:041557e7-8edf-4ecd-bcc3-e10a7e31a9ef.png)","metadata":{},"attachments":{"041557e7-8edf-4ecd-bcc3-e10a7e31a9ef.png":{"image/png":"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"}}},{"cell_type":"markdown","source":"To free up RAM, we convert int32 values to int8 values; however, remember that we must be careful in these procedures, especially when converting float values","metadata":{}},{"cell_type":"code","source":"# Transform float64 columns to float32 \ncols = list(demo_df_num_agg.dtypes[demo_df_num_agg.dtypes == 'float64'].index)\nfor col in tqdm(cols):\n        demo_df_num_agg[col] = demo_df_num_agg[col].astype(np.float32)\n# Transform int32 or int8 columns to int16\ncols = list(demo_df_cat_agg.dtypes[(demo_df_cat_agg.dtypes == 'int64') | (demo_df_cat_agg.dtypes == 'int8')].index)\nfor col in tqdm(cols):\n        demo_df_cat_agg[col] = demo_df_cat_agg[col].astype(np.int16)","metadata":{"execution":{"iopub.status.busy":"2022-09-08T21:10:24.241506Z","iopub.execute_input":"2022-09-08T21:10:24.242188Z","iopub.status.idle":"2022-09-08T21:10:24.327066Z","shell.execute_reply.started":"2022-09-08T21:10:24.242151Z","shell.execute_reply":"2022-09-08T21:10:24.325967Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(demo_df_num_agg.info(memory_usage='deep'))\nprint(demo_df_cat_agg.info(memory_usage='deep'))","metadata":{"execution":{"iopub.status.busy":"2022-09-08T21:10:28.285483Z","iopub.execute_input":"2022-09-08T21:10:28.286553Z","iopub.status.idle":"2022-09-08T21:10:28.305542Z","shell.execute_reply.started":"2022-09-08T21:10:28.286505Z","shell.execute_reply":"2022-09-08T21:10:28.304493Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**(8) Get the difference. The source of the following code comes from the notebook of [MARTIN KOVACEVIC BUVINIC](https://www.kaggle.com/code/ragnar123/amex-lgbm-dart-cv-0-7977). Apologies in advance if I have not referenced other kagglers.**\n\ncuDF's *diff* function with respect to pandas does not handle NaN values during this operation. We have no intention of filling in these values before proceeding, so we transform the cuDF dataset back to pandas","metadata":{}},{"cell_type":"code","source":"demo_df = demo_df.to_pandas()\ndf1 = []\ncustomer_ids = []\nfor customer_id, df in tqdm(demo_df.groupby(['customer_ID'], sort = True)):\n        # Get the differences\n        diff_df1 = df[num_features].diff(1).iloc[[-1]].values.astype(np.float32)\n        # Append to lists\n        df1.append(diff_df1)\n        customer_ids.append(customer_id)","metadata":{"execution":{"iopub.status.busy":"2022-09-08T21:10:32.663645Z","iopub.execute_input":"2022-09-08T21:10:32.664682Z","iopub.status.idle":"2022-09-08T21:10:32.785410Z","shell.execute_reply.started":"2022-09-08T21:10:32.664619Z","shell.execute_reply":"2022-09-08T21:10:32.784160Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Concatenate\ndf1 = np.concatenate(df1, axis = 0)\n# Transform to dataframe\ndf1 = pd.DataFrame(df1, columns = [col + '_diff1' for col in df[num_features].columns])\n# Add customer id\ndf1['customer_ID'] = customer_ids\n# Transform back into cuDF\ndf1 = cudf.DataFrame(df1)\ndf1.head()","metadata":{"execution":{"iopub.status.busy":"2022-09-08T21:10:37.326710Z","iopub.execute_input":"2022-09-08T21:10:37.327409Z","iopub.status.idle":"2022-09-08T21:10:37.355605Z","shell.execute_reply.started":"2022-09-08T21:10:37.327373Z","shell.execute_reply":"2022-09-08T21:10:37.354517Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**(9) Read the csv file with the labels and merge (inner) dataframe demo_df_num_agg with demo_df_cat_agg on customer_ID, then merge with df1 on customer_ID, and finally merge with demo_df_labels on customer_ID.**","metadata":{}},{"cell_type":"code","source":"# Read csv file with labels\ndemo_df_labels = cudf.read_csv('../input/amex-default-prediction/train_labels.csv')\ndemo_df_labels = demo_df_labels.loc[0:1000,:]\n# Merge all produced dataframes \ndemo_df = demo_df_num_agg.merge(demo_df_cat_agg, how = 'inner', on = 'customer_ID').merge(df1, how = 'inner', on = 'customer_ID').merge(demo_df_labels, how = 'inner', on = 'customer_ID')\ndemo_df.head()","metadata":{"execution":{"iopub.status.busy":"2022-09-08T21:10:43.365947Z","iopub.execute_input":"2022-09-08T21:10:43.366646Z","iopub.status.idle":"2022-09-08T21:10:44.124649Z","shell.execute_reply.started":"2022-09-08T21:10:43.366610Z","shell.execute_reply":"2022-09-08T21:10:44.123684Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**GOOD WORK. Now let's summarise the whole preprocessing with 2 custom functions to be applied to the full dataset**","metadata":{}},{"cell_type":"markdown","source":"Clean the disk from datasets, as they are loaded directly by the function","metadata":{}},{"cell_type":"code","source":"del [demo_df, features, cat_features, num_features, demo_df_num_agg, \n     demo_df_cat_agg, num_function, cat_function, cols, diff_df1, df1, customer_ids,\n     demo_df_labels]\n\n\ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2022-09-08T21:10:49.113349Z","iopub.execute_input":"2022-09-08T21:10:49.113913Z","iopub.status.idle":"2022-09-08T21:10:49.259581Z","shell.execute_reply.started":"2022-09-08T21:10:49.113879Z","shell.execute_reply":"2022-09-08T21:10:49.258252Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# DATA PREPROCESSING (Full dataset: train and test)","metadata":{}},{"cell_type":"markdown","source":"**Function customisation**","metadata":{}},{"cell_type":"code","source":"# Get the difference\ndef get_difference(data, num_features):\n    data = data.to_pandas()\n    df1 = []\n    customer_ids = []\n    for customer_id, df in tqdm(data.groupby(['customer_ID'])):\n        # Get the differences\n        diff_df1 = df[num_features].diff(1).iloc[[-1]].values.astype(np.float32)\n        # Append to lists\n        df1.append(diff_df1)\n        customer_ids.append(customer_id)\n    # Concatenate\n    df1 = np.concatenate(df1, axis = 0)\n    # Transform to dataframe\n    df1 = pd.DataFrame(df1, columns = [col + '_diff1' for col in df[num_features].columns])\n    # Add customer id\n    df1['customer_ID'] = customer_ids\n    # Transform back into cuDF\n    df1 = cudf.DataFrame(df1)\n    return df1","metadata":{"execution":{"iopub.status.busy":"2022-09-06T21:58:49.681425Z","iopub.execute_input":"2022-09-06T21:58:49.682302Z","iopub.status.idle":"2022-09-06T21:58:49.711524Z","shell.execute_reply.started":"2022-09-06T21:58:49.682190Z","shell.execute_reply":"2022-09-06T21:58:49.710645Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**PAYMENT VARIABLES**","metadata":{}},{"cell_type":"code","source":"def read_preprocess_payment_data():\n    full_train = cudf.read_parquet('../input/amex-data-integer-dtypes-parquet-format/train.parquet')\n    payment_vars = [col for col in full_train.columns if col.startswith(\"P_\")]\n    cid_time = [\"customer_ID\",\"S_2\"]\n    payment_vars.extend(cid_time)\n    del full_train\n    gc.collect()\n    train = cudf.read_parquet('../input/amex-data-integer-dtypes-parquet-format/train.parquet', columns = list(payment_vars))\n    num_features = train.drop([\"customer_ID\", \"S_2\"], axis = 1).columns.to_list()\n    print('Training set: preprocessing...')\n    # Denoise train (only number) with np.floor\n    train[num_features] = cudf.DataFrame({col: np.floor(train[col]*100)/100 for col in train[num_features].columns})\n    # Adding functions of interest (numerical features)\n    num_function = ['mean', 'std', 'min', 'max','last']\n    train_num_agg = train.groupby(\"customer_ID\", sort = True)[num_features].agg(num_function)\n    # Join the name of columns\n    train_num_agg.columns = ['_'.join(x) for x in train_num_agg.columns]\n    train_num_agg.reset_index(inplace = True)\n    # Transform float64 columns to float32 \n    cols = list(train_num_agg.dtypes[(train_num_agg.dtypes == 'float64') | (train_num_agg.dtypes == 'float16')].index)\n    for col in tqdm(cols):\n        train_num_agg[col] = train_num_agg[col].astype(np.float32)\n    # Get the difference\n    train_diff = get_difference(train, num_features)\n    # Read csv file with labels\n    train_labels = cudf.read_csv('../input/amex-default-prediction/train_labels.csv')\n    # Merge all produced dataframes \n    train = train_num_agg.merge(train_diff, how = 'inner', on = 'customer_ID').merge(train_labels, how = 'inner', on = 'customer_ID')\n    del train_num_agg, train_diff\n    gc.collect()\n    # Save files to disk\n    train = train.to_pandas()\n    train.to_parquet('train_payment_fe.parquet')\n    del train\n    gc.collect()\n    test = cudf.read_parquet('../input/amex-data-integer-dtypes-parquet-format/test.parquet', columns = list(payment_vars))\n    print('Test set: preprocessing...')\n    # Denoise test (only number) with np.floor\n    test[num_features] = cudf.DataFrame({col: np.floor(test[col]*100)/100 for col in test[num_features].columns})\n    # Adding functions of interest (numerical features)\n    num_function = ['mean', 'std', 'min', 'max','last']\n    test_num_agg = test.groupby(\"customer_ID\", sort = True)[num_features].agg(num_function)\n    # Join the name of columns\n    test_num_agg.columns = ['_'.join(x) for x in test_num_agg.columns]\n    test_num_agg.reset_index(inplace = True)\n    # Transform float64 or float16 columns to float32 \n    cols = list(test_num_agg.dtypes[(test_num_agg.dtypes == 'float64') | (test_num_agg.dtypes == 'float16')].index)\n    for col in tqdm(cols):\n        test_num_agg[col] = test_num_agg[col].astype(np.float32)\n    # Get the difference\n    test_diff = get_difference(test, num_features)\n    # Merge all produced dataframes \n    test = test_num_agg.merge(test_diff, how = 'inner', on = 'customer_ID')\n    del test_num_agg, test_diff\n    gc.collect()\n    # Save files to disk\n    test = test.to_pandas()\n    test.to_parquet('test_payment_fe.parquet')\n    del test\n    gc.collect()\n    \nread_preprocess_payment_data()    ","metadata":{"execution":{"iopub.status.busy":"2022-09-06T17:11:37.401904Z","iopub.execute_input":"2022-09-06T17:11:37.402890Z","iopub.status.idle":"2022-09-06T17:32:39.432080Z","shell.execute_reply.started":"2022-09-06T17:11:37.402851Z","shell.execute_reply":"2022-09-06T17:32:39.430581Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**RISK VARIABLES**","metadata":{}},{"cell_type":"code","source":"def read_preprocess_risk_data():\n    full_train = cudf.read_parquet('../input/amex-data-integer-dtypes-parquet-format/train.parquet')\n    risk_vars = [col for col in full_train.columns if col.startswith(\"R_\")]\n    cid_time = [\"customer_ID\",\"S_2\"]\n    risk_vars.extend(cid_time)\n    del full_train\n    gc.collect()\n    train = cudf.read_parquet('../input/amex-data-integer-dtypes-parquet-format/train.parquet', columns = list(risk_vars))\n    num_features = train.drop([\"customer_ID\", \"S_2\"], axis = 1).columns.to_list()\n    print('Training set: preprocessing...')\n    # Denoise train (only number) with np.floor\n    train[num_features] = cudf.DataFrame({col: np.floor(train[col]*100)/100 for col in train[num_features].columns})\n    # Adding functions of interest (numerical features)\n    num_function = ['mean', 'std', 'min', 'max','last']\n    train_num_agg = train.groupby(\"customer_ID\", sort = True)[num_features].agg(num_function)\n    # Join the name of columns\n    train_num_agg.columns = ['_'.join(x) for x in train_num_agg.columns]\n    train_num_agg.reset_index(inplace = True)\n    # Transform float64 columns to float32 \n    cols = list(train_num_agg.dtypes[(train_num_agg.dtypes == 'float64') | (train_num_agg.dtypes == 'float16')].index)\n    for col in tqdm(cols):\n        train_num_agg[col] = train_num_agg[col].astype(np.float32)\n    # Get the difference\n    train_diff = get_difference(train, num_features)\n    # Read csv file with labels\n    train_labels = cudf.read_csv('../input/amex-default-prediction/train_labels.csv')\n    # Merge all produced dataframes \n    train = train_num_agg.merge(train_diff, how = 'inner', on = 'customer_ID').merge(train_labels, how = 'inner', on = 'customer_ID')\n    del train_num_agg, train_diff\n    gc.collect()\n    # Save files to disk\n    train = train.to_pandas()\n    train.to_parquet('train_risk_fe.parquet')\n    del train\n    gc.collect()\n    test = cudf.read_parquet('../input/amex-data-integer-dtypes-parquet-format/test.parquet', columns = list(risk_vars))\n    print('Test set: preprocessing...')\n    # Denoise test (only number) with np.floor\n    test[num_features] = cudf.DataFrame({col: np.floor(test[col]*100)/100 for col in test[num_features].columns})\n    # Adding functions of interest (numerical features)\n    num_function = ['mean', 'std', 'min', 'max','last']\n    test_num_agg = test.groupby(\"customer_ID\", sort = True)[num_features].agg(num_function)\n    # Join the name of columns\n    test_num_agg.columns = ['_'.join(x) for x in test_num_agg.columns]\n    test_num_agg.reset_index(inplace = True)\n    # Transform float64 or float16 columns to float32 \n    cols = list(test_num_agg.dtypes[(test_num_agg.dtypes == 'float64') | (test_num_agg.dtypes == 'float16')].index)\n    for col in tqdm(cols):\n        test_num_agg[col] = test_num_agg[col].astype(np.float32)\n    # Get the difference\n    test_diff = get_difference(test, num_features)\n    # Merge all produced dataframes \n    test = test_num_agg.merge(test_diff, how = 'inner', on = 'customer_ID')\n    del test_num_agg, test_diff\n    gc.collect()\n    # Save files to disk\n    test = test.to_pandas()\n    test.to_parquet('test_risk_fe.parquet')\n    del test\n    gc.collect()\n    \nread_preprocess_risk_data()    ","metadata":{"execution":{"iopub.status.busy":"2022-09-06T17:32:39.434080Z","iopub.execute_input":"2022-09-06T17:32:39.434451Z","iopub.status.idle":"2022-09-06T17:53:48.812678Z","shell.execute_reply.started":"2022-09-06T17:32:39.434420Z","shell.execute_reply":"2022-09-06T17:53:48.811363Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**BALANCE VARIABLES**","metadata":{}},{"cell_type":"code","source":"def read_preprocess_balance_data():\n    full_train = cudf.read_parquet('../input/amex-data-integer-dtypes-parquet-format/train.parquet')\n    balance_vars = [col for col in full_train.columns if col.startswith(\"B_\")]\n    cid_time = [\"customer_ID\",\"S_2\"]\n    balance_vars.extend(cid_time)\n    del full_train\n    gc.collect()\n    train = cudf.read_parquet('../input/amex-data-integer-dtypes-parquet-format/train.parquet', columns = list(balance_vars))\n    features = train.drop([\"customer_ID\", \"S_2\"], axis = 1).columns.to_list()\n    cat_features = [\n        \"B_30\",\n        \"B_38\",\n    ]\n    num_features = [col for col in features if col not in cat_features] #extracts only column names\n    print('Training set: preprocessing...')\n    # Denoise train (only number) with np.floor\n    train[num_features] = cudf.DataFrame({col: np.floor(train[col]*100)/100 for col in train[num_features].columns})\n    # Adding functions of interest (numerical features)\n    num_function = ['mean', 'std', 'min', 'max','last']\n    train_num_agg = train.groupby(\"customer_ID\", sort = True)[num_features].agg(num_function)\n    # Join the name of columns\n    train_num_agg.columns = ['_'.join(x) for x in train_num_agg.columns]\n    train_num_agg.reset_index(inplace = True)\n    # Adding functions of interest (categorical features)\n    cat_function = ['count', 'last', 'nunique']\n    train_cat_agg = train.groupby(\"customer_ID\", sort = True)[cat_features].agg(cat_function)\n    # Join the name of columns\n    train_cat_agg.columns = ['_'.join(x) for x in train_cat_agg.columns]\n    train_cat_agg.reset_index(inplace = True)\n    # Remove columns with ONE single value\n    for col in train_cat_agg.drop(\"customer_ID\", axis = 1).columns:\n        if len(train_cat_agg[col].unique()) == 1:\n            train_cat_agg.drop(col,inplace=True,axis=1)\n    # Transform float64 or float16 columns to float32 \n    cols = list(train_num_agg.dtypes[(train_num_agg.dtypes == 'float64') | (train_num_agg.dtypes == 'float16')].index)\n    for col in tqdm(cols):\n        train_num_agg[col] = train_num_agg[col].astype(np.float32)\n    # Transform int64 or int8 columns to int32\n    cols = list(train_cat_agg.dtypes[(train_cat_agg.dtypes == 'int64') | (train_cat_agg.dtypes == 'int8')].index)\n    for col in tqdm(cols):\n        train_cat_agg[col] = train_cat_agg[col].astype(np.int16)\n    # Get the difference\n    train_diff = get_difference(train, num_features)\n    # Read csv file with labels\n    train_labels = cudf.read_csv('../input/amex-default-prediction/train_labels.csv')\n    # Merge all produced dataframes \n    train = train_num_agg.merge(train_cat_agg, how = 'inner', on = 'customer_ID').merge(train_diff, how = 'inner', on = 'customer_ID').merge(train_labels, how = 'inner', on = 'customer_ID')\n    del train_num_agg, train_cat_agg, train_diff\n    gc.collect()\n    # Save files to disk\n    train = train.to_pandas()\n    train.to_parquet('train_balance_fe.parquet')\n    del train\n    gc.collect()\n    test = cudf.read_parquet('../input/amex-data-integer-dtypes-parquet-format/test.parquet', columns = list(balance_vars))\n    print('Test set: preprocessing...')\n    # Denoise test (only number) with np.floor\n    test[num_features] = cudf.DataFrame({col: np.floor(test[col]*100)/100 for col in test[num_features].columns})\n    # Adding functions of interest (numerical features)\n    num_function = ['mean', 'std', 'min', 'max','last']\n    test_num_agg = test.groupby(\"customer_ID\", sort = True)[num_features].agg(num_function)\n    # Join the name of columns\n    test_num_agg.columns = ['_'.join(x) for x in test_num_agg.columns]\n    test_num_agg.reset_index(inplace = True)\n    # Adding functions of interest (categorical features)\n    cat_function = ['count', 'last', 'nunique']\n    test_cat_agg = test.groupby(\"customer_ID\", sort = True)[cat_features].agg(cat_function)\n    # Join the name of columns\n    test_cat_agg.columns = ['_'.join(x) for x in test_cat_agg.columns]\n    test_cat_agg.reset_index(inplace = True)\n    # Remove columns with ONE single value\n    for col in test_cat_agg.drop(\"customer_ID\", axis = 1).columns:\n        if len(test_cat_agg[col].unique()) == 1:\n            test_cat_agg.drop(col,inplace=True,axis=1)\n    # Transform float64 or float16 columns to float32 \n    cols = list(test_num_agg.dtypes[(test_num_agg.dtypes == 'float64') | (test_num_agg.dtypes == 'float16')].index)\n    for col in tqdm(cols):\n        test_num_agg[col] = test_num_agg[col].astype(np.float32)\n    # Transform int64 or int8 columns to int32\n    cols = list(test_cat_agg.dtypes[(test_cat_agg.dtypes == 'int64') | (test_cat_agg.dtypes == 'int8')].index)\n    for col in tqdm(cols):\n        test_cat_agg[col] = test_cat_agg[col].astype(np.int16)\n    # Get the difference\n    test_diff = get_difference(test, num_features)\n    # Merge all produced dataframes \n    test = test_num_agg.merge(test_cat_agg, how = 'inner', on = 'customer_ID').merge(test_diff, how = 'inner', on = 'customer_ID')\n    del test_num_agg, test_cat_agg, test_diff\n    gc.collect()\n    # Save files to disk\n    test = test.to_pandas()\n    test.to_parquet('test_balance_fe.parquet')\n    del test\n    gc.collect()\n    \nread_preprocess_balance_data()    ","metadata":{"execution":{"iopub.status.busy":"2022-09-06T17:53:48.814308Z","iopub.execute_input":"2022-09-06T17:53:48.814708Z","iopub.status.idle":"2022-09-06T18:14:29.218823Z","shell.execute_reply.started":"2022-09-06T17:53:48.814667Z","shell.execute_reply":"2022-09-06T18:14:29.217717Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**DELINQUENCY VARIABLES**","metadata":{}},{"cell_type":"code","source":"def read_preprocess_delinquency_data():\n    full_train = cudf.read_parquet('../input/amex-data-integer-dtypes-parquet-format/train.parquet')\n    delinquency_vars = [col for col in full_train.columns if col.startswith(\"D_\")]\n    cid_time = [\"customer_ID\",\"S_2\"]\n    delinquency_vars.extend(cid_time)\n    del full_train\n    gc.collect()\n    train = cudf.read_parquet('../input/amex-data-integer-dtypes-parquet-format/train.parquet', columns = list(delinquency_vars))\n    features = train.drop([\"customer_ID\", \"S_2\"], axis = 1).columns.to_list()\n    cat_features = [\n        \"D_114\",\n        \"D_116\",\n        \"D_117\",\n        \"D_120\",\n        \"D_126\",\n        \"D_63\",\n        \"D_64\",\n        \"D_66\",\n        \"D_68\",\n    ]\n    num_features = [col for col in features if col not in cat_features] #extracts only column names\n    print('Training set: preprocessing...')\n    # Denoise train (only number) with np.floor\n    train[num_features] = cudf.DataFrame({col: np.floor(train[col]*100)/100 for col in train[num_features].columns})\n    # Adding functions of interest (numerical features)\n    num_function = ['mean', 'std', 'min', 'max','last']\n    train_num_agg = train.groupby(\"customer_ID\", sort = True)[num_features].agg(num_function)\n    # Join the name of columns\n    train_num_agg.columns = ['_'.join(x) for x in train_num_agg.columns]\n    train_num_agg.reset_index(inplace = True)\n    # Adding functions of interest (categorical features)\n    cat_function = ['count', 'last', 'nunique']\n    train_cat_agg = train.groupby(\"customer_ID\", sort = True)[cat_features].agg(cat_function)\n    # Join the name of columns\n    train_cat_agg.columns = ['_'.join(x) for x in train_cat_agg.columns]\n    train_cat_agg.reset_index(inplace = True)\n    # Remove columns with ONE single value\n    for col in train_cat_agg.drop(\"customer_ID\", axis = 1).columns:\n        if len(train_cat_agg[col].unique()) == 1:\n            train_cat_agg.drop(col,inplace=True,axis=1)\n    # Transform float64 or float16 columns to float32 \n    cols = list(train_num_agg.dtypes[(train_num_agg.dtypes == 'float64') | (train_num_agg.dtypes == 'float16')].index)\n    for col in tqdm(cols):\n        train_num_agg[col] = train_num_agg[col].astype(np.float32)\n    # Transform int64 or int8 columns to int32\n    cols = list(train_cat_agg.dtypes[(train_cat_agg.dtypes == 'int64') | (train_cat_agg.dtypes == 'int8')].index)\n    for col in tqdm(cols):\n        train_cat_agg[col] = train_cat_agg[col].astype(np.int16)\n    # Get the difference\n    train_diff = get_difference(train, num_features)\n    # Read csv file with labels\n    train_labels = cudf.read_csv('../input/amex-default-prediction/train_labels.csv')\n    # Merge all produced dataframes \n    train = train_num_agg.merge(train_cat_agg, how = 'inner', on = 'customer_ID').merge(train_diff, how = 'inner', on = 'customer_ID').merge(train_labels, how = 'inner', on = 'customer_ID')\n    del train_num_agg, train_cat_agg, train_diff\n    gc.collect()\n    # Save files to disk\n    train = train.to_pandas()\n    train.to_parquet('train_delinquency_fe.parquet')\n    del train\n    gc.collect()\n    test = cudf.read_parquet('../input/amex-data-integer-dtypes-parquet-format/test.parquet', columns = list(delinquency_vars))\n    print('Test set: preprocessing...')\n    # Denoise test (only number) with np.floor\n    test[num_features] = cudf.DataFrame({col: np.floor(test[col]*100)/100 for col in test[num_features].columns})\n    # Adding functions of interest (numerical features)\n    num_function = ['mean', 'std', 'min', 'max','last']\n    test_num_agg = test.groupby(\"customer_ID\", sort = True)[num_features].agg(num_function)\n    # Join the name of columns\n    test_num_agg.columns = ['_'.join(x) for x in test_num_agg.columns]\n    test_num_agg.reset_index(inplace = True)\n    # Adding functions of interest (categorical features)\n    cat_function = ['count', 'last', 'nunique']\n    test_cat_agg = test.groupby(\"customer_ID\", sort = True)[cat_features].agg(cat_function)\n    # Join the name of columns\n    test_cat_agg.columns = ['_'.join(x) for x in test_cat_agg.columns]\n    test_cat_agg.reset_index(inplace = True)\n    # Remove columns with ONE single value\n    for col in test_cat_agg.drop(\"customer_ID\", axis = 1).columns:\n        if len(test_cat_agg[col].unique()) == 1:\n            test_cat_agg.drop(col,inplace=True,axis=1)\n    # Transform float64 or float16 columns to float32 \n    cols = list(test_num_agg.dtypes[(test_num_agg.dtypes == 'float64') | (test_num_agg.dtypes == 'float16')].index)\n    for col in tqdm(cols):\n        test_num_agg[col] = test_num_agg[col].astype(np.float32)\n    # Transform int64 or int8 columns to int32\n    cols = list(test_cat_agg.dtypes[(test_cat_agg.dtypes == 'int64') | (test_cat_agg.dtypes == 'int8')].index)\n    for col in tqdm(cols):\n        test_cat_agg[col] = test_cat_agg[col].astype(np.int16)\n    # Get the difference\n    test_diff = get_difference(test, num_features)\n    # Merge all produced dataframes \n    test = test_num_agg.merge(test_cat_agg, how = 'inner', on = 'customer_ID').merge(test_diff, how = 'inner', on = 'customer_ID')\n    del test_num_agg, test_cat_agg, test_diff\n    gc.collect()\n    # Save files to disk\n    test = test.to_pandas()\n    test.to_parquet('test_delinquency_fe.parquet')\n    del test\n    gc.collect()\n    \nread_preprocess_delinquency_data()    ","metadata":{"execution":{"iopub.status.busy":"2022-09-06T21:43:06.575673Z","iopub.execute_input":"2022-09-06T21:43:06.576034Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**SPEND VARIABLES**","metadata":{}},{"cell_type":"code","source":"def read_preprocess_spend_data():\n    full_train = cudf.read_parquet('../input/amex-data-integer-dtypes-parquet-format/train.parquet')\n    spend_vars = [col for col in full_train.columns if col.startswith(\"S_\")]\n    cid_time = [\"customer_ID\",\"S_2\"]\n    spend_vars.extend(cid_time)\n    del full_train\n    gc.collect()\n    train = cudf.read_parquet('../input/amex-data-integer-dtypes-parquet-format/train.parquet', columns = list(spend_vars))\n    num_features = train.drop([\"customer_ID\", \"S_2\"], axis = 1).columns.to_list()\n    print('Training set: preprocessing...')\n    # Denoise train (only number) with np.floor\n    train[num_features] = cudf.DataFrame({col: np.floor(train[col]*100)/100 for col in train[num_features].columns})\n    # Adding functions of interest (numerical features)\n    num_function = ['mean', 'std', 'min', 'max','last']\n    train_num_agg = train.groupby(\"customer_ID\", sort = True)[num_features].agg(num_function)\n    # Join the name of columns\n    train_num_agg.columns = ['_'.join(x) for x in train_num_agg.columns]\n    train_num_agg.reset_index(inplace = True)\n    # Transform float64 columns to float32 \n    cols = list(train_num_agg.dtypes[(train_num_agg.dtypes == 'float64') | (train_num_agg.dtypes == 'float16')].index)\n    for col in tqdm(cols):\n        train_num_agg[col] = train_num_agg[col].astype(np.float32)\n    # Get the difference\n    train_diff = get_difference(train, num_features)\n    # Read csv file with labels\n    train_labels = cudf.read_csv('../input/amex-default-prediction/train_labels.csv')\n    # Merge all produced dataframes \n    train = train_num_agg.merge(train_diff, how = 'inner', on = 'customer_ID').merge(train_labels, how = 'inner', on = 'customer_ID')\n    del train_num_agg, train_diff\n    gc.collect()\n    # Save files to disk\n    train = train.to_pandas()\n    train.to_parquet('train_spend_fe.parquet')\n    del train\n    gc.collect()\n    test = cudf.read_parquet('../input/amex-data-integer-dtypes-parquet-format/test.parquet', columns = list(spend_vars))\n    print('Test set: preprocessing...')\n    # Denoise test (only number) with np.floor\n    test[num_features] = cudf.DataFrame({col: np.floor(test[col]*100)/100 for col in test[num_features].columns})\n    # Adding functions of interest (numerical features)\n    num_function = ['mean', 'std', 'min', 'max','last']\n    test_num_agg = test.groupby(\"customer_ID\", sort = True)[num_features].agg(num_function)\n    # Join the name of columns\n    test_num_agg.columns = ['_'.join(x) for x in test_num_agg.columns]\n    test_num_agg.reset_index(inplace = True)\n    # Transform float64 or float16 columns to float32 \n    cols = list(test_num_agg.dtypes[(test_num_agg.dtypes == 'float64') | (test_num_agg.dtypes == 'float16')].index)\n    for col in tqdm(cols):\n        test_num_agg[col] = test_num_agg[col].astype(np.float32)\n    # Get the difference\n    test_diff = get_difference(test, num_features)\n    # Merge all produced dataframes \n    test = test_num_agg.merge(test_diff, how = 'inner', on = 'customer_ID')\n    del test_num_agg, test_diff\n    gc.collect()\n    # Save files to disk\n    test = test.to_pandas()\n    test.to_parquet('test_spend_fe.parquet')\n    del test\n    gc.collect()\n    \nread_preprocess_spend_data() ","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}