机器学习相关操作方法分享(一)
目录
1 数据简介及数据读取<br>2 数据清洗<br>3 探索性数据分析<br> 3.1 各部门团队的组成情况<br> 3.2 员工生产率分布情况<br> 3.3. 数据相关性分析<br>4 特征工程<br>5 预测员工生产率<br> 5.1 训练集测试集划分<br> 5.2 线性回归<br> 5.3 回归决策树<br> 5.4 Bagging回归<br>6 总结<br>
<div id="1"></div>
1 数据简介及数据读取
服装厂员工生产率数据集共包含1197行、15列,其中actual_productivity
一列为我们最终需要进行预测的员工实际生产率,各字段类型和含义如下表所示:
date | 字符型 | 日期 MM-DD-YYYY |
quarter | 字符型 | 每月第几周 |
department | 字符型 | 部门 |
day | 字符型 | 星期 |
team | 整型 | 团队编号 |
targeted_productivity | 浮点型 | 目标生产率 |
smv | 浮点型 | 标准分钟值,任务分配的时间 |
wip | 浮点型 | 在制品,包括产品未完成项的数量 |
over_time | 整型 | 代表每个团队的超时时间,以分钟为单位 |
incentive | 整型 | 激励特定行动的奖金 |
idle_time | 浮点型 | 由于多种原因导致生产中断的时间 |
idle_men | 整型 | 因生产中断而闲置的工人数 |
no_of_style_change | 整型 | 特定产品风格的变化次数 |
no_of_workers | 浮点型 | 每个团队的工人数量 |
actual_productivity | 浮点型 | 工人交付的实际生产率百分比,取值范围是0-1 |
首先,我们使用Pandas库读取数据,并查看数据的基本信息。 | ||
import pandas as pd | ||
import numpy as np | ||
import seaborn as sns | ||
import matplotlib.pyplot as plt |
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
plt.rcParams['font.family'] = ['sans-serif']
import warnings
不显示警告
warnings.filterwarnings('ignore')
读取数据,查看数据前五行
garments = pd.read_csv('./input/garments_worker_productivity.csv')garments.loc[garments.department=='finishing ', 'department'] = 'finishing'garments.head()cols = [f for f in garments.columns if f not in ['date','quarter','department','day']]garments[cols].corr()garments.info()数据集共包含1197行、15列,其中date
、quarter
、department
和day
字段数据类型为object,其余为数字类型。同时可以发现,wip
一列存在缺失值。下面我们查看一下数据的基本统计信息:garments['quarter'].value_counts()garments.describe(include='all')从结果中可以看出actual_productivity
实际生产率的最大值为1.120437,而该字段实际取值范围为0-1,后续我们会对其进行处理。<div id="2"></div>
2 数据清洗
在上一部分我们发现wip
一列存在缺失值,在处理之前我们先计算一下缺失值的比例:
查看wip缺失值比例
round(garments.wip.isnull().sum()/len(garments.wip),2)wip
中有42%的缺失值,由于占比较大,这里我们直接删去该字段。
删去wip这一列
garments.drop(columns=['wip'], inplace=True)接下来使用pd.to_datetime()
函数将date
一列数据类型改为日期类型,并将其设置为索引:
将日期转换为时间类型,设置为索引
garments.date = pd.to_datetime(garments.date)
garments.set_index(['date'],inplace=True)
查看日期范围
garments.index可以看到数据是从2015年1月1日到2015年3月11日期间收集的。我们根据日期在数据集中添加月份month
一列:
添加月份
garments['month'] = garments.index.month_name().valuesgarments.head()从数据中发现,over_time
每个团队的超时时间字段数值较大,这是由于该字段以分钟为单位,为了便于观察我们将单位转化为小时。
将超时时间over_time改为以小时为单位
garments['over_time'] = garments['over_time']/60garments.head()数据中team
团队编号一列为数值型,而实际分析中应将其视为离散型变量,这里使用astype
方法将其转为object
。
将团队编号team类型改为object
garments['team'] = garments['team'].astype('object')现在,我们开始分析数据中是否存在异常值,通过观察发现,department
部门字段中的唯一值存在异常:
查看部门字段的唯一取值
garments['department'].value_counts()garments['department'].unique()garments.groupby(['department'])[['targeted_productivity','actual_productivity']].agg({'mean','max','min'})该字段的取值出现拼写错误及空格问题,我们将该列的取值进行统一:sewing
为缝纫部门,finishing
为精加工车间。
# 修改为finishing和sewing
garments['department'] = garments.department.apply(lambda x: 'finishing' if 'finishing' in x else 'sewing')
garments['department'].value_counts()
数据中缝纫部有691条记录,精加工车间有506条记录。最后,我们将actual_productivity
实际生产率大于1的值统一为1。
将生产率大于1的设置为1
garments.actual_productivity = garments.actual_productivity.apply(lambda x: 1 if x>1 else x)
<div id="3"></div>
3 探索性数据分析
在这一部分,我们将结合可视化分析方法对数据进行初步的了解,首先查看各部门团队的组成情况,及记录数量的比例。<div id="3.1"></div>
3.1 各部门团队的组成情况
分组聚合统计各部门各个团队的记录数量
team = garments.groupby(['department','team'])['team'].count()teamgarments.groupby(['department','team'])[['targeted_productivity','actual_productivity']].agg({'mean','max','min'})team_c = round(garments.groupby(['department','team'])['no_of_workers'].mean())team_c
3.2 员工生产率分布情况
在分析完各部门团队组成之后,我们将一起来观察各部门生产率的分布情况。
设置颜色
my_colors = ["#F1B5B9", "#AAC8D1"]sns.set_palette( my_colors )
fig, ax =plt.subplots(figsize=(8,4))
dpt_g = garments.groupby('department')['actual_productivity']
sns.distplot(dpt_g.get_group('finishing'),label='精加工车间')sns.distplot(dpt_g.get_group('sweing'), label='缝纫部')
plt.title('各部门生产率分布情况',size=15)plt.legend()plt.xlabel('生产率', size=12)plt.ylabel('频次', size=12)
plt.show()两部门生产率整体均集中于0.6至1之间,同时较少员工生产率低于0.5。相比之下,缝纫部员工更多集中在0.8左右,精加工车间较多员工生产率接近于1,但整体分布较为分散。下面我们通过绘制箱线图,查看两部门各个团队生产率的分散情况。fig, ax =plt.subplots(figsize=(12,4))
sns.boxplot(x='team',y='actual_productivity',data=garments,hue='department')
plt.title('各团队生产率分散情况',size=15)plt.xlabel('团队编号', size=12)plt.ylabel('生产率', size=12)plt.legend(loc='lower left')
plt.show()从上图可知,缝纫部各团队生产率均比较集中,但同时存在较多生产率偏低的异常点。精加工车间中团队6和11生产率最分散,团队1-5、10、11和12生产率均较高。接着我们将探究不同时间对员工生产率的影响。fig, ax =plt.subplots(3,1,figsize=(8,10))
设置颜色
my_colors = ["#F1B5B9", "#AAC8D1"]sns.set_palette( my_colors )
sns.boxplot(x='day',y='actual_productivity',data=garments,hue='department', order=['Monday','Tuesday','Wednesday','Thursday','Saturday','Sunday'], ax=ax[0])sns.boxplot(x='quarter',y='actual_productivity',data=garments,hue='department', ax=ax[1])sns.boxplot(x='month',y='actual_productivity',data=garments,hue='department', ax=ax[2])
ax[0].set_title('不同星期对员工生产率的影响',size=15)ax[1].set_title('每月各周对员工生产率的影响',size=15)ax[2].set_title('不同月份对员工生产率的影响',size=15)
ax[0].legend(loc='lower right')ax[1].legend(loc='lower right')ax[2].legend(loc='lower right')
#设置默认的间距plt.tight_layout()plt.show()从上面三幅图可知,不同时间对员工生产率并无明显影响,仅在每月第五周两部门员工生产率略有提升。<div id="3.3"></div>
3.3 数据相关性分析
最后,我们使用数据中的连续型字段绘制相关系数矩阵热力图。import seaborn as snsimport matplotlib.pyplot as pltimport numpy as np
# 正常显示中文标签
plt.rcParams['font.sans-serif'] = ['SimHei']
# 正常显示负号
plt.rcParams['axes.unicode_minus'] = False
全局字体的大小设置
plt.rcParams.update({'font.size': 9})
计算相关系数
cols = [f for f in garments.columns if f not in ['quarter','department','day','month']]corr = garments[cols].corr()
fig = plt.figure(figsize=(7,7))
cmap = sns.diverging_palette(220,10,as_cmap=True)mask = np.zeros_like(corr)mask[np.triu_indices_from(mask)] = True
sns.heatmap(corr, annot=True, square=True,linewidths=1.5, cmap=cmap, mask=mask, center=0, cbar_kws={"shrink": .5})
plt.title("特征间的相关性",fontsize=15)
plt.show()从图中能够发现,no_of_workers
每个团队的员工人数、smv
任务分配时间及over_time
每个团队超时时间之间有明显的正相关关系。此外,idle_time
生产中断时间和idle_men
因中断而闲置的工人数也存在较强的正相关性。<div id="4"></div>
4 特征工程
在这一部分,我们将进行特征工程。首先,对department
进行数值编码,缝纫部sewing
为1,精加工车间finishing
为0。from sklearn.preprocessing import LacbelEncoder
le = LabelEncoder()garments['department'] = le.fit_transform(garments['department'])
garments.head()接下来,我们对数据集中的离散型变量进行独热编码。
添加特征
tmp_df = garments.groupby(['department','team'])[['actual_productivity','targeted_productivity','smv','incentive','over_time','idle_time','idle_men','no_of_style_change','no_of_workers']].agg({'mean','max','min'})tmp_df = tmp_df.reset_index()
tmp_df.columns = ['department','team'] + ['_'.join(f) for f in tmp_df.columns if f[0] not in ['department','team']]tmp_df.head()garments = garments.merge(tmp_df, on=['department','team'], how='left')garments = pd.get_dummies(garments)garments.head()<div id="5"></div>
5 预测员工生产率
<div id="5.1"></div>
5.1 训练集测试集划分
在训练模型之前,我们先将训练集测试集按照4:1进行划分,并将特征进行Z-Score标准化处理。from sklearn.model_selection import train_test_splitfrom sklearn.preprocessing import StandardScaler
X=garments.drop(['actual_productivity'], axis=1)y=garments['actual_productivity']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1)
数据标准化
s = StandardScaler()
X_train_sm = s.fit_transform(X_train)
X_test_sm = s.transform(X_test)
X_train_sm = X_trainX_test_sm = X_test<div id="5.2"></div>
5.2 线性回归
首先,在训练集上训练线性回归模型,并在测试集上进行预测和评估,输出决定系数R2和均方误差:from sklearn.linear_model import LinearRegressionfrom sklearn.metrics import r2_score,mean_squared_error
mlr = LinearRegression()mlr.fit(X_train_sm, y_train)y_pred_sm = mlr.predict(X_test_sm)
print(f'R2 为 {round(r2_score(y_test,y_pred_sm),4)}')print('均方误差 (MSE): %.4f' % mean_squared_error(y_test, y_pred_sm))
可视化
plt.figure(figsize=(6, 4))
plt.scatter(x=y_test,y=y_pred_sm, alpha=.5)
plt.xlabel('实际值')plt.ylabel('预测值')plt.title('实际值预测值对比',size=15)
plt.show()from sklearn.linear_model import LinearRegression, Ridgefrom sklearn.metrics import r2_score,mean_squared_error
mlr = Ridge()mlr.fit(X_train_sm, y_train)y_pred_sm = mlr.predict(X_test_sm)
print(f'R2 为 {round(r2_score(y_test,y_pred_sm),4)}')print('均方误差 (MSE): %.4f' % mean_squared_error(y_test, y_pred_sm))
可视化
plt.figure(figsize=(6, 4))
plt.scatter(x=y_test,y=y_pred_sm, alpha=.5)
plt.xlabel('实际值')plt.ylabel('预测值')plt.title('实际值预测值对比',size=15)
plt.show()决定系数仅为0.19,模型预测效果较差。<div id="5.3"></div>
5.3 回归决策树
接下来我们尝试构建回归决策树模型,设置树的最大深度为10:from sklearn.model_selection import KFoldfrom sklearn.tree import DecisionTreeRegressor
skf = KFold(n_splits=5, shuffle=True, random_state=2021)for train_index, test_index in skf.split(X,y):
dtreg = DecisionTreeRegressor(max_depth=10,random_state=0) dtreg.fit(X.loc[train_index], y[train_index]) y_pred = dtreg.predict(X.loc[test_index]) print(f'R2 为 {round(r2_score(y[test_index],y_pred),4)}') print('均方误差 (MSE): %.4f' % mean_squared_error(y[test_index], y_pred))
使用回归决策树
from sklearn.tree import DecisionTreeRegressorfrom sklearn import tree
dtreg = DecisionTreeRegressor(max_depth=10,random_state=0)
dtreg.fit(X_train_sm, y_train)
y_pred_sm = dtreg.predict(X_test_sm)
print(f'R2 为 {round(r2_score(y_test,y_pred_sm),4)}')print('均方误差 (MSE): %.4f' % mean_squared_error(y_test, y_pred_sm))
可视化
plt.figure(figsize=(6, 4))
plt.scatter(x=y_test, y=y_pred_sm, alpha=.5)
plt.xlabel('实际值')plt.ylabel('预测值')plt.title('实际值预测值对比',size=15)
plt.show()模型在测试集上决定系数R2为0.43,均方误差 (MSE)为 0.02,预测效果略有提高。<div id="5.4"></div>
5.4 Bagging回归
最后使用BaggingRegressor
构建模型,我们使用回归决策树作为基模型。from sklearn.ensemble import BaggingRegressorfrom sklearn import tree
br = BaggingRegressor(n_estimators=800, random_state=11)
br.fit(X_train_sm, y_train)
y_pred_sm = br.predict(X_test_sm)
print(f'R2 为 {round(r2_score(y_test,y_pred_sm),4)}')print('均方误差 (MSE): %.4f' % mean_squared_error(y_test, y_pred_sm))
可视化
plt.figure(figsize=(6, 4))
plt.scatter(x=y_test,y=y_pred_sm, alpha=.5)
plt.xlabel('实际值')plt.ylabel('预测值')plt.title('实际值预测值对比',size=15)
plt.show()最终R2提升到了0.5052,均方误差为0.0155。<div id="6"></div>
6 总结
本案例我们使用了服装厂员工生产效率数据集,结合可视化分析方法探索了数据中隐藏的信息,并通过建立线性回归、回归决策树和Bagging回归三种模型预测员工实际生产率。