1.摘要:

本文详述了新手如何通过数据预览,探索式数据分析,缺失数据填补,删除关联特征以及派生新特征等数据处理方法,完成Kaggle的Titanic幸存预测要求的内容和目标。

2.背景介绍:

Titanic幸存预测是数据分析入门和机器学习入门经典的开源项目之一。它要求我们通过训练数据集分析出具有什么特征的人更可能幸存,并预测出测试数据集中的每一位乘客是否生还。
该项目是一个二元分类问题

3.数据处理完整流程:

3.1:加载数据

在加载数据前,把可能会用到的相关包全部加载。

#基本数据分析
import pandas as pd
import numpy as np
#可视化
%matplotlib inline
import matplotlib.pyplot as plt
import seaborn as sns
#模型, 数据处理,模型融合 相关方法。
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC, LinearSVC
from sklearn.ensemble import RandomForestClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.linear_model import Perceptron
from sklearn.linear_model import SGDClassifier
from sklearn.tree import DecisionTreeClassifier
from xgboost import XGBClassifier
from sklearn.metrics import precision_score
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import GridSearchCV, cross_val_score, StratifiedKFold, learning_curve
import warnings
warnings.filterwarnings('ignore')

通过如下代码将训练数据和测试数据分别加载到名为train_df和test_df的data.frame中

train_df=pd.read_csv('titanic/train.csv')#训练集
test_df=pd.read_csv('titanic/test.csv')#测试集
#把训练数据和测试数据放一起进行数据处理
combine = pd.concat([train_df,test_df])

3.2 数据预览

先观察数据

print(train_df.describe())
print(test_df.describe())
train_df.describe(include=['O'])




把训练数据和测试数据放一起进行数据处理

combine = pd.concat([train_df,test_df])
combine.head()

PassengerId :标识乘客的ID
Survived:标识该乘客是否幸存。
Pclass:标识乘客的船舱等级
Name:除包含姓和名以外,还包含Mr. Mrs. Dr.这样的具有西方文化特点的信息
Sex:标识乘客性别
Age:标识乘客年龄,有缺失值
SibSp:代表兄弟姐妹及配偶的个数。
Parch:代表父母或子女的个数
Ticket:代表乘客的船票号
Fare:代表乘客的船票价
Cabin:代表乘客所在的舱位,有缺失值
Embarked 代表乘客登船口岸

在这个问题中:
一共有891个样本
Survived的标签是通过0或1来区分
大概38%的样本是survived
大多数乘客(>75%)没有与父母或是孩子一起旅行
大约30%的乘客有亲属和/或配偶一起登船
票价的差别非常大,少量的乘客(<1%)付了高达$512的费用
很少的乘客(<1%)年纪在64-80之间
上面3个表了解到,Age Cabin Fare Embarked 存在缺失值

3.31 乘客等级越高,幸存率越高

plt.figure(figsize=(12,4))
train_df.pivot_table(index='Pclass',columns='Survived',values='PassengerId',aggfunc='count').plot.bar(stacked=True)plt.title(u"根据舱等级的获救情况");


根据可视化结果发现:1等舱的人生存率超过了50%,2等舱,3等舱生存率递减。
将Pclass作为特征之一。这里我们把船舱位置离散化。

df=pd.get_dummies(combine['Pclass'],prefix='Pclass')
combine=pd.concat([combine,df],axis=1)

3.32 不同Title的乘客幸存率不同

乘客姓名重复度太低,不适合直接使用。而姓名中包含Mr. Mrs. Dr.等具有文化特征的信息,可将之抽取出来。
我们在丢弃Name与PassengerId这两个特征之前,希望从Name特征里提取出Titles的特征,并测试Titles与survival之间的关系。
在下面的代码中,我们通过正则提取了Title特征,正则表达式为(\w+.),它会在Name特征里匹配第一个以“.”号为结束的单词。

同时,指定expand=False的参数会返回一个DataFrame。

combine['Title'] = combine.Name.str.extract('([A-Za-z]+)\.', expand=False)
pd.crosstab(combine['Title'], combine['Sex'])

表中最多的四个称谓是MISS Mr Mrs Master

所以把所有的male全部称呼为Mr

但是女人分为Miss 和Mrs 两类,查阅资料,把剩下的称呼归到Miss或者Mrs里来。

Mme:称呼非英语民族的”上层社会”已婚妇女,及有职业的妇女

