{"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":"Credit to 此般浅薄 for the initial inspiration","metadata":{}},{"cell_type":"markdown","source":"LightGBM是一种基于决策树算法的梯度提升框架，它可以用于分类问题、回归问题和排序问题。\n\n相比其他基于决策树的梯度提升框架，LightGBM具有更快的训练速度和更高的准确性，尤其适合处理大规模数据集。\nLightGBM的核心思想是将训练数据集按照某种指标（如梯度）进行划分，从而构建一棵棵决策树。在训练过程中，LightGBM会对每个叶子节点进行梯度优化，从而提高模型的准确性。\n\n此外，LightGBM还支持特征并行和数据并行等多种并行计算模式，可以充分利用多核CPU和GPU的计算能力。\n\n和LightGBM相似的模型包括XGBoost、CatBoost等基于决策树的梯度提升框架。它们都采用了类似的决策树构建方式和梯度优化算法，但在一些细节方面有所不同。例如，XGBoost采用了二阶泰勒展开的方法来近似损失函数，从而提高模型的准确性；CatBoost则采用了基于对称置换的排序方法来处理类别特征，从而提高模型的鲁棒性。\n\n这些模型都可以用于解决分类、回归和排序问题，并且都在机器学习领域得到了广泛的应用。\n\nwritten by Allen","metadata":{}},{"cell_type":"markdown","source":"时间序列是指在时间上按照一定的时间顺序排列的一组数据序列，通常是一些定期或不定期地收集到的数据，例如每日的股票价格、每月的销售量、每年的气温变化等。\n\n时间序列分析是一种统计分析方法，用于预测未来的趋势、周期性和季节性变化等，并在实际应用中广泛用于经济学、金融学、气象学、工程学等领域。\n\n常用的时间序列分析方法包括平稳性检验、自相关函数和偏自相关函数分析、时间序列分解、移动平均模型、自回归模型、ARIMA模型等。","metadata":{}},{"cell_type":"code","source":"# Install tsflex and seglearn\n!pip install tsflex --no-index --find-links=file:///kaggle/input/time-series-tools\n!pip install seglearn --no-index --find-links=file:///kaggle/input/time-series-tools","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","execution":{"iopub.execute_input":"2023-04-21T01:30:41.243137Z","iopub.status.busy":"2023-04-21T01:30:41.242724Z","iopub.status.idle":"2023-04-21T01:31:05.875275Z","shell.execute_reply":"2023-04-21T01:31:05.873632Z","shell.execute_reply.started":"2023-04-21T01:30:41.243099Z"},"papermill":{"duration":26.425736,"end_time":"2023-04-16T22:41:22.382325","exception":false,"start_time":"2023-04-16T22:40:55.956589","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# seglearn.feature_functions是一个Python库，用于生成时间序列信号的特征。它包含了许多用于生成时间序列特征的函数，如平均值、方差、斜率、峰度等等。这些函数可以被用于提取特征，并用于机器学\n\nimport numpy as np\nimport pandas as pd\nfrom sklearn import *\nimport glob\nfrom tqdm.auto import tqdm\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom os import path\nfrom pathlib import Path\n'''\n这段代码导入了 seglearn 库中的两个模块：base_features 和 emg_features。\n\nbase_features 模块提供了常用的特征函数，用于计算时间序列数据的基本特征，例如均值、方差、最大值、最小值等。这些特征函数可以用于多种时间序列数据分析任务，包括机器学习、信号处理等领域。\n\nemg_features 模块则提供了一些专门用于肌电信号处理的特征函数，例如肌电干扰峰（EMG spike）、动作电位（action potential）等。这些特征函数可以用于分析肌肉收缩的特征，例如肌肉的活跃度、疲劳程度等。\n\n这些特征函数可以帮助使用者更方便地进行时间序列数据的特征计算和分析。\n'''\nfrom seglearn.feature_functions import base_features, emg_features\n'''\n这段代码导入了 tsflex 库中的两个模块：FeatureCollection 和 MultipleFeatureDescriptors。\n\nFeatureCollection 是一个类，用于存储和操作时间序列数据的特征。它提供了一些方法来计算和保存特征，并支持将多个特征集合在一起。\n\nMultipleFeatureDescriptors 是另一个类，用于描述多个特征的计算方式。它可以将多个特征计算方式打包在一起，方便使用者一次性计算多个特征。\n\n这些模块可以帮助使用者更方便地进行时间序列数据的特征计算和管理。\n'''\nfrom tsflex.features import FeatureCollection, MultipleFeatureDescriptors\n'''\nseglearn_feature_dict_wrapper 是 TSFLEX 库中的一个模块，它提供了一个用于将 Seglearn 库中的时间序列特征转换为字典格式的包装器。\n\nSeglearn 是一个用于时间序列分析的 Python 库，其目标是提供简单、统一的接口来处理时间序列数据。\n\nseglearn_feature_dict_wrapper 模块中的函数可以将 Seglearn 库中的时间序列特征转换为字典格式，以便后续在 TSFLEX 中使用。\n\n这个模块的作用是简化特征工程的过程，并且提供了一种方便的方法来处理时间序列数据。\n'''\nfrom tsflex.features.integrations import seglearn_feature_dict_wrapper\n'''\n它是scikit-learn中的一个交叉验证方法，用于将数据集划分为K个互不相交的折叠，其中每个折叠都用于验证模型，其余折叠用于训练模型。\n\n与K折交叉验证不同的是，GroupKFold考虑到了数据集中的分组信息，确保同一组的数据不会同时出现在训练集和验证集中，这对于处理时间序列数据或具有相关性的样本非常有用。\n'''\nfrom sklearn.model_selection import GroupKFold\n'''\n这段代码导入了LightGBM库和sklearn库中的MultiOutputRegressor类、clone函数和average_precision_score函数。\n\n其中，LightGBM是一种基于决策树的梯度提升框架，常用于处理大规模数据集和高维特征；MultiOutputRegressor类是用于多输出回归问题的封装器，可以将任何回归模型转换为多输出回归模型；\n\nclone函数用于创建一个新的估计器对象，它具有与原始估计器相同的参数和属性；\n\naverage_precision_score函数用于计算二分类问题中平均精度（average precision），即预测为正例的样本中实际为正例的比例，是一种评估分类器性能的指标。\n'''\nimport lightgbm as lgb\nfrom sklearn.multioutput import MultiOutputRegressor\nfrom sklearn.base import clone\nfrom sklearn.metrics import average_precision_score","metadata":{"execution":{"iopub.execute_input":"2023-04-21T01:31:05.87828Z","iopub.status.busy":"2023-04-21T01:31:05.877797Z","iopub.status.idle":"2023-04-21T01:31:08.601308Z","shell.execute_reply":"2023-04-21T01:31:08.600294Z","shell.execute_reply.started":"2023-04-21T01:31:05.878239Z"},"papermill":{"duration":2.755431,"end_time":"2023-04-16T22:41:25.148066","exception":false,"start_time":"2023-04-16T22:41:22.392635","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Grab important files","metadata":{"papermill":{"duration":0.009576,"end_time":"2023-04-16T22:41:25.16752","exception":false,"start_time":"2023-04-16T22:41:25.157944","status":"completed"},"tags":[]}},{"cell_type":"code","source":"'''\nglob.glob是Python中的一个函数，用于查找符合特定模式的文件路径。\n\n它接受一个包含通配符的字符串参数，返回一个包含符合模式的文件路径的列表。\n\n通配符可以用来匹配文件名中的任何部分，如*表示匹配任意字符串，?表示匹配任意单个字符，[]表示匹配指定字符集合中的任意一个字符等等。\n\nglob.glob可以用于快速查找符合特定模式的文件，比如查找所有的文本文件，或者查找某个目录下所有的图片文件等等。\n'''\n\nroot = '/kaggle/input/tlvmc-parkinsons-freezing-gait-prediction/'\n\ntrain = glob.glob(path.join(root, 'train/**/**'))\ntest = glob.glob(path.join(root, 'test/**/**'))\n\nsubjects = pd.read_csv(path.join(root, 'subjects.csv'))\ntasks = pd.read_csv(path.join(root, 'tasks.csv'))\nevents = pd.read_csv(path.join(root, 'events.csv'))\n\ntdcsfog_metadata = pd.read_csv(path.join(root, 'tdcsfog_metadata.csv'))\ndefog_metadata = pd.read_csv(path.join(root, 'defog_metadata.csv')) \n\ntdcsfog_metadata['Module'] = 'tdcsfog'\ndefog_metadata['Module'] = 'defog'\n\nfull_metadata = pd.concat([tdcsfog_metadata, defog_metadata])\n","metadata":{"execution":{"iopub.execute_input":"2023-04-21T01:31:08.602929Z","iopub.status.busy":"2023-04-21T01:31:08.602597Z","iopub.status.idle":"2023-04-21T01:31:08.749108Z","shell.execute_reply":"2023-04-21T01:31:08.748115Z","shell.execute_reply.started":"2023-04-21T01:31:08.602897Z"},"papermill":{"duration":0.170669,"end_time":"2023-04-16T22:41:25.347896","exception":false,"start_time":"2023-04-16T22:41:25.177227","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"subjects.loc[subjects['Subject'] == 'fe5d84', 'Sex'] = 'F'","metadata":{"execution":{"iopub.execute_input":"2023-04-21T01:31:08.752315Z","iopub.status.busy":"2023-04-21T01:31:08.751967Z","iopub.status.idle":"2023-04-21T01:31:08.758469Z","shell.execute_reply":"2023-04-21T01:31:08.757339Z","shell.execute_reply.started":"2023-04-21T01:31:08.752281Z"}},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"seed = 100\ncluster_size = 8","metadata":{"execution":{"iopub.execute_input":"2023-04-21T01:31:08.761109Z","iopub.status.busy":"2023-04-21T01:31:08.760314Z","iopub.status.idle":"2023-04-21T01:31:08.776238Z","shell.execute_reply":"2023-04-21T01:31:08.775046Z","shell.execute_reply.started":"2023-04-21T01:31:08.761062Z"},"papermill":{"duration":0.02392,"end_time":"2023-04-16T22:41:25.577077","exception":false,"start_time":"2023-04-16T22:41:25.553157","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"'''\n这段代码的作用是进行数据预处理和聚类分析。首先，将 \"Sex\" 列中的每个值进行因式分解，返回一个整数数组，用于对 \"Sex\" 列进行编码。\n这段代码的作用是将数据集中的“Sex”列转换成数字编码。具体来说，它会返回一个由 0 和 1 组成的数组，其中 0 表示性别为女性，1 表示性别为男性。这种编码方式可以方便地在机器学习模型中使用。\n\n然后，将数据框中的缺失值填充为 0，并按照 \"Subject\" 列进行分组，对每个分组的数据取中位数。\n\n接着，使用 KMeans 算法对 \"Subject\" 列进行聚类，将每个数据点分配到不同的簇中。最后，使用 \"rename\" 函数将数据框中的列名更改为新的名称。\n\n这些操作通常用于数据分析和机器学习中的特征工程和聚类分析。\n'''\nsubjects['Sex'] = subjects['Sex'].factorize()[0]\nsubjects = subjects.fillna(0).groupby('Subject').median()\nsubjects['s_group'] = cluster.KMeans(n_clusters = cluster_size, random_state = seed).fit_predict(subjects[subjects.columns[1:]])\nnew_names = {'Visit':'s_visit','Age':'s_age','YearsSinceDx':'s_years','UPDRSIII_On':'s_on','UPDRSIII_Off':'s_off','NFOGQ':'s_NFOGQ', 'Sex': 's_sex'}\nsubjects = subjects.rename(columns = new_names)\nsubjects","metadata":{"execution":{"iopub.execute_input":"2023-04-21T01:31:08.779552Z","iopub.status.busy":"2023-04-21T01:31:08.777921Z","iopub.status.idle":"2023-04-21T01:31:08.870787Z","shell.execute_reply":"2023-04-21T01:31:08.869562Z","shell.execute_reply.started":"2023-04-21T01:31:08.779514Z"},"papermill":{"duration":0.110973,"end_time":"2023-04-16T22:41:25.698532","exception":false,"start_time":"2023-04-16T22:41:25.587559","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 计算每个任务的时长，将时长作为一个新的列添加到原任务列表中，使用了 Pandas 库中的 DataFrame 的操作。\ntasks['Duration'] = tasks['End'] - tasks['Begin']\n# 使用 Pandas 的 pivot_table 方法，对任务列表进行重新排列，将任务的时长按照 Id 和 Task 进行分组，统计每个任务的总时长。\ntasks = pd.pivot_table(tasks, values=['Duration'], index=['Id'], columns=['Task'], aggfunc='sum', fill_value=0)\n# 对新的 DataFrame 列表的列名进行修改，将列名中的 Duration 去掉。\ntasks.columns = [c[1] for c in tasks.columns]\n# 重置 DataFrame 的索引。\ntasks = tasks.reset_index()\n# 使用 KMeans 算法对任务列表进行聚类，将聚类结果作为新的一列添加到 DataFrame 中。\ntasks['t_group'] = cluster.KMeans(n_clusters = cluster_size, random_state = seed).fit_predict(tasks[tasks.columns[1:]])","metadata":{"execution":{"iopub.execute_input":"2023-04-21T01:31:08.872422Z","iopub.status.busy":"2023-04-21T01:31:08.872068Z","iopub.status.idle":"2023-04-21T01:31:08.948153Z","shell.execute_reply":"2023-04-21T01:31:08.946914Z","shell.execute_reply.started":"2023-04-21T01:31:08.872362Z"},"papermill":{"duration":0.108699,"end_time":"2023-04-16T22:41:25.903139","exception":false,"start_time":"2023-04-16T22:41:25.79444","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# merge the subjects with the metadata\n# 使用merge函数将两个数据集按照Subject列进行左连接，即full_metadata中的每一行都会被保留，并且如果有相应的Subject值在subjects中出现，则会将对应的行合并到full_metadata中。\n# 最后使用copy()函数创建了一个新的DataFrame，以保留原始数据集的完整性。\nmetadata_w_subjects = full_metadata.merge(subjects, how='left', on='Subject').copy()\nfeatures = metadata_w_subjects.columns","metadata":{"execution":{"iopub.execute_input":"2023-04-21T01:31:08.950104Z","iopub.status.busy":"2023-04-21T01:31:08.949542Z","iopub.status.idle":"2023-04-21T01:31:08.962007Z","shell.execute_reply":"2023-04-21T01:31:08.960776Z","shell.execute_reply.started":"2023-04-21T01:31:08.950064Z"},"papermill":{"duration":0.060364,"end_time":"2023-04-16T22:41:26.141472","exception":false,"start_time":"2023-04-16T22:41:26.081108","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"metadata_w_subjects['Medication'] = metadata_w_subjects['Medication'].factorize()[0]","metadata":{"execution":{"iopub.execute_input":"2023-04-21T01:31:08.964093Z","iopub.status.busy":"2023-04-21T01:31:08.963763Z","iopub.status.idle":"2023-04-21T01:31:08.970239Z","shell.execute_reply":"2023-04-21T01:31:08.968995Z","shell.execute_reply.started":"2023-04-21T01:31:08.96406Z"}},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Extract from seglearn.feature_functions import base_features, emg_features\n\nfrom tsflex.features import FeatureCollection, MultipleFeatureDescriptors\nfrom tsflex.features.integrations import seglearn_feature_dict_wrapper from the time series data itself","metadata":{"papermill":{"duration":0.021931,"end_time":"2023-04-16T22:41:26.370019","exception":false,"start_time":"2023-04-16T22:41:26.348088","status":"completed"},"tags":[]}},{"cell_type":"code","source":"'''\n这段代码中，MultipleFeatureDescriptors 是一个类，它的实例 basic_feats 用于将多个时间序列数据转换成一组特征向量。\n\nfunctions 参数是一个函数列表，用于计算每个时间序列的特征值。\n\n在这里，seglearn_feature_dict_wrapper 函数将 base_features() 函数返回的特征字典转换为列表。\n\nseries_names 参数是一个字符串列表，用于指定每个时间序列数据的名称。\n\nwindows 和 strides 参数分别是窗口大小和步长，用于将时间序列数据分割成多个窗口。\n'''\nbasic_feats = MultipleFeatureDescriptors(\n    functions=seglearn_feature_dict_wrapper(base_features()),\n    series_names=['AccV', 'AccML', 'AccAP'],\n    windows=[5000],\n    strides=[5000],\n)\n\nemg_feats = emg_features()\ndel emg_feats['simple square integral'] # is same as abs_energy (which is in base_features)\n\nemg_feats = MultipleFeatureDescriptors(\n    functions=seglearn_feature_dict_wrapper(emg_feats),\n    series_names=['AccV', 'AccML', 'AccAP'],\n    windows=[5000],\n    strides=[5000],\n)\n\n'''\n这段代码将之前定义的 basic_feats 和 emg_feats 两个特征集合合并成一个 FeatureCollection 对象 fc。\n\nFeatureCollection 是 tsfresh 库中用于表示特征集合的类，可以对其中的特征进行统一的处理和提取。合并后，fc 中就包含了 basic_feats 和 emg_feats 中定义的所有特征。\n'''\nfc = FeatureCollection([basic_feats, emg_feats])","metadata":{"execution":{"iopub.execute_input":"2023-04-21T01:31:08.974543Z","iopub.status.busy":"2023-04-21T01:31:08.974165Z","iopub.status.idle":"2023-04-21T01:31:08.9843Z","shell.execute_reply":"2023-04-21T01:31:08.983112Z","shell.execute_reply.started":"2023-04-21T01:31:08.974497Z"},"papermill":{"duration":0.032203,"end_time":"2023-04-16T22:41:26.517639","exception":false,"start_time":"2023-04-16T22:41:26.485436","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 这段代码是一个Python函数，其作用是读取一个CSV文件并将其转换为一个Pandas DataFrame对象。\ndef reader(file):\n    try:\n        # 其中，index_col参数指定了要用作索引的列名，usecols参数指定了要使用的列名列表。这里我们只选择了6列进行读取。\n        df = pd.read_csv(file, index_col='Time', usecols=['Time', 'AccV', 'AccML', 'AccAP', 'StartHesitation', 'Turn' , 'Walking'])\n\n        # 这一部分将CSV文件的路径进行分割，并将文件名中的.csv后缀去掉作为Id列的值。同时，它还将CSV文件所在的目录名作为Module列的值。\n        path_split = file.split('/')\n        df['Id'] = path_split[-1].split('.')[0]\n        dataset = Path(file).parts[-2]\n        df['Module'] = dataset\n        \n        # this is done because the speeds are at different rates for the datasets\n#         if dataset == 'tdcsfog':\n#             df.AccV = df.AccV / 9.80665\n#             df.AccML = df.AccML / 9.80665\n#             df.AccAP = df.AccAP / 9.80665\n\n        # 这一部分将Time列的值除以该列中最大值，将结果作为Time_frac列的值。\n        df['Time_frac']=(df.index/df.index.max()).values\n        \n        # 这一部分将两个其他的DataFrame对象（tasks和metadata_w_subjects）与当前的DataFrame对象进行合并。合并的方式是使用Id列作为连接键，并将结果填充到NaN值处。\n        df = pd.merge(df, tasks[['Id','t_group']], how='left', on='Id').fillna(-1)\n        \n        df = pd.merge(df, metadata_w_subjects[['Id','Subject', 'Visit','Test','Medication','s_group']], how='left', on='Id').fillna(-1)\n        \n        # 这一部分使用名为fc.calculate()的函数计算一些特征，并将其添加到DataFrame对象中。然后，它将计算出的特征与当前的DataFrame对象进行合并。\n        df_feats = fc.calculate(df, return_df=True, include_final_window=True, approve_sparsity=True, window_idx=\"begin\").astype(np.float32)\n        df = df.merge(df_feats, how=\"left\", left_index=True, right_index=True)\n        \n#         # stride\n#         df[\"Stride\"] = df[\"AccV\"] + df[\"AccML\"] + df[\"AccAP\"]\n\n#         # step\n#         df[\"Step\"] = np.sqrt(abs(df[\"Stride\"]))\n        \n        # 最后，这一部分使用fillna()函数填充空值，并返回处理后的DataFrame对象。如果出现任何异常情况，则直接跳过。\n        df.fillna(method=\"ffill\", inplace=True)\n        \n        return df\n    except: pass\n\n\n'''\n这段代码主要实现了以下功能：\n\n遍历 train 列表中的每个文件 f，使用 reader 函数读取文件内容，并将读取的内容作为一个 DataFrame 对象；\n将所有读取的 DataFrame 对象通过 concat 函数合并为一个 DataFrame 对象 train；\n对 train 中的缺失值进行填充，将所有 NaN 值替换为 0；\n输出 train 的形状信息（即行数和列数）。\n'''\ntrain = pd.concat([reader(f) for f in tqdm(train)]).fillna(0); print(train.shape)\n'''\n首先，从train数据集的列名中选择所有不包含指定列名的列，存储在cols变量中。\n\n接下来，将StartHesitation、Turn、Walking这三列名存储在pcols变量中。最后，将Id、StartHesitation、Turn、Walking这四列名存储在scols变量中。这些变量可能会在后续的数据处理或分析中使用。\n'''\ncols = [c for c in train.columns if c not in ['Id','Subject','Module', 'Time', 'StartHesitation', 'Turn' , 'Walking', 'Valid', 'Task','Event']]\npcols = ['StartHesitation', 'Turn' , 'Walking']\nscols = ['Id', 'StartHesitation', 'Turn' , 'Walking']\ntrain=train.reset_index(drop=True)","metadata":{"execution":{"iopub.execute_input":"2023-04-21T01:31:08.98627Z","iopub.status.busy":"2023-04-21T01:31:08.985904Z","iopub.status.idle":"2023-04-21T01:39:16.081832Z","shell.execute_reply":"2023-04-21T01:39:16.079813Z","shell.execute_reply.started":"2023-04-21T01:31:08.986236Z"},"papermill":{"duration":533.977331,"end_time":"2023-04-16T22:50:20.513889","exception":false,"start_time":"2023-04-16T22:41:26.536558","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train.head()","metadata":{"execution":{"iopub.execute_input":"2023-04-21T01:39:16.086577Z","iopub.status.busy":"2023-04-21T01:39:16.085905Z","iopub.status.idle":"2023-04-21T01:39:16.122094Z","shell.execute_reply":"2023-04-21T01:39:16.120863Z","shell.execute_reply.started":"2023-04-21T01:39:16.086495Z"},"papermill":{"duration":0.048438,"end_time":"2023-04-16T22:50:20.575881","exception":false,"start_time":"2023-04-16T22:50:20.527443","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"LGBMMultiOutputRegressor算法中的参数对预测精度的影响如下：\n\ncolsample_bytree：\n每次构建树时，随机选择一定比例的特征进行训练，这个参数可以有效地降低过拟合。当colsample_bytree越大时，每次训练使用的特征数量也就越多，模型的多样性增加，模型的复杂度也会增加，因此容易过拟合。\n当colsample_bytree越小时，每次训练使用的特征数量也就越少，模型的多样性减少，模型的复杂度也会降低，因此容易欠拟合。\n建议将其设置为0.5~0.8之间的数值。\n\nlearning_rate：\n学习率决定了每次迭代中模型参数的更新幅度，较小的学习率能够使得模型更加稳定，但是需要更多的迭代次数才能收敛。\n当learning_rate越大时，模型训练速度会加快，但容易出现震荡或不收敛的情况。当learning_rate越小时，模型训练速度会变慢，但可以更好地学习到数据的细节信息。\n建议将其设置为0.01~0.1之间的数值。\n\nmax_depth：\n决策树的最大深度，较大的max_depth容易导致过拟合，较小的max_depth容易导致欠拟合。当max_depth越大时，模型的复杂度也会增加，容易过拟合。\n当max_depth越小时，模型的复杂度也会降低，容易欠拟合。\n建议将其设置为5~10之间的数值。\n\nmin_child_weight：\n决定了叶子节点上最小样本数，较大的min_child_weight可以防止过拟合，但可能会导致欠拟合。当\nmin_child_weight越大时，模型的复杂度也会降低，容易欠拟合。当min_child_weight越小时，模型的复杂度也会增加，容易过拟合。\n建议将其设置为1~10之间的数值。\n\nn_estimators：\n决策树的数量，较大的n_estimators可以提高模型的精度，但是也会增加计算时间。当n_estimators越大时，模型的复杂度也会增加，容易过拟合。当n_estimators越小时，模型的复杂度也会降低，容易欠拟合。\n建议将其设置为100~1000之间的数值。\n\nsubsample：每次迭代中，随机选择一定比例的样本进行训练，这个参数可以有效地降低过拟合。当subsample越大时，每次训练使用的样本数量也就越多，模型的复杂度也会增加，容易过拟合。当subsample越小时，每次训练使用的样本数量也就越少，模型的复杂度也会降低，容易欠拟合。建议将其设置为0.5~0.8之间的数值。\n\n为了提高预测精度，可以使用网格搜索或随机搜索来调整各个参数的值，以找到最优的参数组合。需要注意的是，搜索过程需要在训练集和验证集上进行交叉验证，以避免过拟合。","metadata":{}},{"cell_type":"markdown","source":"\nLGBMMultiOutputRegressor算法中的参数colsample_bytree、learning_rate、max_depth、min_child_weight、n_estimators、subsample都会影响模型的精度，\n但各个参数的影响因数据集而异，所以需要根据具体情况来进行调整。一般来说，调整learning_rate和n_estimators可以获得较显著的精度提升。\n\n其中，learning_rate是控制模型收敛速度的参数。如果learning_rate太大，模型会快速收敛但可能会过拟合；\n如果learning_rate太小，模型会收敛缓慢，需要更多的迭代次数才能达到收敛，但可以减少过拟合的风险。因此，调整learning_rate可以平衡模型的收敛速度和准确度。\n\nn_estimators是指模型中树的数量，也就是迭代次数。增加n_estimators可以提高模型的准确度，但也会增加计算时间和可能导致过拟合。\n因此，可以通过交叉验证的方式，找到一个最佳的n_estimators值，以平衡准确度和计算时间。\n\n其他参数的调整也可以对模型的精度产生影响，例如colsample_bytree可以控制每个决策树使用的特征数量，max_depth可以控制树的深度，min_child_weight可以控制子节点的最小权重等。\n这些参数的调整需要结合具体数据集来进行，可以通过网格搜索等方法来确定最优的参数组合。","metadata":{}},{"cell_type":"code","source":"best_params_ = {'colsample_bytree': 0.5282057895135501,\n 'learning_rate': 0.22659963168004743,\n 'max_depth': 8,\n 'min_child_weight': 3.1233911067827616,\n 'n_estimators': 291,\n 'subsample': 0.9961057796456088,\n }\n\ndef custom_average_precision(y_true, y_pred):\n    score = average_precision_score(y_true, y_pred)\n    return 'average_precision', score, True\n\n'''\n这段代码是一个自定义的多输出LGBMRegressor类，继承了sklearn的MultiOutputRegressor类。这个类的作用是用于多目标回归任务，其中每一个目标都是一个LGBMRegressor。这个类中的fit()方法的作用是训练模型。\n\n具体实现过程如下：\n\n首先，self.estimators_ = [clone(self.estimator) for _ in range(y.shape[1])]会创建与输出目标数量相同的LGBMRegressor模型，并将这些模型保存在self.estimators_中。\n\n接着，for i, estimator in enumerate(self.estimators_):会遍历每个目标对应的模型，其中i表示目标的索引，estimator表示对应的LGBMRegressor模型。\n\n然后，if eval_set: fit_params['eval_set'] = [(eval_set[0], eval_set[1][:, i])]会将评估数据集中对应目标的数据提取出来，作为当前模型的评估数据集。\n\n最后，estimator.fit(X, y[:, i], **fit_params)会使用当前模型对输入数据X和对应目标的输出数据y[:, i]进行训练。其中的**fit_params是传递给estimator.fit()方法的额外参数，例如训练轮数、学习率等等。\n\n最终，fit()方法会返回self对象。\n'''\nclass LGBMMultiOutputRegressor(MultiOutputRegressor):\n    def fit(self, X, y, eval_set=None, **fit_params):\n        self.estimators_ = [clone(self.estimator) for _ in range(y.shape[1])]\n        \n        for i, estimator in enumerate(self.estimators_):\n            if eval_set:\n                fit_params['eval_set'] = [(eval_set[0], eval_set[1][:, i])]\n            estimator.fit(X, y[:, i], **fit_params)\n        \n        return self","metadata":{"execution":{"iopub.execute_input":"2023-04-21T01:39:16.134812Z","iopub.status.busy":"2023-04-21T01:39:16.134463Z","iopub.status.idle":"2023-04-21T01:39:16.145204Z","shell.execute_reply":"2023-04-21T01:39:16.143994Z","shell.execute_reply.started":"2023-04-21T01:39:16.134779Z"},"papermill":{"duration":0.031497,"end_time":"2023-04-16T22:50:29.071479","exception":false,"start_time":"2023-04-16T22:50:29.039982","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"'''\n这段代码中，GroupKFold(5)是一个基于组的交叉验证方法，将数据集分成5个不重叠的折叠，每个折叠都由一个组中的数据组成。\n\n这个方法是为了避免同一组内的数据分到不同的折叠中，从而保证训练集和验证集中的数据不会互相干扰。这个方法常用于时间序列数据或者空间数据等具有一定相关性的数据集。\n\nkfold.split(train, groups=train.Subject)是将数据集train分成5个折叠，返回一个生成器对象，可以通过循环来遍历每个折叠的索引。\n\n其中，train是要进行交叉验证的数据集，groups=train.Subject是指定每个样本所属的组，这里假设每个样本都带有一个Subject属性表示所属组的标识。\n\n在交叉验证时，目的是将同一组的数据划分到同一个折叠中，所以需要根据组信息来划分数据。\n'''\nkfold = GroupKFold(5)\ngroups=kfold.split(train, groups=train.Subject)\n\nregs = []\ncvs = []\n\n# 使用 enumerate 函数遍历 groups 列表，并使用 tqdm 函数显示进度条。groups 是一个列表，其中包含了数据集的所有折叠（fold），每个折叠是由训练集和测试集的索引组成的元组 (tr_idx, te_idx)。\nfor _, (tr_idx, te_idx) in enumerate(tqdm(groups, total=5, desc=\"Folds\")):\n    \n    # 将训练集索引 tr_idx 转换为 Pandas Series，并使用 sample 方法从中随机抽取 2000000 个样本。这里使用了随机种子 42，以便使结果可重复。\n    tr_idx = pd.Series(tr_idx).sample(n=2000000,random_state=42).values\n\n    # 创建一个 LGBMMultiOutputRegressor 对象，该对象内部包含多个 LGBMRegressor 模型，用于同时预测多个输出变量。best_params 是一个字典，其中包含了 LGBMRegressor 的超参数，用于指定模型的配置。\n    multioutput_regressor = LGBMMultiOutputRegressor(lgb.LGBMRegressor(**best_params_))\n\n    # 从训练集和测试集中获取指定列（cols 和 pcols）的数据，并将其转换为 numpy 数组。cols 包含了特征列，pcols 包含了目标变量列。\n    x_train = train.loc[tr_idx, cols].to_numpy()\n    y_train = train.loc[tr_idx, pcols].to_numpy()\n    \n    x_test = train.loc[te_idx, cols].to_numpy()\n    y_test = train.loc[te_idx, pcols].to_numpy()\n\n    # 使用训练集数据拟合 multioutput_regressor 模型，并在每个训练周期后使用测试集数据计算平均精度（custom_average_precision）作为评估指标。\n    # 如果模型在连续 15 个训练周期中没有得到改进，则提前停止训练。verbose 参数设置为 0，表示不输出训练过程中的日志信息。\n    multioutput_regressor.fit(\n        x_train, y_train,\n        eval_set=(x_test, y_test),\n        eval_metric=custom_average_precision,\n        early_stopping_rounds=15,\n        verbose = 0,\n    )\n    \n    # 将训练好的模型添加到列表 regs 中。\n    regs.append(multioutput_regressor)\n    \n    # 使用测试集数据计算平均精度，并将其添加到列表 cvs 中。clip 方法用于将预测结果限制在 0 和 1 之间，避免出现无效结果。\n    cv = metrics.average_precision_score(y_test, multioutput_regressor.predict(x_test).clip(0.0,1.0))\n    \n    cvs.append(cv)\n    \nprint(cvs)\nprint(np.mean(cvs))","metadata":{"execution":{"iopub.execute_input":"2023-04-21T01:39:16.147155Z","iopub.status.busy":"2023-04-21T01:39:16.146814Z","iopub.status.idle":"2023-04-21T01:51:08.031261Z","shell.execute_reply":"2023-04-21T01:51:08.029721Z","shell.execute_reply.started":"2023-04-21T01:39:16.147123Z"},"papermill":{"duration":943.279901,"end_time":"2023-04-16T23:06:12.366865","exception":false,"start_time":"2023-04-16T22:50:29.086964","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub = pd.read_csv(path.join(root, 'sample_submission.csv'))\nsubmission = []\n\nfor f in test:\n    df = pd.read_csv(f)\n    # 将df的索引设置为'Time'列，drop=True表示不保留'Time'列，inplace=True表示直接在原df上修改\n    df.set_index('Time', drop=True, inplace=True)\n\n    # 根据文件路径f，给df添加一个名为'Id'的新列，该列的值为文件名（不包含扩展名）\n    df['Id'] = f.split('/')[-1].split('.')[0]\n\n    # 获取文件路径f中倒数第二个部分（即倒数第二个目录名），并将其赋值给变量dataset\n    dataset = Path(f).parts[-2]\n        \n    \n    # 将df的索引标准化，即将索引除以索引中的最大值，并将结果存储到名为'Time_frac'的新列中\n    df['Time_frac']=(df.index/df.index.max()).values\n    # 将df和名为tasks的DataFrame对象进行左连接，连接关键字为'Id'，连接方式为'left'，然后用-1填充缺失值\n    df = pd.merge(df, tasks[['Id','t_group']], how='left', on='Id').fillna(-1)\n\n    # 将df和名为metadata_w_subjects的DataFrame对象进行左连接，连接关键字为'Id'，连接方式为'left'，然后用-1填充缺失值\n    df = pd.merge(df, metadata_w_subjects[['Id','Subject', 'Visit','Test','Medication','s_group']], how='left', on='Id').fillna(-1)\n    # 调用名为fc.calculate的函数，计算df的特征，返回一个DataFrame对象，存储到df_feats变量中\n    df_feats = fc.calculate(df, return_df=True, include_final_window=True, approve_sparsity=True, window_idx=\"begin\")\n    # 将df和df_feats进行左连接，连接键为索引列，连接方式为'left'，并将结果存储到df中\n    df = df.merge(df_feats, how=\"left\", left_index=True, right_index=True)\n    # 用前面的非空值填充缺失值\n    df.fillna(method=\"ffill\", inplace=True)\n\n        \n    res_vals = []\n    \n    # 首先是一个 for 循环，其中 i_fold 从 0 到 4 进行循环，即执行五次，用来进行五折交叉验证。\n    for i_fold in range(5):\n        \n        # 对于每一次循环，pred 是使用第 i_fold 折的模型进行预测得到的结果，regs[i_fold] 表示第 i_fold 折的模型，df[cols] 是用于预测的数据，.clip(0.0,1.0) 的作用是将预测结果限制在 0 到 1 的范围内。\n        pred = regs[i_fold].predict(df[cols]).clip(0.0,1.0)\n        # 将每个模型的预测结果加入一个列表 res_vals 中，np.expand_dims 是用于将维度扩展的函数，这里的作用是将 pred 扩展为 3 维，在第三维度上进行拼接。\n        res_vals.append(np.expand_dims(np.round(pred, 3), axis = 2))\n    \n    # 将五个模型的预测结果进行平均，np.concatenate 是用于拼接数组的函数，axis = 2 表示在第三维进行拼接，即将五个预测结果拼接为一个数组，然后在第三维度上进行平均值计算，得到最终的预测结果 res_vals。\n    res_vals = np.mean(np.concatenate(res_vals, axis = 2), axis = 2)\n    # 将预测结果转化为 DataFrame 格式，并且指定列名为 pcols。\n    res = pd.DataFrame(res_vals, columns=pcols)\n    \n    # 将预测结果合并到原始数据 df 中\n    df = pd.concat([df,res], axis=1)\n    # 将 df 中的 Id 列和索引值进行拼接，作为最终提交的 ID\n    df['Id'] = df['Id'].astype(str) + '_' + df.index.astype(str)\n    # 将 df 中的预测结果加入到 submission 列表中，用于最终的提交。\n    submission.append(df[scols])\n    \nsubmission = pd.concat(submission)\nsubmission = pd.merge(sub[['Id']], submission, how='left', on='Id').fillna(0.0)\nsubmission[scols].to_csv('submission.csv', index=False)","metadata":{"execution":{"iopub.execute_input":"2023-04-21T01:51:08.034312Z","iopub.status.busy":"2023-04-21T01:51:08.033808Z","iopub.status.idle":"2023-04-21T01:51:16.946572Z","shell.execute_reply":"2023-04-21T01:51:16.945259Z","shell.execute_reply.started":"2023-04-21T01:51:08.034249Z"},"papermill":{"duration":10.790056,"end_time":"2023-04-16T23:06:23.20594","exception":false,"start_time":"2023-04-16T23:06:12.415884","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission","metadata":{"execution":{"iopub.execute_input":"2023-04-21T01:51:16.949566Z","iopub.status.busy":"2023-04-21T01:51:16.948492Z","iopub.status.idle":"2023-04-21T01:51:16.967779Z","shell.execute_reply":"2023-04-21T01:51:16.966552Z","shell.execute_reply.started":"2023-04-21T01:51:16.949502Z"},"papermill":{"duration":0.076052,"end_time":"2023-04-16T23:06:23.331235","exception":false,"start_time":"2023-04-16T23:06:23.255183","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]}]}