# 5.2 数据预处理

    ##### 5.2 数据预处理 #####

    ### 载入数据和相应包 ###
    # 清空工作目录
    rm(list = ls())
    # 加载机器学习包
    # install.packages(caret)
    library(caret)
    # 加载数据
    dat = read.csv('相亲数据2.csv', fileEncoding = "UTF-8")
    dim(dat)
    ##  [1] 15 12
    head(dat)
    ##    名字 决定 性别 是否喜欢矮矬穷   年龄 智力 对方名字 对方决定 对方性别
    ##  1 夜华    1    1              0  50000    9     白浅        1        0
    ##  2 白浅    0    0              0 140000    8     离镜        1        1
    ##  3 东华    0    1              0 360000   10     凤九        1        0
    ##  4 素锦    0    0              0  70000    5     离镜        0        1
    ##  5 离镜    1    1              0 140000    6     玄女        1        0
    ##  6 成玉    0    0              0    300    6     连宋        1        1
    ##    对方是否喜欢矮矬穷 对方年龄 对方智力
    ##  1                  0   140000        8
    ##  2                  0   140000        6
    ##  3                  0    30000        5
    ##  4                  0   140000        6
    ##  5                  0   100000        4
    ##  6                  0    90000        7
    # dat$性别 = factor(dat$性别, levels = c("男", "女"), labels = c(0, 1))
    # dat$对方性别 = factor(dat$对方性别, levels = c("男", "女"), labels = c(0, 1))

    ### 1.数据分割 ###

    ## (1) 按照因变量进行分层抽样 ##
    # 数据划分为训练集和测试集

    # 设置随机种子
    set.seed(1234)
    # 将数据集的80%划分为训练集，20%划分为测试集
    trainIndex = createDataPartition(dat$决定, p = .8,
                                     list = FALSE,
                                     times = 1)
    # createDataPartition会自动从y的各个level随机取出等比例的数据来，组成训练集,可理解为分层抽样；
    datTrain = dat[trainIndex, ]
    # 训练集
    datTest = dat[-trainIndex, ]
    # 测试集
    table(dat$决定) / nrow(dat)  # 全集上因变量各个水平的比例
    ##
    ##          0         1
    ##  0.5333333 0.4666667
    table(datTrain$决定) / nrow(datTrain)  # 训练集上因变量各个水平的比例
    ##
    ##          0         1
    ##  0.5833333 0.4166667
    table(datTest$决定) / nrow(datTest)  # 测试集上因变量各个水平的比例
    ##
    ##          0         1
    ##  0.3333333 0.6666667

    ## (2) 交叉验证抽样 ##
    set.seed(1234)
    index = createFolds(dat$决定, k = 3, list = FALSE, returnTrain = TRUE)
    index
    ##   [1] 2 3 2 1 1 1 1 3 2 1 3 3 2 3 2
    testIndex = which(index == 1)
    datTraincv = dat[-testIndex, ]
    # 训练集
    datTestcv = dat[testIndex, ]
    # 测试集

    ## (3) boostrapping sample ##
    set.seed(1234)
    createResample(dat$决定, times = 3, list = F)
    ##        Resample1 Resample2 Resample3
    ##   [1,]         1         1         3
    ##   [2,]         2         1         4
    ##   [3,]         4         3         4
    ##   [4,]         5         3         4
    ##   [5,]         5         4         5
    ##   [6,]         8         4         5
    ##   [7,]         9         5         5
    ##   [8,]        10         5         7
    ##   [9,]        10         5         8
    ##  [10,]        10         5         9
    ##  [11,]        10         8        10
    ##  [12,]        10        13        10
    ##  [13,]        11        13        12
    ##  [14,]        13        13        13
    ##  [15,]        14        14        15

    ## (4) 分割时间序列 ##
    # 加载数据
    growdata = read.csv('水哥成长日记.csv', fileEncoding = "UTF-8")
    head(growdata)
    ##         时间 颜值
    ##  1 2016年1月    5
    ##  2 2016年2月    6
    ##  3 2016年3月    7
    ##  4 2016年4月    8
    ##  5 2016年5月    9
    ##  6 2016年6月   10
    (timeSlices = createTimeSlices(1:nrow(growdata),
                                   initialWindow = 5, horizon = 2, fixedWindow = TRUE))
    ##  $train
    ##  $train$Training5
    ##  [1] 1 2 3 4 5
    ##
    ##  $train$Training6
    ##  [1] 2 3 4 5 6
    ##
    ##
    ##  $test
    ##  $test$Testing5
    ##  [1] 6 7
    ##
    ##  $test$Testing6
    ##  [1] 7 8

    # 5表示初始的window，2表示测试集是训练集后的2位；fixedwindow表示都是训练集宽度一致，如果想递每次都从第一个样本开始，那么就得设置为FALSE，默认为TRUE。

    ### 2.处理缺失值 ###

    # 3种方法：preProcess()函数，该函数提供了三种缺失值填补的方法，即K近邻方法、Bagging树集成方法和中位数法。
    # 需要注意的是，采用K近邻方法时，会对原始数据进行标准化，如果需要返回原始值，还需将标准化公式倒推回来；
    # 使用Bagging树集成方法，理论上对缺失值的填补更权威，但其效率比较低；
    # 使用中位数方法，速度非常快，但填补的准确率有待验证。
    # 如果你想使用多重插补法，不妨也可以试试mice包，其操作原理是基于MC（蒙特卡洛模拟法）。
    # preProcess can be used to impute data sets based only on information in the training #set，注意只能用训练集信息。

    # 在此之前必须数据分割成训练集测试集

    ## (1) 中位数法 ##
    imputation_k = preProcess(datTrain,method = 'medianImpute')
    datTrain1 = predict(imputation_k, datTrain)
    (datTest1 = predict(imputation_k, datTest))
    ##       名字 决定 性别 是否喜欢矮矬穷 年龄 智力 对方名字 对方决定 对方性别
    ##  11   唐晶    0    0              0   36  8.0   陈俊生        0        1
    ##  12   宫羽    1    0              0   21  5.0   梅长苏        0        1
    ##  14 罗子君    1    0              0   35  8.5     贺函        1        1
    ##     对方是否喜欢矮矬穷 对方年龄 对方智力
    ##  11                  0       39        6
    ##  12                  0       31        9
    ##  14                  0       40        8
    median(datTrain$智力, na.rm = T)
    ##  [1] 8.5
    # 显然中位数这个填补方法不太合理，除非样本取值比较均匀;注意这里用的也是训练集的中位数

    ## (2) K近邻方法 ##
    imputation_k = preProcess(datTrain, method = 'knnImpute')
    ##  Warning in preProcess.default(datTrain, method = "knnImpute"): These
    ##  variables have zero variances: 是否喜欢矮矬穷, 对方是否喜欢矮矬穷
    datTrain1 = predict(imputation_k, datTrain)
    datTest1 = predict(imputation_k, datTest)
    datTrain$智力 = datTrain1$智力 * sd(datTrain$智力, na.rm = T) + mean(datTrain$智力, na.rm = T)
    datTest$智力 = datTest1$智力 * sd(datTrain$智力, na.rm = T) + mean(datTrain$智力, na.rm = T)
    datTest
    ##       名字 决定 性别 是否喜欢矮矬穷 年龄 智力 对方名字 对方决定 对方性别
    ##  11   唐晶    0    0              0   36  8.0   陈俊生        0        1
    ##  12   宫羽    1    0              0   21  5.0   梅长苏        0        1
    ##  14 罗子君    1    0              0   35  7.8     贺函        1        1
    ##     对方是否喜欢矮矬穷 对方年龄 对方智力
    ##  11                  0       39        6
    ##  12                  0       31        9
    ##  14                  0       40        8
    # 注意，这里自动用的是训练集的mean和sd对测试集进行标准化

    ### 3.处理0方差变量 ###
    # label为数字
    dim(datTrain)
    ##  [1] 12 12
    (nzv = nearZeroVar(datTrain))
    ##  [1]  4 10
    datTrain = datTrain[, -nzv]

    ### 4.处理高度线性相关变量 ###
    # 数据中不能有NA
    datTrain1 = datTrain[, -c(1, 6)]
    (descrCor = cor(datTrain1))
    ##                  决定       性别       年龄       智力    对方决定
    ##  决定      1.00000000  0.2390457 -0.3627202 -0.1069045  0.09759001
    ##  性别      0.23904572  1.0000000  0.2249529  0.6708204  0.00000000
    ##  年龄     -0.36272022  0.2249529  1.0000000  0.4101776 -0.23058113
    ##  智力     -0.10690450  0.6708204  0.4101776  1.0000000  0.12171612
    ##  对方决定  0.09759001  0.0000000 -0.2305811  0.1217161  1.00000000
    ##  对方性别 -0.02857143 -0.8366600 -0.3366009 -0.5345225  0.09759001
    ##  对方年龄 -0.16239646 -0.4046800  0.2198356 -0.4162950 -0.10723488
    ##  对方智力  0.39266401 -0.1877293 -0.6989444  0.0000000  0.03832008
    ##              对方性别   对方年龄    对方智力
    ##  决定     -0.02857143 -0.1623965  0.39266401
    ##  性别     -0.83666003 -0.4046800 -0.18772930
    ##  年龄     -0.33660090  0.2198356 -0.69894438
    ##  智力     -0.53452248 -0.4162950  0.00000000
    ##  对方决定  0.09759001 -0.1072349  0.03832008
    ##  对方性别  1.00000000  0.2150919  0.25803635
    ##  对方年龄  0.21509188  1.0000000 -0.25755521
    ##  对方智力  0.25803635 -0.2575552  1.00000000
    highlyCorDescr = findCorrelation(descrCor, cutoff = .75, names = F, verbose = T)
    ##  Compare row 2  and column  6 with corr  0.837
    ##    Means:  0.366 vs 0.265 so flagging column 2
    ##  All correlations <= 0.75
    highlyCorDescr
    ##  [1] 2
    filteredTrain = datTrain1[, -highlyCorDescr]
    # input只能是numeric型的dataframe或者matrix，且无缺失值(在此之前必须处理缺失值)

    ### 6.创造虚拟变量 ###

    # 转化为虚拟变量
    dummies = dummyVars( ~ 性别, data = dat)
    head(predict(dummies, newdata = dat))
    ##    性别
    ##  1    1
    ##  2    0
    ##  3    1
    ##  4    0
    ##  5    1
    ##  6    0
    dummies = dummyVars(决定 ~., data = dat)
    # 对于每个分类型变量，所有的水平都单独变成了一个变量，这种生成虚拟变量的方法对于回归可能不适用；
    # 好处在于你可以随意的删除某个哑变量作为参照水平
    dat1 = predict(dummies, newdata = dat)
    dat = data.frame(决定 = dat$决定, dat1[, -c(1, 20, 38, 39, 55, 56)])
    dim(dat)
    ##  [1] 15 36
    head(dat)
    ##    决定 名字.成玉 名字.东华 名字.宫羽 名字.老谭 名字.离镜 名字.罗子君
    ##  1    1         0         0         0         0         0           0
    ##  2    0         0         0         0         0         0           0
    ##  3    0         0         1         0         0         0           0
    ##  4    0         0         0         0         0         0           0
    ##  5    1         0         0         0         0         1           0
    ##  6    0         1         0         0         0         0           0
    ##    名字.梅长苏 名字.曲筱绡 名字.素锦 名字.孙悟空 名字.唐晶 名字.夜华
    ##  1           0           0         0           0         0         1
    ##  2           0           0         0           0         0         0
    ##  3           0           0         0           0         0         0
    ##  4           0           0         1           0         0         0
    ##  5           0           0         0           0         0         0
    ##  6           0           0         0           0         0         0
    ##    名字.宇文玥 名字.折颜 性别 是否喜欢矮矬穷   年龄 智力 对方名字.白浅
    ##  1           0         0    1              0  50000    9             1
    ##  2           0         0    0              0 140000    8             0
    ##  3           0         0    1              0 360000   10             0
    ##  4           0         0    0              0  70000    5             0
    ##  5           0         0    1              0 140000    6             0
    ##  6           0         0    0              0    300    6             0
    ##    对方名字.陈俊生 对方名字.大梁公主 对方名字.凤九 对方名字.贺函
    ##  1               0                 0             0             0
    ##  2               0                 0             0             0
    ##  3               0                 0             1             0
    ##  4               0                 0             0             0
    ##  5               0                 0             0             0
    ##  6               0                 0             0             0
    ##    对方名字.靖王 对方名字.离镜 对方名字.连宋 对方名字.梅长苏 对方名字.素锦
    ##  1             0             0             0               0             0
    ##  2             0             1             0               0             0
    ##  3             0             0             0               0             0
    ##  4             0             1             0               0             0
    ##  5             0             0             0               0             0
    ##  6             0             0             1               0             0
    ##    对方名字.玄女 对方名字.赵启平 对方名字.紫霞 对方决定 对方性别
    ##  1             0               0             0        1        0
    ##  2             0               0             0        1        1
    ##  3             0               0             0        1        0
    ##  4             0               0             0        0        1
    ##  5             1               0             0        1        0
    ##  6             0               0             0        1        1
    ##    对方是否喜欢矮矬穷 对方年龄
    ##  1                  0   140000
    ##  2                  0   140000
    ##  3                  0    30000
    ##  4                  0   140000
    ##  5                  0   100000
    ##  6                  0    90000

    dat$决定 = factor(dat$决定, levels = c(0, 1), labels = c("拒绝", "接收"))
    # head(model.matrix(决定 ~ 性别, data = dat))
    # head(model.matrix(决定 ~., data = dat))
    # 出来的结果像回归结果一样，有常数项，以及每个分类型变量都单独变成了（c-1）个变量

    ### 5.标准化 ###
    preProcValues = preProcess(datTrain, method = c("center", "scale"))
    trainTransformed = predict(preProcValues, datTrain)
    testTransformed = predict(preProcValues, datTest)
    # 利用训练集的均值和方差对测试集进行标准化