Lady:贵族夫人

the Countess:女伯爵

Ms:美国用来称呼婚姻状态不明的妇女

Mlle:小姐

Dona:家庭主妇

combine['Title'] = combine['Title'].replace(['Lady', 'Countess',
'Dr','Mme', 'Dona'], 'Mrs')
combine['Title'] = combine['Title'].replace(['Don','Dona','Major', 'Capt', 'Jonkheer', 'Rev', 'Col','Sir','Dr'],'Mr')
combine['Title'] = combine['Title'].replace(['Mlle','Ms'],'Miss')
combine[['Title','Survived']].groupby('Title').mean().plot.bar()
plt.title('名字中带有不同称谓的获救情况')

 train_df[['Title', 'Survived']].groupby(['Title'], as_index=False).mean()


从上面的可视化图和Title表可看出,Title为Mr的乘客幸存比例非常小,而Title为Mrs和Miss的乘客幸存比例非常大。
我们决定保留这个新的Title特征并加入到训练模型
把Title名字离散化

df2=pd.get_dummies(combine['Title'],prefix='Title')
combine=pd.concat([combine,df2],axis=1)

3.33 女性幸存率远高于男性

对于Sex变量,由Titanic号沉没的背景可知,逃生时遵循“妇女与小孩先走”的规则,由此猜想,Sex变量应该对预测乘客幸存有帮助。

combine.pivot_table( index='Sex',columns='Survived',values='PassengerId',aggfunc='count').plot.bar(stacked=True)
plt.title('不同性别的获救情况')


可视化的结果验证了这一猜想,大部分女性得以幸存,而男性中只有很小部分幸存。

3.34 名字长度也和幸存与否有关?

train_df.groupby(train_df.Name.apply(lambda x: len(x)))['Survived'].mean().plot()
plt.title('不同名字长度获救概率的分布情况')


大概是Name长度越长,Name里包含的称谓越多,社会地位越高吧。
我们把名字长度分成5个区间并进行离散化。

combine['Name_Len'] = combine['Name'].apply(lambda x: len(x))
combine['Name_Len'] = pd.qcut(combine['Name_Len'],5)
df10 = pd.get_dummies(combine['Name_Len'],prefix='Name_Len')
combine = pd.concat([combine,df10],axis=1)

3.35 配偶及兄弟姐妹数适中的乘客更易幸存;父母与子女数适中的乘客更可能幸存;生成新的Feature(FamilySize)

train_df.pivot_table(index='SibSp',columns='Survived',values='PassengerId',aggfunc='count').plot.bar()
plt.title('不同亲戚数量获救情况')

train_df.pivot_table(index='Parch',columns='Survived',values='PassengerId',aggfunc='count').plot.bar()
plt.title('不同父母孩子数量获救情况')


SibSp与Parch都说明,当乘客无亲人时,幸存率较低,乘客有少数亲人时,幸存率高于50%,而当亲人数过高时,幸存率反而降低。在这里,可以考虑将SibSp与Parch相加,生成新的变量,FamilySize(家庭成员数量)。

combine['Familysize']=combine['Parch']+combine['SibSp']
combine.pivot_table(index='Familysize',columns='Survived',values='PassengerId',aggfunc='count').plot.bar()



从图中可以看到,当家庭成员数为0时,生存率不足1/3,当家庭成员数在1-3之间的时候,生存率在50%以上,当家庭成员超过3个时,生存率骤降,不足50%
我们把Familysize作为新的特征变量加入要训练的模型中。
分成3组,并离散化。

combine['Familysize'] = np.where(combine['Familysize']==0, 'solo',np.where(combine['Familysize']<=3, 'normal', 'big'))
df4 = pd.get_dummies(combine['Familysize'],prefix='Familysize')
combine = pd.concat([combine,df4],axis=1)

3.36 添加一些特别的Feature

因为Sex对于Survived的影响很大,并且在泰坦尼克场景下,女性死亡和男性存活都是小概率事件,模型会很容易判断女性乘客存活、男性乘客死亡,为了提升模型对于这一类群体的识别能力,我们分析数据找到了一个重要特征,family,同一个family下的生存死亡模式有很大程度上是相同的,例如:有一个family有一个女性死亡,这个family其他的女性的死亡概率也比较高。

因此,我们把这些特殊的family标注出来作为特征值。

