
1. 理解数据塑形的核心需求数据科学项目中80%的时间都花在数据预处理上这句话你可能听过无数次。但真正开始处理一个具体项目时才会明白为什么数据塑形如此重要。想象你正在准备一顿晚餐食材买回来了但有的需要切块有的需要切片还有的需要腌制——数据预处理就是这样的过程。上周我处理过一个电商用户行为数据集原始数据就像一堆杂乱无章的食材。模型要求输入必须是特定维度的张量而原始数据却是二维表格分类特征还是A/B/C这样的字符串数值特征的尺度差异大到离谱。这时候就需要np.newaxis来调整维度pd.get_dummies来处理分类变量配合fit/norm/hist来验证数据分布。为什么这些操作如此关键以最简单的神经网络为例输入层的神经元数量是固定的如果你的数据维度不匹配就像试图把方形积木塞进圆形孔洞。我在第一次尝试时忽略了维度匹配结果模型直接报错维度不匹配调试了两小时才发现问题。2. np.newaxis的维度魔法2.1 基础用法与原理第一次看到np.newaxis时我觉得这命名真是直白得可爱——新轴。它的作用就是在指定位置插入一个新维度。来看这个实际案例import numpy as np price_data np.random.rand(100) # 100个产品价格 print(price_data.shape) # 输出 (100,)假设我们要用这个数据训练LSTM模型但LSTM要求输入形状是(样本数, 时间步, 特征数)。我们的数据只有(100,)怎么办# 错误示范直接reshape会丢失维度信息 reshaped price_data.reshape(100, 1) print(reshaped.shape) # (100, 1) # 正确做法使用np.newaxis expanded price_data[:, np.newaxis] # 等同于price_data.reshape(100, 1) print(expanded.shape) # (100, 1)虽然这个简单例子中reshape也能达到同样效果但在复杂操作链中np.newaxis的可读性更好。我在处理3D医学影像数据时经常需要这样扩展维度mri_scan np.random.rand(256, 256) # 单张MRI切片 volumetric_data mri_scan[np.newaxis, ..., np.newaxis] print(volumetric_data.shape) # (1, 256, 256, 1) 适合输入3D CNN2.2 高级应用场景在图像处理中经常需要批量处理图片。假设我们有100张RGB图片(每张256x256)常规存储是(100,256,256,3)。但某些框架要求通道在前(100,3,256,256)。这时候np.newaxis就派上用场了images np.random.rand(100, 256, 256, 3) # 错误的转置方式会打乱数据 # 正确做法 channels_first images.transpose(0, 3, 1, 2)更复杂的例子是处理视频数据。上周我处理过一个体育动作识别项目原始数据是(视频数,帧数,高,宽,通道)。但模型需要(批次,帧数,通道,高,宽)。通过组合np.newaxis和transpose我们实现了完美转换video_data np.random.rand(10, 30, 1080, 1920, 3) # 10个视频每个30帧 processed video_data[:, :, np.newaxis, ...].transpose(0,1,4,2,3) print(processed.shape) # (10, 30, 3, 1080, 1920)3. 数据排序与分布分析3.1 使用np.argsort进行智能排序argsort是我最喜欢用的NumPy函数之一。它不直接排序数据而是返回排序后的索引。这在处理关联数据时特别有用。比如电商场景中我们既要按价格排序又要保持价格与产品ID的对应关系product_ids np.array([1001, 1002, 1003, 1004]) prices np.array([299, 599, 199, 899]) sort_indices np.argsort(prices) print(排序索引:, sort_indices) # [2 0 1 3] print(按价格排序的产品ID:, product_ids[sort_indices]) # [1003 1001 1002 1004]在推荐系统中我经常用argsort来获取Top-N推荐。比如用户-物品评分矩阵中对每个用户找出评分最高的3个物品user_ratings np.random.rand(1000, 100) # 1000用户对100物品的评分 top3_indices np.argsort(user_ratings, axis1)[:, -3:]3.2 可视化验证数据分布数据质量决定模型上限。我习惯在预处理前后都检查数据分布。上周分析金融交易数据时发现原始金额呈现严重长尾分布import seaborn as sns import matplotlib.pyplot as plt from scipy.stats import norm transactions np.random.exponential(scale1000, size1000) sns.distplot(transactions, fitnorm) plt.title(原始交易金额分布) plt.show()经过对数变换后数据更接近正态分布log_trans np.log1p(transactions) sns.distplot(log_trans, fitnorm) plt.title(对数变换后分布) plt.show()对于高维数据我常用pairplot快速检查特征间关系df pd.DataFrame(np.random.randn(1000, 4), columns[age, income, spending, savings]) sns.pairplot(df) plt.show()4. 分类特征编码实战4.1 get_dummies基础应用分类变量是结构化数据中的常客。上周处理用户数据集时遇到职业字段有12种取值。直接喂给模型且慢字符串输入会让大多数模型直接报错。users pd.DataFrame({ age: [25, 32, 45], occupation: [teacher, engineer, teacher] }) dummies pd.get_dummies(users, columns[occupation]) print(dummies)输出结果age occupation_engineer occupation_teacher 0 25 0 1 1 32 1 0 2 45 0 14.2 处理高基数分类变量当分类变量取值很多时如城市名直接get_dummies会导致维度爆炸。这时我通常将低频类别合并为其他使用均值编码或目标编码考虑嵌入层(深度学习场景)cities [北京]*300 [上海]*250 [广州]*200 [深圳]*150 [杭州]*100 [其他]*50 df pd.DataFrame({city: np.random.choice(cities, 1000)}) # 方法1只保留高频城市 top_cities df[city].value_counts().nlargest(5).index df[city] df[city].where(df[city].isin(top_cities), 其他) # 方法2均值编码(假设有目标变量y) df[y] np.random.randint(0, 2, size1000) mean_encoding df.groupby(city)[y].mean() df[city_encoded] df[city].map(mean_encoding)4.3 处理未见过的类别生产环境中常遇到训练时没见过的类别。好的预处理应该能处理这种情况# 训练数据 train pd.DataFrame({color: [red, blue, green]}) # 测试数据可能出现新颜色 test pd.DataFrame({color: [red, yellow]}) # 安全做法先获取所有可能取值 all_colors train[color].unique() dummies_train pd.get_dummies(train.assign( colorlambda x: x[color].where(x[color].isin(all_colors), other) )) dummies_test pd.get_dummies(test.assign( colorlambda x: x[color].where(x[color].isin(all_colors), other) ))5. 完整数据处理流水线示例让我们把这些技术串联起来处理一个真实场景的数据集。假设我们有一组房屋数据import pandas as pd import numpy as np # 模拟数据 np.random.seed(42) data { area: np.random.normal(100, 30, 1000), rooms: np.random.randint(1, 6, 1000), district: np.random.choice([A, B, C, D], 1000), price: np.random.normal(500000, 150000, 1000) } df pd.DataFrame(data) # 1. 调整数值特征维度 X_num df[[area, rooms]].values X_num X_num[:, :, np.newaxis] # 从(1000,2)变为(1000,2,1) # 2. 处理分类特征 X_cat pd.get_dummies(df[district], prefixdist) # 3. 标准化数值特征 from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_num_scaled scaler.fit_transform(X_num.squeeze())[..., np.newaxis] # 4. 合并特征 X_processed np.concatenate([ X_num_scaled, X_cat.values[:, :, np.newaxis].squeeze() ], axis1) # 5. 验证分布 plt.figure(figsize(12, 6)) plt.subplot(121) sns.distplot(df[price], fitnorm) plt.title(原始价格分布) plt.subplot(122) sns.distplot(np.log1p(df[price]), fitnorm) plt.title(对数变换后分布) plt.show()这个流程展示了如何将各种数据塑形技术组合使用。在实际项目中我通常会把这个流程封装成可复用的预处理类特别是当需要处理多个相似数据集时。