9.2 pandas与sklearn
9.2.2 数据预处理
- 缺失值处理
# 各列缺失值数量
df.count()
df.isna().sum() # df.notna()
# 各列缺失值比例
df.isnull().mean() * 100
# 缺失值可视化
import missingno as msno
import matplotlib.pyplot as plt
msno.matrix(df) # 矩阵图:直观展示缺失值在数据中的分布
plt.show()
msno.bar(df) # 条形图:按列展示完整数据比例
plt.show()
# 删除缺失值
# axis表示要压缩的维度。若axis=0,则压缩行,保留列方向的操作结果
df = df.dropna() # 删除包含任何缺失值的行
df = df.dropna(axis=1, how='all') # 删除全是缺失值的列
df = df.dropna(subset=["col1", "col2"]) # 删除特定列缺失值所在的行
# 填充缺失值
df_filled = df.fillna(0) # 用固定值填充
df['A'] = df['A'].fillna(df['A'].mean()) # 均值
df['B'] = df['B'].fillna(df['B'].median()) # 中位数
df = df.ffill() # 前向填充
df = df.bfill() # 后向填充
df = df.interpolate() # 用插值法填充(更平滑的填充方式)- 异常值处理
# Z-score
df_num = df.select_dtypes(include=['number']) # 筛选数值列
df_zscore = df_num.apply(lambda col:(col-col.mean())/col.std(), axis = 0) # df_zscore是数据框
df[(df_zscores.abs() > 2).any(axis = 1)]
# 四分位距
Q1 = num_df.quantile(0.25)
Q3 = num_df.quantile(0.75)
IQR = Q3 - Q1
outliers = ((num_df < (Q1 - 1.5 * IQR)) | (num_df > (Q3 + 1.5 * IQR)))
df[df_num.columns] = df_num.clip(lower=Q1, upper=Q3, axis = 1)- 标准化
from sklearn.preprocessing import StandardScaler
# from sklearn.preprocessing import MinMaxScaler
# 选取数值列
numeric_cols = df.select_dtypes(include=['number']).columns
# 标准化并替换
scaler = StandardScaler() # scaler = MinMaxScaler()
df[numeric_cols] = scaler.fit_transform(df[numeric_cols])- 编码
# 独热编码
df = pd.get_dummies(df, dtype = int)
# 也可考虑from sklearn.preprocessing import OneHotEncoder
# 哑变量编码
df = pd.get_dummies(df, dtype = int, drop_first=True)
# 频率编码
frequency = df['A'].value_counts(normalize=True)
df['A'] = df['A'].map(frequency)
# 有序变量编码
df['评分'] = pd.Categorical(
df['评分'],
categories=['低', '中', '高', '极高'],
ordered=True
)
df['评分编码'] = df['评分'].cat.codes
# 自定义编码
edu_map = {'小学': 1, '中学': 2, '大学': 3, '硕士': 4, '博士': 5}
df['edu'] = df['edu'].map(edu_map)- 表的连接与拼接
# on表示主键
# how表示连接方式:inner、outer、left、right
df_merge = df1.merge(df2, on='id', how='inner')
df_merge = pd.merge(df1, df2, on='id', how='inner')
# 当键名不同时
df = pd.merge(df1, df2, left_on='id', right_on='user_id', how='inner')
# 表拼接
# ignore_index=True表示充值索引
df_concat = pd.concat([df1, df2], axis = 0) # 纵向堆叠
df_concat = pd.concat([df1, df2], axis = 1) # 横向堆叠- 数据透视表
.pivot()和pd.melt()都不会保留未用到的列,得注意。
# 长表变宽表
wide = df.pivot(index='name', columns='subject', values='score').reset_index() # 将索引列转化为第一列
wide = wide.merge(df[['name', 'age']].drop_duplicates(), on='name', how='left') # 保留原始列
# 若index存在重复值,则考虑pivot_table(),可以指定处理重复值的聚合函数
# 宽表变长表
# 参数value_vars是个列表,存储需要压缩的列名,如果不填,默认除id_vars外的所有列
pd.melt(wide, id_vars=['name', 'age'], var_name='subject', value_name='score')- 分组聚合
# 核心表达式
DataFrame.groupby(keys)[columns].agg(func) # 如果是多个列则columns应该再用列表嵌套
# 单函数聚合
df.groupby('region')['revenue'].agg('sum')
# 多函数聚合
df.groupby('region')[['revenue', 'unit']].agg(['sum', 'mean'])
# 字典映射(只对特定列进行聚合):列名为键,函数为值
df.groupby('region').agg({
'revenue': 'sum',
'units': 'mean'
})
# 命名聚合:新列名=('旧列名', '函数名')
df.groupby('region').agg(
total_revenue=('revenue', 'sum'),
avg_units=('units', 'mean'),
max_revenue=('revenue', 'max')
)
# 自定义函数
df.groupby('region')['revenue'].agg(lambda x: x.max() - x.min())9.2.3 分割数据集
from sklearn.model_selection import train_test_split
# 训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 训练集和验证集
X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.25, random_state=42)
# 分层分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify= categorical_var)
# 交叉验证
from sklearn.model_selection import KFold
kf = KFold(n_splits=10, shuffle=True, random_state=42)
for train_index, test_index in kf.split(X, y):
# 获取分割后的训练集和测试集
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
...
# 也可使用cross_val_score及cross_validate来简化交叉验证流程
cross_val_score(model, X, y, cv=5, scoring='accuracy') # 单个评估指标
cross_validate(model, X, y, cv=5, scoring=['accuracy', 'f1_macro'], return_train_score=True) # 多个评估指标