combine['Fname'] = combine['Name'].apply(lambda x:x.split(',')[0])
combine['Familysize'] = combine['SibSp']+combine['Parch']
dead_female_Fname = list(set(combine[(combine.Sex=='female') & (combine.Age>=12)& (combine.Survived==0) & (combine.Familysize>1)]['Fname'].values))
survive_male_Fname = list(set(combine[(combine.Sex=='male') & (combine.Age>=12)& (combine.Survived==1) & (combine.Familysize>1)]['Fname'].values))
combine['Dead_female_family'] = np.where(combine['Fname'].isin(dead_female_Fname),1,0)
combine['Survive_male_family'] = np.where(combine['Fname'].isin(survive_male_Fname),1,0)
combine = combine.drop(['Name','Fname'],axis=1)

3.37 共票号乘客幸存率高

我们把那些票号一样的乘客的属性变成Share,把那些单独票号的乘客的属性变成Unique

a=combine.groupby('Ticket').Ticket.count()
b=np.arange(0,929)
for i in b:if a[i]>1:combine.loc[combine.Ticket==a.index[i],'newTicket']='Share'else:combine.loc[combine.Ticket==a.index[i],'newTicket']='Unique'

可视化newTicket

combine.pivot_table(index='newTicket',columns='Survived',values='PassengerId',aggfunc='count').plot.bar(stacked=True)



我们能明显的看到共享Ticket的乘客生存率超过了50%,而唯一票号的乘客生存率只有不到30%。这里我猜想是因为共同票号的乘客是朋友或者亲戚家人,所以相互关照,相互救援,生存率上升。
我们把票号唯一与否也加入Feature里面。

df8 = pd.get_dummies(combine['newTicket'],prefix='Ticket')
combine = pd.concat([combine,df8],axis=1)

3.38 支出船票费越高幸存率越高

因为Fare只有一个缺失值,这里考虑用median去填补它。然后把填补后的Fare分组,看一下不同船票支出对Survived的影响大小。

combine['Fare'].fillna(combine['Fare'].dropna().median(), inplace=True)
combine['bins'] = pd.qcut(combine['Fare'],3 )
combine[['bins', 'Survived']].groupby(['bins'], as_index=False).mean()


我们可以看到,Fare越大,存活率越高。
把bins离散化,加入到Feature中

df7 = pd.get_dummies(combine['bins'],prefix='bins')
combine = pd.concat([combine,df7],axis=1)

3.39 不同码头登船的乘客幸存率不同

因为缺失值只有2个,所以也用median代替。

combine['Embarked'] = combine['Embarked'].fillna('S')
train_df.pivot_table(index='Embarked',columns='Survived',values='PassengerId',aggfunc='count').plot.bar(stacked=True)plt.title(u"各登录港口乘客的获救情况")
plt.xlabel(u"登录港口")
plt.ylabel(u"人数") 



可以看到,C口的幸存率超过了50%,S码头登船的乘客存活率最低。
Embarked与生存率相关,离散化后加入Feature。

df6 = pd.get_dummies(combine['Embarked'],prefix='Embarked')
combine = pd.concat([combine,df6],axis=1)

3.40有无Cabin记录的乘客幸存率不同

先对Cabin进行处理,有缺失值标记为YES,不是缺失值的标记为NO

combine.loc[(combine.Cabin.notnull()),'Cabin'] = 'Yes'
combine.loc[(combine.Cabin.isnull()),'Cabin'] = 'No'

combine.pivot_table(index='Cabin',columns='Survived',values='PassengerId',aggfunc='count').plot.bar()
plt.title('有无Cabin的获救情况')



有Cabin记录的乘客生存率高达66%,没有记录的乘客生存率低至29.9%
加入Feature。

df5 = pd.get_dummies(combine['Cabin'],prefix='Cabin')
combine = pd.concat([combine,df5],axis=1)

4 两种方法填补Age缺失值

4.1 随机森林来预测填补Age值

这里面的的Age可以用随机森林来求,可以groupby以后,不同的区间的median来求。

我们这里先用scikit-learn中的RandomForest来拟合一下缺失的年龄数据

