如何在PyTorch中将OhioT1DM数据集的XML文件转为张量?
实现步骤:从OhioT1DM XML数据到模型输入张量
下面是从数据集读取、解析到张量转换的完整流程,适配你的需求:
1. 导入依赖库
先把需要的工具库导入:
import os import xml.etree.ElementTree as ET import pandas as pd import torch from sklearn.preprocessing import LabelEncoder, StandardScaler from torch.nn.utils.rnn import pad_sequence
2. 批量收集XML文件路径
写个工具函数,一次性收集2018和2020年指定split(train/test)下的所有XML文件:
DATA_ROOT = "dataset" def collect_xml_files(target_years, split_type): xml_paths = [] for year in target_years: split_dir = os.path.join(DATA_ROOT, str(year), split_type) for filename in os.listdir(split_dir): if filename.endswith(".xml"): xml_paths.append(os.path.join(split_dir, filename)) return xml_paths # 收集训练集和测试集文件 train_xmls = collect_xml_files([2018, 2020], "train") test_xmls = collect_xml_files([2018, 2020], "test")
3. 解析XML文件为结构化数据
遍历所有XML文件,提取患者基本信息和各类事件数据,整理成统一格式:
def parse_single_xml(xml_path): tree = ET.parse(xml_path) root = tree.getroot() patient_base = root.attrib # 提取患者id、weight、insulin_type等属性 patient_id = patient_base["id"] all_events = [] # 处理临时基础胰岛素事件(temp_basal) temp_basal_node = root.find("temp_basal") if temp_basal_node: for event in temp_basal_node.findall("event"): all_events.append({ "patient_id": patient_id, "event_type": "temp_basal", "ts": event.attrib["ts_begin"], "ts_end": event.attrib["ts_end"], "value": float(event.attrib["value"]), **patient_base }) # 处理餐食事件(meal) meal_node = root.find("meal") if meal_node: for event in meal_node.findall("event"): all_events.append({ "patient_id": patient_id, "event_type": "meal", "ts": event.attrib["ts"], "meal_type": event.attrib["type"], "carbs": float(event.attrib["carbs"]), **patient_base }) # 可以继续添加其他事件类型的解析(比如血糖、大剂量胰岛素等) return all_events # 解析所有训练数据 train_raw_data = [] for path in train_xmls: train_raw_data.extend(parse_single_xml(path)) # 转成DataFrame,方便后续预处理 train_df = pd.DataFrame(train_raw_data)
4. 数据预处理
把原始数据转换成模型能接受的格式:
# 1. 处理时间特征:把字符串转成datetime格式,方便排序/提取时序特征 def parse_time_str(time_str): return pd.to_datetime(time_str, format="%d-%m-%Y %H:%M:%S") train_df["ts"] = train_df["ts"].apply(parse_time_str) train_df["ts_end"] = train_df["ts_end"].fillna(pd.NaT).apply(lambda x: parse_time_str(x) if pd.notna(x) else x) # 2. 编码类别特征(比如胰岛素类型、餐食类型) le_insulin = LabelEncoder() train_df["insulin_type_encoded"] = le_insulin.fit_transform(train_df["insulin_type"]) le_meal = LabelEncoder() # 仅对餐食事件编码,非餐食事件填充默认值 train_df["meal_type_encoded"] = train_df["meal_type"].apply( lambda x: le_meal.fit_transform([x])[0] if pd.notna(x) else 0 ) # 3. 归一化数值特征(weight、value、carbs) scaler = StandardScaler() numeric_cols = ["weight", "value", "carbs"] train_df[numeric_cols] = scaler.fit_transform(train_df[numeric_cols].fillna(0))
5. 转换为张量
根据你的模型类型(时序模型/普通MLP),选择不同的转换方式:
方式1:时序模型(如LSTM)输入
按患者分组,构建时序序列并做padding:
# 按患者分组,每个组内按时间排序 patient_groups = train_df.groupby("patient_id") sequences = [] feature_cols = ["insulin_type_encoded", "meal_type_encoded", "weight", "value", "carbs"] for _, group in patient_groups: sorted_group = group.sort_values("ts") seq_tensor = torch.tensor(sorted_group[feature_cols].values, dtype=torch.float32) sequences.append(seq_tensor) # 对序列做padding,统一长度 padded_sequences = pad_sequence(sequences, batch_first=True) # padded_sequences形状:[患者数量, 最长序列长度, 特征数量]
方式2:普通MLP输入
把每个事件作为独立样本,直接转换:
train_features = train_df[feature_cols].values train_tensor = torch.tensor(train_features, dtype=torch.float32) # train_tensor形状:[事件总数, 特征数量]
6. 输入模型示例
以LSTM为例,简单搭建模型并输入数据:
class T1DMLSTM(nn.Module): def __init__(self, input_size, hidden_size, num_layers, output_size): super().__init__() self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True) self.fc = nn.Linear(hidden_size, output_size) def forward(self, x): out, _ = self.lstm(x) # 取每个序列最后一个时间步的输出做预测 return self.fc(out[:, -1, :]) # 初始化模型 model = T1DMLSTM(input_size=padded_sequences.shape[2], hidden_size=64, num_layers=2, output_size=1) # 前向传播 output = model(padded_sequences)
内容的提问来源于stack exchange,提问作者Pegtomous
相关产品推荐
相关产品推荐

