保姆级教程:用Python和Pandas快速上手UJIIndoorLoc室内定位数据集
Python实战:UJIIndoorLoc室内定位数据集的深度解析与Pandas高效处理
在室内导航技术快速发展的今天,WLAN指纹定位作为成本效益最高的解决方案之一,正受到越来越多研究者和开发者的关注。而UJIIndoorLoc作为该领域最具代表性的开放数据集,为机器学习初学者和数据科学家提供了绝佳的学习素材。本文将带你从零开始,在Jupyter Notebook环境中使用Python和Pandas,深入探索这个包含520个WAP信号强度、经纬度坐标和建筑信息的多维数据集。
1. 环境准备与数据加载
在开始数据分析之前,我们需要搭建一个合适的工作环境。推荐使用Anaconda创建独立的Python环境,避免依赖冲突:
conda create -n indoor_loc python=3.8 pandas numpy matplotlib seaborn jupyter conda activate indoor_loc数据集可以从UCI机器学习仓库直接下载,解压后会得到两个关键文件:
trainingData.csv:包含19,937条训练样本validationData.csv:包含1,111条验证样本
使用Pandas加载数据时,建议指定low_memory=False参数以避免类型推断问题:
import pandas as pd train_df = pd.read_csv('trainingData.csv', low_memory=False) valid_df = pd.read_csv('validationData.csv', low_memory=False)初次加载后,我们可以快速检查数据的基本结构:
print(f"训练集形状: {train_df.shape}") print(f"验证集形状: {valid_df.shape}") print("\n训练集列名示例:") print(train_df.columns[:10]) # 显示前10列名称2. 理解数据列结构与WAP信号特征
UJIIndoorLoc数据集的核心是520个WAP(Wireless Access Point)的信号强度测量值,这些列被命名为WAP001到WAP520。理解这些信号强度的编码方式至关重要:
- 信号强度范围:-104dBm到0dBm表示实际测量到的信号强度,数值越小信号越弱
- 特殊值+100:表示在该位置未检测到对应的WAP
- 信号强度分布:大多数有效信号集中在-90dBm到-30dBm之间
我们可以使用以下代码快速分析WAP列的基本统计信息:
# 提取所有WAP列 wap_cols = [col for col in train_df.columns if col.startswith('WAP')] # 计算每列非+100值的数量 wap_detection_counts = (train_df[wap_cols] != 100).sum() print(f"检测到信号最多的WAP: {wap_detection_counts.idxmax()} ({wap_detection_counts.max()}次)") print(f"检测到信号最少的WAP: {wap_detection_counts.idxmin()} ({wap_detection_counts.min()}次)")为了更直观地理解信号分布,可以绘制热力图:
import seaborn as sns import matplotlib.pyplot as plt # 随机选取50个WAP列绘制信号强度分布 sample_waps = np.random.choice(wap_cols, 50, replace=False) plt.figure(figsize=(12, 6)) sns.heatmap(train_df[sample_waps].replace(100, np.nan), cmap="viridis") plt.title("50个随机WAP的信号强度热力图") plt.show()3. 地理位置数据的处理与分析
数据集中的地理位置信息采用特殊的坐标系统表示,需要特别注意:
| 列名 | 描述 | 值范围 | 转换方法 |
|---|---|---|---|
| LONGITUDE | 经度 | -7695.93到-7299.78 | 实际值 = 原始值/10^6 |
| LATITUDE | 纬度 | 4864745.74到4865017.36 | 实际值 = 原始值/10^6 |
| FLOOR | 楼层 | 0到4 | 直接使用 |
| BUILDINGID | 建筑ID | 0到2 | 直接使用 |
转换坐标到实际值的Python实现:
train_df['LONGITUDE'] = train_df['LONGITUDE'] / 1e6 train_df['LATITUDE'] = train_df['LATITUDE'] / 1e6建筑和楼层分布的可视化:
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5)) # 建筑分布饼图 building_counts = train_df['BUILDINGID'].value_counts() ax1.pie(building_counts, labels=building_counts.index, autopct='%1.1f%%') ax1.set_title('建筑分布比例') # 楼层分布柱状图 floor_counts = train_df['FLOOR'].value_counts().sort_index() ax2.bar(floor_counts.index, floor_counts.values) ax2.set_title('楼层分布') ax2.set_xlabel('楼层') ax2.set_ylabel('样本数量') plt.show()4. 高级数据分析技巧
4.1 基于用户ID的时间序列分析
数据集包含USERID和TIMESTAMP列,这让我们可以分析不同用户的移动模式:
# 将时间戳转换为可读格式 train_df['DATETIME'] = pd.to_datetime(train_df['TIMESTAMP'], unit='s') # 选取一个用户进行分析 sample_user = train_df[train_df['USERID'] == 6].sort_values('DATETIME') # 绘制用户移动轨迹 plt.figure(figsize=(10, 6)) plt.scatter(sample_user['LONGITUDE'], sample_user['LATITUDE'], c=sample_user['FLOOR'], cmap='viridis', s=50) plt.colorbar(label='楼层') plt.title('用户6的移动轨迹') plt.xlabel('经度') plt.ylabel('纬度') plt.grid(True) plt.show()4.2 信号强度与位置关系的三维分析
使用PCA降维技术可视化WAP信号与位置的关系:
from sklearn.decomposition import PCA # 准备数据 - 替换+100为NaN wap_data = train_df[wap_cols].replace(100, np.nan) # 填充缺失值为最小信号强度 wap_filled = wap_data.fillna(-104) # 应用PCA降维到3维 pca = PCA(n_components=3) wap_pca = pca.fit_transform(wap_filled) # 3D散点图 from mpl_toolkits.mplot3d import Axes3D fig = plt.figure(figsize=(12, 8)) ax = fig.add_subplot(111, projection='3d') scatter = ax.scatter(wap_pca[:,0], wap_pca[:,1], wap_pca[:,2], c=train_df['BUILDINGID'], cmap='tab10', s=20) plt.colorbar(scatter, label='建筑ID') ax.set_title('WAP信号PCA降维(3D)按建筑着色') plt.show()4.3 数据清洗与特征工程实战
在实际建模前,我们需要进行一系列数据预处理:
# 1. 处理WAP信号列 # 将+100替换为NaN,然后填充为比最小信号更弱的值 wap_processed = train_df[wap_cols].replace(100, np.nan) wap_processed = wap_processed.fillna(-110) # 比-104更弱的值表示未检测到 # 2. 标准化信号强度 from sklearn.preprocessing import MinMaxScaler scaler = MinMaxScaler(feature_range=(0, 1)) wap_scaled = scaler.fit_transform(wap_processed) # 3. 创建新特征 - 检测到的WAP数量 train_df['WAP_COUNT'] = (train_df[wap_cols] != 100).sum(axis=1) # 4. 创建新特征 - 平均信号强度(仅计算检测到的WAP) train_df['AVG_RSSI'] = train_df[wap_cols].replace(100, np.nan).mean(axis=1) # 查看新特征的分布 fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5)) sns.histplot(train_df['WAP_COUNT'], bins=30, ax=ax1) ax1.set_title('每个位置检测到的WAP数量分布') sns.histplot(train_df['AVG_RSSI'], bins=30, ax=ax2) ax2.set_title('平均信号强度分布') plt.show()5. 构建基础定位模型
作为数据分析的收尾,我们可以尝试构建一个简单的楼层分类模型:
from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report # 准备特征和目标变量 X = wap_scaled y = train_df['FLOOR'] # 划分训练/测试集 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42) # 训练随机森林模型 rf = RandomForestClassifier(n_estimators=100, random_state=42) rf.fit(X_train, y_train) # 评估模型 y_pred = rf.predict(X_test) print(classification_report(y_test, y_pred))模型评估结果显示,即使是简单的随机森林也能在楼层分类上达到不错的准确率。在实际项目中,我们可以进一步:
- 尝试更复杂的模型架构
- 加入位置坐标作为目标进行回归
- 使用交叉验证优化超参数
- 部署为实时定位服务API