# 乘客分成已知年龄和未知年龄两部分
Age_isnull=combine[combine.Age.isnull()]
Age_notnull=combine[combine.Age.notnull()]
from sklearn.ensemble import RandomForestRegressor
def set_missing_age(df):# 把已有的数值型特征取出来丢进Random Forest Regressor中age_df = df[['Age','Fare', 'Parch', 'SibSp', 'Pclass_1', 'Pclass_2','Pclass_3']]Age_isnull=age_df[age_df.Age.isnull()]Age_notnull=age_df[age_df.Age.notnull()]x = Age_notnull.drop(['Age'],axis=1)y = Age_notnull['Age']rfr = RandomForestRegressor(random_state=0, n_estimators=2000, n_jobs=-1)rfr.fit(x,y)predictedAges = rfr.predict(Age_isnull.drop(['Age'],axis=1))# 用得到的预测结果填补原缺失数据df.loc[(df.Age.isnull()),'Age'] = predictedAgesreturn df
combine=set_missing_age(combine)
fig=plt.figure(figsize=(12,6))plt.subplot(121)
train_df[train_df.Survived ==1].Age.plot.hist(bins=16)
plt.title('Survived==1')plt.subplot(122)
train_df[train_df.Survived ==0].Age.plot.hist(bins=16)
plt.title('Survived==0')

combine['Agebins'] = pd.cut(combine['Age'], 5)
combine[['Agebins', 'Survived']].groupby(['Agebins']).mean().sort_values(by='Agebins', ascending=True)


经过缺失值填补分组后,我们可以清楚的看到小孩子的获救概率是最高的,年轻人和老年人死亡率较高,猜测 中年人可能有一部分是Pclass=1的等级。 离散化,加入Feature

df9 = pd.get_dummies(combine['Agebins'],prefix='Agebins')
combine = pd.concat([combine,df9],axis=1)

4.2 groupby分组后填补。

不同的年龄有不同的称呼,比如年轻女孩称呼MISS,年龄稍大的女人称呼Mrs。所以,可能Age和Title有关。 年龄较大可能有更高的等级和地位,所以可能Age和Pclass也有关。
先把Title和Pclass分组,然后求每一个组的众数Age,用这些数来填补那些分在同一组的缺失值。
group = combine_df.groupby([‘Title’, ‘Pclass’])[‘Age’]
combine_df[‘Age’] = group.transform(lambda x: x.fillna(x.median()))

group = combine.groupby(['Title', 'Pclass'])['Age']
combine['Age'] = group.transform(lambda x: x.fillna(x.median()))

4.3 小孩子的标签很重要。

我们一直遵循着女人和小孩优先的原则,所以我们还要添加一个Kid的标签加入Feature。

combine['IsChild'] = np.where(combine['Age']<=12,1,0)

5 建模数据准备

保留有用的特征向,将所有数据数值化。

#丢掉多余的columns
combine=combine.drop(['Age','Embarked','Fare','Parch','Sex','SibSp','Cabin','Pclass','Ticket','bins','newTicket','Familysize','Title','Name_Len','Agebins'],axis=1)
#把Feature Name 替换了
combine.rename(columns={'bins_(-0.001, 8.662]':'Fare_1','bins_(8.662, 26.0]':'Fare_2','bins_(26.0, 512.329]':'Fare_3','Agebins_(0.0902, 16.136]':'Age_step1','Agebins_(16.136, 32.102]':'Age_step2','Agebins_(32.102, 48.068]':'Age_step3','Agebins_(48.068, 64.034]':'Age_step4','Agebins_(64.034, 80.0]':'Age_step5','Name_Len_(11.999, 19.0]':'Name_lenstep1','Name_Len_(19.0, 23.2]':'Name_lenstep2','Name_Len_(23.2, 27.0]':'Name_lenstep3','Name_Len_(27.0, 32.0]':'Name_lenstep4','Name_Len_(32.0, 82.0]':'Name_lenstep5'},inplace=True)combine.columns #查看剩下的columns

#把所有Feature值转化为数值型编码:
features = combine.drop(["PassengerId","Survived"], axis=1).columns
le = LabelEncoder()
for feature in features:le = le.fit(combine[feature])combine[feature] = le.transform(combine[feature])
combine.head()

#提取数据集
X_all = combine.iloc[:891,:].drop(["PassengerId","Survived"], axis=1)
Y_all = combine.iloc[:891,:]["Survived"]
X_test = combine.iloc[891:,:].drop(["PassengerId","Survived"], axis=1)

6 建模调优

6.1 分别考察逻辑回归、支持向量机、最近邻、决策树、随机森林、gbdt、xgbGBDT几类算法的性能。

#提取这几种算法,放在clfs里
lr = LogisticRegression()
svc = SVC()
knn = KNeighborsClassifier(n_neighbors = 3)
dt = DecisionTreeClassifier()
rf = RandomForestClassifier(n_estimators=300,min_samples_leaf=4,class_weight={0:0.745,1:0.255})
gbdt = GradientBoostingClassifier(n_estimators=500,learning_rate=0.03,max_depth=3)
xgbGBDT = XGBClassifier(max_depth=3, n_estimators=300, learning_rate=0.05)
clfs = [lr, svc, knn, dt, rf, gbdt, xgbGBDT]
#这里的cross_val_score交叉验证的方法可以帮助我们进行调参,最终得到一组最佳的模型参数,使得测试数据的准确率和泛化能力最佳。
kfold = 10
cv_results = []
for classifier in clfs :cv_results.append(cross_val_score(classifier, X_all,y = Y_all, scoring = "accuracy", cv = kfold, n_jobs=4))
算出每个模型的准确率的平均值和标准差
cv_means = []
cv_std = []
for cv_result in cv_results:cv_means.append(cv_result.mean())cv_std.append(cv_result.std())cv_res = pd.DataFrame({"CrossValMeans":cv_means,"CrossValerrors": cv_std,"Algorithm":["LR","SVC",'KNN','decision_tree',"random_forest","GBDT","xgbGBDT"]})print('cv_res')
g = sns.barplot("CrossValMeans","Algorithm",data = cv_res, palette="Set3",orient = "h",**{'xerr':cv_std})
g.set_xlabel("Mean Accuracy")
g = g.set_title("Cross validation scores")


6.2 把不同的模型组合在一起

#定义一个类
class Ensemble(object):
#创建类中的函数:def __init__(self,estimators):self.estimator_names = []self.estimators = []for i in estimators:self.estimator_names.append(i[0])self.estimators.append(i[1])self.clf = LogisticRegression()#拟合训练集和训练结果def fit(self, train_x, train_y):for i in self.estimators:i.fit(train_x,train_y)x = np.array([i.predict(train_x) for i in self.estimators]).Ty = train_yself.clf.fit(x, y)#用拟合好的模型来预测def predict(self,x):x = np.array([i.predict(x) for i in self.estimators]).T#print(x)return self.clf.predict(x)#比较预测结果def score(self,x,y):s = precision_score(y,self.predict(x))return s
把不同的分类器放进集成框架中
bag = Ensemble([('xgb',xgbGBDT),('lr',lr),('rf',rf),('svc',svc),('gbdt',gbdt)])
score = 0
#评估模型,把训练集随机分成0.8份(X_train)和0.2份(X_cv), 拟合训练数据然后预测X_cv,得到的结果和Y_cv比较,重复10次,算出最终得分。
for i in range(0,10):num_test = 0.20X_train, X_cv, Y_train, Y_cv = train_test_split(X_all, Y_all, test_size=num_test)bag.fit(X_train, Y_train)#Y_test = bag.predict(X_test)acc_xgb = round(bag.score(X_cv, Y_cv) * 100, 2)score+=acc_xgb
print(score/10) #83.059

7 总结:

最后在官网上提交的结果是0.79904,新更新排名17%。

7.1 值得改进的地方:

  • 1.我们可以把Title中频数小的称呼提取出来,形成一个新的Feature。
  • 2.预测Age的随机森林模型中的Feature可以更加的详细。而且应该用填补缺失值以后的特征向量去拟合模型。
  • 3.也许名字长度没有那么重要???
  • 4.模型准确度的天花板取决于特征工程,所以当以上3个小tips都完成以后,试着换一种模型融合算法,看能不能有更好的结果。
  • 如果您看到这里,那就点个赞再走咯~~
  • 祝数据分析和机器学习之路一切顺利!!

8 改进之后的排名

1,我把Title那些名称使用较少的名字提取出来,然后用Rare命名成立一个单独的feature。

2,用随机森林预测Age只保留了Title和Pclass两个特征值。最后得分0.808

数据分析入门项目之 :Titanic: Machine Learning from Disaster相关推荐

  1. 机器学习案例学习【每周一例】之 Titanic: Machine Learning from Disaster

     下面一文章就总结几点关键: 1.要学会观察,尤其是输入数据的特征提取时,看各输入数据和输出的关系,用绘图看! 2.训练后,看测试数据和训练数据误差,确定是否过拟合还是欠拟合: 3.欠拟合的话,说明模 ...

  2. 大数据第一课(满分作业)——泰坦尼克号生存者预测(Titanic - Machine Learning from Disaster)

    大数据第一课(满分作业)--泰坦尼克号生存者预测(Titanic - Machine Learning from Disaster) 1 项目背景 1.1 The Challenge 1.2 What ...

  3. 小白的机器学习之路(1)---Kaggle竞赛:泰坦尼克之灾(Titanic Machine Learning from Disaster)

    我是目录 前言 数据导入 可视化分析 Pclass Sex Age SibSp Parch Fare Cabin Embarked 特征提取 Title Family Size Companion A ...

  4. 【Kaggle】Titanic - Machine Learning from Disaster(二)

    文章目录 1. 前言 2. 预备-环境配置 3. 数据集处理 3.1 读取数据集 3.2 查看pandas数据信息 3.2.1 查看总体信息 3.2.2 数据集空值统计 3.3. 相关性分析 3.3. ...

  5. Kaggle | Titanic - Machine Learning from Disaster【泰坦尼克号生存预测】 | baseline及优秀notebook总结

    文章目录 一.数据介绍 二.代码 三.代码优化方向 一.数据介绍   Titanic - Machine Learning from Disaster是主要针对机器学习初学者开展的比赛,数据格式比较简 ...

  6. 【kaggle入门题一】Titanic: Machine Learning from Disaster

    原题: Start here if... You're new to data science and machine learning, or looking for a simple intro ...

  7. Kaggle——泰坦尼克号(Titanic: Machine Learning from Disaster)详细过程

    一.简介 1.数据 (1)训练集(train.csv) (2)测试集(test.csv) (3)提交文件示例(gender_submission.csv) 对于训练集,我们为每位乘客提供结果.模型将基 ...

  8. Kaggle比赛(一)Titanic: Machine Learning from Disaster

    泰坦尼克号幸存预测是本小白接触的第一个Kaggle入门比赛,主要参考了以下两篇教程: https://www.cnblogs.com/star-zhao/p/9801196.html https:// ...

  9. Titanic: Machine Learning from Disaster-kaggle入门赛-学习笔记

    Titanic: Machine Learning from Disaster 对实验用的数据的认识,数据中的特殊点/离群点的分析和处理,特征工程(feature engineering)很重要. 注 ...

最新文章

  1. C语言编程题显示日历,C语言日历问题
  2. Spring Boot-切换嵌入式Servlet容器
  3. 《Python游戏编程快速上手》第三章-猜数字游戏
  4. javaweb----DAO模型设计
  5. 阿里北大:深度哈希算法最新综述
  6. SQL语句写返回一天内的纪录,得到一周内星期几的时间
  7. HDU2072 单词数(解法二)【废除!!!】
  8. 了解更多关于11gR2 diskmon
  9. SQL Server 2008 R2 完全卸载
  10. 软件测试工程师笔试题带答案(一)
  11. 2019 WordPress免费收费中文主题推荐 强迫症患者的救赎
  12. python整合excel表_Python实现合并excel表格的方法分析
  13. vue解决mintui中使用MessageBox弹窗拦截,移动端多次点击手机的物理返回键,选择确定后页面返回不正确问题
  14. 微信公众号开发淘宝优惠券查询的思路和原理
  15. 手机登陆 html,手机登录(登陆)路由器方法
  16. Spark抽取MySQL分表优化
  17. 再见牛根生,再见英雄时代
  18. zabbix之3触发器/action及模板
  19. VC/MFC 编程技巧大总结
  20. 工程职业伦理_Mooc_2019_期末考试参考答案

热门文章

  1. 【项目管理】Java离线版语音识别-语音转文字
  2. 简述a.Equals(b)和a=b的区别
  3. 野火烧不尽,新型“现金贷”出现
  4. 2021年金属非金属矿井通风复审考试及金属非金属矿井通风模拟考试
  5. html 内容不换行,html标题不换行 html 什么代码可以不换行
  6. 教育大数据挖掘--菜鸟入门
  7. 正态分布、期望和均值的区别、有偏和无偏估计量(方差)的理解
  8. SFR算法详解(一)——基础理论
  9. java web连接数据库
  10. 解决Unity3D中“There is no 'Renderer' attached to the game object”问题