Chapter 9 寫一段下載數據集的耙蟲

9.1

作者小編在旭、續、序這麼說過,

運氣不錯,R確實收集了一個名為「Boston」的數據集。

這是作者小編幾年來,講授統計程式語言R的經驗值。既然是個人的經驗值,表示「一定有盲點」。為了確認這個盲點有多大,作者小編請「Google Search」幫忙,在輸入框給了以下這一句英文

what packages have BOSTON dataset in r

Google Search接到這一段「英文字」之後,會進行「搜尋」,跟「what packages have BOSTON dataset in r」「最吻合」的十個頁面摘要會出現在第一頁。經過作者小編耐心、細心、順心看過一輪之後,發現幾個套件收錄了「波士頓數據集」:

  1. MASS
  2. mlbench
  3. spData

那怎麼樣才能「拿到」波士頓數據集呢?

9.2 學習目標

9.3 載入R的波士頓數據集

在剛剛搜尋結果的前十名網頁裡,有一頁是

How do i load data set part of the MASS library in R?

這是STACK OVERFLOW的「問答頁」,作者小編的經驗值(依舊可能有盲點、可能有其他更好的問答頁),點進去,如果發現有「綠色勾勾」,表示「有經過問者或是網友、訪者驗證過的答案」,對一般剛入門R的讀者諸君絕對值得把答案的參考碼帶走,帶回自己的環境驗證一下「合不合用」:

library(MASS)
data(Boston)
head(Boston)
##      crim zn indus chas   nox    rm  age    dis rad tax ptratio  black lstat
## 1 0.00632 18  2.31    0 0.538 6.575 65.2 4.0900   1 296    15.3 396.90  4.98
## 2 0.02731  0  7.07    0 0.469 6.421 78.9 4.9671   2 242    17.8 396.90  9.14
## 3 0.02729  0  7.07    0 0.469 7.185 61.1 4.9671   2 242    17.8 392.83  4.03
## 4 0.03237  0  2.18    0 0.458 6.998 45.8 6.0622   3 222    18.7 394.63  2.94
## 5 0.06905  0  2.18    0 0.458 7.147 54.2 6.0622   3 222    18.7 396.90  5.33
## 6 0.02985  0  2.18    0 0.458 6.430 58.7 6.0622   3 222    18.7 394.12  5.21
##   medv
## 1 24.0
## 2 21.6
## 3 34.7
## 4 33.4
## 5 36.2
## 6 28.7

看到上面這一張表,表示我們成功了,成功載入計算環境,讓環境擁有「Boston」這個數據集。但是,這三句話到底是什麼意思呢?經過作者小編再一次徵詢Google Search,以下問題

  1. what is library in r
  2. what is data in r
  3. what is head in r

得知,發現

  1. library」可以「Loading/Attaching And Listing Of Packages」。
  2. data」可以「Loads specified data sets, or list the available data sets.」。
  3. head」可以「Return The First Or Last Part Of An Object」。

意思是說:

第一句話「library(MASS)」,讓我們請R把硬碟裡的「MASS」套件載入R的計算環境。

第二句話「data(Boston)」,讓我們請R載入「Boston」這個名字的數據集。

第三句話「head(Boston)」,讓我們顯示「Boston」這個數據集的前六筆觀察值。

9.3.1 課堂練習

  1. 載入套件「mlbench」的波士頓數據集,並且顯示該數據集的前三筆。
  2. 載入套件「spData」的波士頓數據集,並且顯示該數據集的前三筆。

9.3.2 小結語

雖然三句話就可以擁有波士頓數據集,不論它的英文名字是「Boston」還是「boston」,還是「BostonHousing」,

  1. library(MASS)
  2. data(Boston)
  3. head(Boston)

但,作者小編想知道R是不是可以讓我們直接從網際網路抓到波士頓數據集?請讀者諸君保持輕鬆心情,繼續「欣賞」R的表演!

9.4 直接從網際網路抓到波士頓數據集

在第二個套件mlbench提供的文件裡,我們發現有一節叫做「Source」,記載著波士頓數據集的網路位置:

The original data have been taken from the UCI Repository Of Machine Learning Databases at

the corrected data have been taken from Statlib at

Both were converted to R format by Friedrich.Leisch@ci.tuwien.ac.at.

當作者小編試著點出第一個網址,

失敗!

再點第二個網址,進入

knitr::include_url("http://lib.stat.cmu.edu/datasets/")

這是一個「數據集封存(資料)庫(Datasets Archive)」,它的名字叫做「StatLib」。這一個資料庫的維護人,是一位卡內基梅隆大學統計系的研究員,「Pantelis Vlachos」。「Pantelis Vlachos」在1998年(作者小編拿到博士學位的西元年)接手「Mike Meyer」在1989年創立的「StatLib」。接下來,讀者諸君、作者小編不是「用眼睛」往下慢慢找,要不然就是用「control + F」喚出「Google Chrome」搜尋框,然後輸入「boston」,就會找到波士頓數據集的linkhttp://lib.stat.cmu.edu/datasets/boston」以及一段簡介

The Boston house-price data of Harrison, D. and Rubinfeld, D.L. ‘Hedonic prices and the demand for clean air’, J. Environ. Economics & Management, vol.5, 81-102, 1978. Used in Belsley, Kuh & Welsch, ‘Regression diagnostics …’, Wiley, 1980. (51256 bytes)

9.4.1 找到波士頓數據集所在的網頁

拷貝上面的網址(link)到下面這一句話的「小括號」內,就可以在作者小編的網頁「刊入」他人、網友允許「被刊入」的網頁。

knitr::include_url("http://lib.stat.cmu.edu/datasets/boston")

接下來,

怎麼讀取http://lib.stat.cmu.edu/datasets/boston的網路檔案,並且取得數據呢?

同樣的,沒辦法就上Google Search找辦法。作者小編在搜尋框輸入

how to read online data in r

跟之前的經驗一模一樣,Google Search給十個參考網頁,說實在的,作者小編「線外努力」一陣子之後,發現第十個頁面提供的答案對初學R的讀者諸君最容易,

# Reading Data From TXT|CSV Files: R Base Functions
knitr::include_url("http://www.sthda.com/english/wiki/reading-data-from-txt-csv-files-r-base-functions")

因為對方拒絕連線,請讀者諸君點開這裏,往下看會看到這一段「Reading a file from internet」。

9.4.2 引用read.delim讀取網路上的波士頓數據集

拷貝STHDA在「Reading a file from internet」提供的參考碼:

my_data <- read.delim("http://www.sthda.com/upload/boxplot_format.txt")
head(my_data)

改成

x <- read.delim("http://lib.stat.cmu.edu/datasets/boston")
head(x)
##         The.Boston.house.price.data.of.Harrison..D..and.Rubinfeld..D.L...Hedonic
## 1      prices and the demand for clean air', J. Environ. Economics & Management,
## 2  vol.5, 81-102, 1978.   Used in Belsley, Kuh & Welsch, 'Regression diagnostics
## 3     ...', Wiley, 1980.   N.B. Various transformations are used in the table on
## 4                                                   pages 244-261 of the latter.
## 5                                                            Variables in order:
## 6                                         CRIM     per capita crime rate by town

觀察上述畫面,發現什麼呢?

程式「read.delim」把網頁前頭的數據集說明也一起讀進來了,再看一次波士頓數據集所在的原始頁面:

knitr::include_url("http://lib.stat.cmu.edu/datasets/boston")

作者小編認真地「數」過一次,發現該網頁的第二十一列(由左往右算,但不含空白列)才開始有數字,而且似乎「每一筆都被切成兩列」。於是乎,作者小編試試以下的程式碼:

# 因為有一行被放在表頭的欄位名稱那裡,所以只要減去19行。
x <- x[-(1:19),,drop = FALSE]
rownames(x) <- 1:dim(x)[1]
colnames(x) <- "Boston"
head(x, 3)
##                                                                        Boston
## 1  0.00632  18.00   2.310  0  0.5380  6.5750  65.20  4.0900   1  296.0  15.30
## 2                                                        396.90   4.98  24.00
## 3  0.02731   0.00   7.070  0  0.4690  6.4210  78.90  4.9671   2  242.0  17.80

檢查網頁上的第一個數字確實是0.00632。再數一數第一列跟第二列總共有幾個數字?我們會發現確實是14個,所以作者小編到目前為止提供的程式碼,並無法如套件MASS那樣,把14個數字放在14的欄位裡,而是這14個數字被切成「11 + 3」放在一個欄位裡。好消息是作者小編把說明文字的「欄位名稱」換成「Boston」。一大堆數字被放在一個「格子(一列加一欄)」裡,看似數字,所以作者小編檢查一下

class(x[,"Boston"])
## [1] "character"

對初學者而言,character是需要一點時間才能理出頭緒來的,請讀者諸君如果願意,先前進factor參考到底什麼是「character」?

一格一堆數字,絕對不是「數字」,那就只好是「文字」了!

x[,"Boston"] <- as.character(x[,"Boston"])
class(x[,"Boston"])
## [1] "character"
head(x, 3)
##                                                                        Boston
## 1  0.00632  18.00   2.310  0  0.5380  6.5750  65.20  4.0900   1  296.0  15.30
## 2                                                        396.90   4.98  24.00
## 3  0.02731   0.00   7.070  0  0.4690  6.4210  78.90  4.9671   2  242.0  17.80

上下兩張表,看起來一模一樣,這就是「factor」的魅力所在。讀者諸君一定要前進factor,關注、欣賞「factor」的魅力到底在哪裡?看過「表頭(head)」接下來,看一下「表尾(tail)」,

tail(x, 3)
##                                                                           Boston
## 1010                                                        393.45   6.48  22.00
## 1011  0.04741   0.00  11.930  0  0.5730  6.0300  80.80  2.5050   1  273.0  21.00
## 1012                                                        396.90   7.88  11.90

發現「read.delim」確實抓到「1012/2 = 506」筆觀察值。為了得到14個欄位,我們得先把分成兩列的文字「接」成一列:

paste(x[1,1], x[2,1])
## [1] " 0.00632  18.00   2.310  0  0.5380  6.5750  65.20  4.0900   1  296.0  15.30   396.90   4.98  24.00"

9.4.3 把一串文字內的全部數字通通抓來

現在全部14數字被擺在「一串文字」內,假如我們有辦法通通把它們抓出來、分開來,我們就又接近MASSBoston一步了。為此,我們需要以下這支小程式:

Numextract <- function(string){
    unlist(regmatches(string,
                      gregexpr("[[:digit:]]+\\.*[[:digit:]]*",
                               string)
                      )
           )
}

作者小編暫時「不解釋」這支程式,請讀者諸君繼續看作者小編怎麼用?

Numextract(paste(x[1,1], x[2,1]))
##  [1] "0.00632" "18.00"   "2.310"   "0"       "0.5380"  "6.5750"  "65.20"  
##  [8] "4.0900"  "1"       "296.0"   "15.30"   "396.90"  "4.98"    "24.00"
as.numeric(Numextract(paste(x[1,1], x[2,1])))
##  [1]   0.00632  18.00000   2.31000   0.00000   0.53800   6.57500  65.20000
##  [8]   4.09000   1.00000 296.00000  15.30000 396.90000   4.98000  24.00000

就三個動作:

  1. 接起來,「paste」。
  2. 抓出來,「Numextract」。
  3. 變回來,「as.numeric」。

所以重複同樣的動作506次,就可以抓到整個「Boston Housing Price」數據集!

9.4.4 如何重複某一組動作很多遍呢?

一開始,paste(x[1,1], x[2,1]),「1」在前、「2」在後。那如果可以「配對」,以下這兩句話可以產生一連串、連續的奇數跟偶數,

seq(1,dim(x)[1],2)
##   [1]    1    3    5    7    9   11   13   15   17   19   21   23   25   27   29
##  [16]   31   33   35   37   39   41   43   45   47   49   51   53   55   57   59
##  [31]   61   63   65   67   69   71   73   75   77   79   81   83   85   87   89
##  [46]   91   93   95   97   99  101  103  105  107  109  111  113  115  117  119
##  [61]  121  123  125  127  129  131  133  135  137  139  141  143  145  147  149
##  [76]  151  153  155  157  159  161  163  165  167  169  171  173  175  177  179
##  [91]  181  183  185  187  189  191  193  195  197  199  201  203  205  207  209
## [106]  211  213  215  217  219  221  223  225  227  229  231  233  235  237  239
## [121]  241  243  245  247  249  251  253  255  257  259  261  263  265  267  269
## [136]  271  273  275  277  279  281  283  285  287  289  291  293  295  297  299
## [151]  301  303  305  307  309  311  313  315  317  319  321  323  325  327  329
## [166]  331  333  335  337  339  341  343  345  347  349  351  353  355  357  359
## [181]  361  363  365  367  369  371  373  375  377  379  381  383  385  387  389
## [196]  391  393  395  397  399  401  403  405  407  409  411  413  415  417  419
## [211]  421  423  425  427  429  431  433  435  437  439  441  443  445  447  449
## [226]  451  453  455  457  459  461  463  465  467  469  471  473  475  477  479
## [241]  481  483  485  487  489  491  493  495  497  499  501  503  505  507  509
## [256]  511  513  515  517  519  521  523  525  527  529  531  533  535  537  539
## [271]  541  543  545  547  549  551  553  555  557  559  561  563  565  567  569
## [286]  571  573  575  577  579  581  583  585  587  589  591  593  595  597  599
## [301]  601  603  605  607  609  611  613  615  617  619  621  623  625  627  629
## [316]  631  633  635  637  639  641  643  645  647  649  651  653  655  657  659
## [331]  661  663  665  667  669  671  673  675  677  679  681  683  685  687  689
## [346]  691  693  695  697  699  701  703  705  707  709  711  713  715  717  719
## [361]  721  723  725  727  729  731  733  735  737  739  741  743  745  747  749
## [376]  751  753  755  757  759  761  763  765  767  769  771  773  775  777  779
## [391]  781  783  785  787  789  791  793  795  797  799  801  803  805  807  809
## [406]  811  813  815  817  819  821  823  825  827  829  831  833  835  837  839
## [421]  841  843  845  847  849  851  853  855  857  859  861  863  865  867  869
## [436]  871  873  875  877  879  881  883  885  887  889  891  893  895  897  899
## [451]  901  903  905  907  909  911  913  915  917  919  921  923  925  927  929
## [466]  931  933  935  937  939  941  943  945  947  949  951  953  955  957  959
## [481]  961  963  965  967  969  971  973  975  977  979  981  983  985  987  989
## [496]  991  993  995  997  999 1001 1003 1005 1007 1009 1011
seq(2,dim(x)[1],2)
##   [1]    2    4    6    8   10   12   14   16   18   20   22   24   26   28   30
##  [16]   32   34   36   38   40   42   44   46   48   50   52   54   56   58   60
##  [31]   62   64   66   68   70   72   74   76   78   80   82   84   86   88   90
##  [46]   92   94   96   98  100  102  104  106  108  110  112  114  116  118  120
##  [61]  122  124  126  128  130  132  134  136  138  140  142  144  146  148  150
##  [76]  152  154  156  158  160  162  164  166  168  170  172  174  176  178  180
##  [91]  182  184  186  188  190  192  194  196  198  200  202  204  206  208  210
## [106]  212  214  216  218  220  222  224  226  228  230  232  234  236  238  240
## [121]  242  244  246  248  250  252  254  256  258  260  262  264  266  268  270
## [136]  272  274  276  278  280  282  284  286  288  290  292  294  296  298  300
## [151]  302  304  306  308  310  312  314  316  318  320  322  324  326  328  330
## [166]  332  334  336  338  340  342  344  346  348  350  352  354  356  358  360
## [181]  362  364  366  368  370  372  374  376  378  380  382  384  386  388  390
## [196]  392  394  396  398  400  402  404  406  408  410  412  414  416  418  420
## [211]  422  424  426  428  430  432  434  436  438  440  442  444  446  448  450
## [226]  452  454  456  458  460  462  464  466  468  470  472  474  476  478  480
## [241]  482  484  486  488  490  492  494  496  498  500  502  504  506  508  510
## [256]  512  514  516  518  520  522  524  526  528  530  532  534  536  538  540
## [271]  542  544  546  548  550  552  554  556  558  560  562  564  566  568  570
## [286]  572  574  576  578  580  582  584  586  588  590  592  594  596  598  600
## [301]  602  604  606  608  610  612  614  616  618  620  622  624  626  628  630
## [316]  632  634  636  638  640  642  644  646  648  650  652  654  656  658  660
## [331]  662  664  666  668  670  672  674  676  678  680  682  684  686  688  690
## [346]  692  694  696  698  700  702  704  706  708  710  712  714  716  718  720
## [361]  722  724  726  728  730  732  734  736  738  740  742  744  746  748  750
## [376]  752  754  756  758  760  762  764  766  768  770  772  774  776  778  780
## [391]  782  784  786  788  790  792  794  796  798  800  802  804  806  808  810
## [406]  812  814  816  818  820  822  824  826  828  830  832  834  836  838  840
## [421]  842  844  846  848  850  852  854  856  858  860  862  864  866  868  870
## [436]  872  874  876  878  880  882  884  886  888  890  892  894  896  898  900
## [451]  902  904  906  908  910  912  914  916  918  920  922  924  926  928  930
## [466]  932  934  936  938  940  942  944  946  948  950  952  954  956  958  960
## [481]  962  964  966  968  970  972  974  976  978  980  982  984  986  988  990
## [496]  992  994  996  998 1000 1002 1004 1006 1008 1010 1012

上下對過之後,會發現確實「配對成功」。這樣擺更容易看,即便只看表頭的六組:

head(data.frame(前 = seq(1,dim(x)[1],2), 後 = seq(2,dim(x)[1],2)))
##   前 後
## 1  1  2
## 2  3  4
## 3  5  6
## 4  7  8
## 5  9 10
## 6 11 12

有了這樣的「配對關係」,R就可以幫我們這一組動作許多次了:

  1. 接起來,「paste」。
  2. 抓出來,「Numextract」。
  3. 變回來,「as.numeric」。
x <- as.numeric(
  Numextract(
    paste(x[seq(1,dim(x)[1],2),1], x[seq(2,dim(x)[1],2),1])
    )
  )
head(x, 14)
##  [1]   0.00632  18.00000   2.31000   0.00000   0.53800   6.57500  65.20000
##  [8]   4.09000   1.00000 296.00000  15.30000 396.90000   4.98000  24.00000
tail(x, 14)
##  [1]   0.04741   0.00000  11.93000   0.00000   0.57300   6.03000  80.80000
##  [8]   2.50500   1.00000 273.00000  21.00000 396.90000   7.88000  11.90000

9.4.5 把全部數字放在矩陣裡

我們有了全部的數字,觀察上述R的「輸出」之後,也發現每一筆觀察值的14個數字依序出現、擺放,加上我們知道波士頓數據集收錄了14個欄位,所以根據矩陣的原理跟原則以及R對矩陣的支持、支援,以下的第一句話可以把「x」內的全部數字依序從左而右擺成一張「506 x 14」的矩陣:

boston <- matrix(x, ncol = 14, byrow = TRUE)
head(boston, 3)
##         [,1] [,2] [,3] [,4]  [,5]  [,6] [,7]   [,8] [,9] [,10] [,11]  [,12]
## [1,] 0.00632   18 2.31    0 0.538 6.575 65.2 4.0900    1   296  15.3 396.90
## [2,] 0.02731    0 7.07    0 0.469 6.421 78.9 4.9671    2   242  17.8 396.90
## [3,] 0.02729    0 7.07    0 0.469 7.185 61.1 4.9671    2   242  17.8 392.83
##      [,13] [,14]
## [1,]  4.98  24.0
## [2,]  9.14  21.6
## [3,]  4.03  34.7

再一次呼叫原始網頁,讓我們再一次核對彼此的數字與出現順序:

knitr::include_url("http://lib.stat.cmu.edu/datasets/boston")

9.4.6 抓取波士頓數據集的欄位名稱

再抓一次原始檔案,並且觀察欄位相關資訊在哪裏?

x <- read.delim("http://lib.stat.cmu.edu/datasets/boston")
head(x, 21)
##           The.Boston.house.price.data.of.Harrison..D..and.Rubinfeld..D.L...Hedonic
## 1        prices and the demand for clean air', J. Environ. Economics & Management,
## 2    vol.5, 81-102, 1978.   Used in Belsley, Kuh & Welsch, 'Regression diagnostics
## 3       ...', Wiley, 1980.   N.B. Various transformations are used in the table on
## 4                                                     pages 244-261 of the latter.
## 5                                                              Variables in order:
## 6                                           CRIM     per capita crime rate by town
## 7        ZN       proportion of residential land zoned for lots over 25,000 sq.ft.
## 8                        INDUS    proportion of non-retail business acres per town
## 9   CHAS     Charles River dummy variable (= 1 if tract bounds river; 0 otherwise)
## 10                     NOX      nitric oxides concentration (parts per 10 million)
## 11                                   RM       average number of rooms per dwelling
## 12                 AGE      proportion of owner-occupied units built prior to 1940
## 13                   DIS      weighted distances to five Boston employment centres
## 14                              RAD      index of accessibility to radial highways
## 15                               TAX      full-value property-tax rate per $10,000
## 16                                            PTRATIO  pupil-teacher ratio by town
## 17         B        1000(Bk - 0.63)^2 where Bk is the proportion of blacks by town
## 18                                       LSTAT    % lower status of the population
## 19                        MEDV     Median value of owner-occupied homes in $1000's
## 20      0.00632  18.00   2.310  0  0.5380  6.5750  65.20  4.0900   1  296.0  15.30
## 21                                                            396.90   4.98  24.00

請讀者諸君現在跟畫面互動、互動,數一數欄位名稱位於哪幾列?加上我們已經知道「read.delim」會給我們「factor」的「x」,所以照例

  1. 取出「x」的那一段(各位剛剛數出來的結果)、
  2. 強迫變成「文字(as.character)」、
  3. 把結果丟給(<-)「colsBoston」,「colsBoston」自取,但是讀者諸君在關鍵時刻要取對、取好「名字」,除了練習取名字,也學英文,也學溝通,請讀者諸君慎思!
colsBoston <- as.character(x[6:19,])
colsBoston
##  [1] " CRIM     per capita crime rate by town"                                        
##  [2] " ZN       proportion of residential land zoned for lots over 25,000 sq.ft."     
##  [3] " INDUS    proportion of non-retail business acres per town"                     
##  [4] " CHAS     Charles River dummy variable (= 1 if tract bounds river; 0 otherwise)"
##  [5] " NOX      nitric oxides concentration (parts per 10 million)"                   
##  [6] " RM       average number of rooms per dwelling"                                 
##  [7] " AGE      proportion of owner-occupied units built prior to 1940"               
##  [8] " DIS      weighted distances to five Boston employment centres"                 
##  [9] " RAD      index of accessibility to radial highways"                            
## [10] " TAX      full-value property-tax rate per $10,000"                             
## [11] " PTRATIO  pupil-teacher ratio by town"                                          
## [12] " B        1000(Bk - 0.63)^2 where Bk is the proportion of blacks by town"       
## [13] " LSTAT    % lower status of the population"                                     
## [14] " MEDV     Median value of owner-occupied homes in $1000's"
colsBostonFull <- colsBoston # 留個備份,方便師長後續講解。

抓一個來好好瞧瞧,

colsBoston[1]
## [1] " CRIM     per capita crime rate by town"

一開始是「CRIM」,接下來「一段空白」,再來是「一句英文」,

per capita crime rate by town

應該是「波士頓數據集的主人、擁有者或是編輯者」試著解釋什麼是「CRIM」,一般我們會說是「欄位說明」,而「CRIM」是「欄位名稱」,其中「欄位」是統計學的「變數」,所以「欄位名稱」是「變數名稱」。回憶一下,之前我們

  1. 接起來,「paste」。
  2. 抓出來,「Numextract」。
  3. 變回來,「as.numeric」。

一樣是「一串文字」,但是現在「不是數字問題」,純粹是「文字問題」。如果我們可以「抓到CRIM」,再重複14次,我們就成功了!先「拆字」,

strsplit(colsBoston[1], " ")
## [[1]]
##  [1] ""       "CRIM"   ""       ""       ""       ""       "per"    "capita"
##  [9] "crime"  "rate"   "by"     "town"

再看第二條「文字串」,

strsplit(colsBoston[2], " ")
## [[1]]
##  [1] ""            "ZN"          ""            ""            ""           
##  [6] ""            ""            ""            "proportion"  "of"         
## [11] "residential" "land"        "zoned"       "for"         "lots"       
## [16] "over"        "25,000"      "sq.ft."

為了強化猜測與信心,我們再看第三條「文字串」,

strsplit(colsBoston[3], " ")
## [[1]]
##  [1] ""           "INDUS"      ""           ""           ""          
##  [6] "proportion" "of"         "non-retail" "business"   "acres"     
## [11] "per"        "town"

「觀察」過後,我們發現「欄位名稱」,也就是「變數名稱」,出現在

  1. [[1]]
  2. ""

之後。

怎麼「抓到它呢?」

strsplit(colsBoston[1], " ")
## [[1]]
##  [1] ""       "CRIM"   ""       ""       ""       ""       "per"    "capita"
##  [9] "crime"  "rate"   "by"     "town"
strsplit(colsBoston[1], " ")[[1]]
##  [1] ""       "CRIM"   ""       ""       ""       ""       "per"    "capita"
##  [9] "crime"  "rate"   "by"     "town"
strsplit(colsBoston[1], " ")[[1]][2]
## [1] "CRIM"

接下來,「重複」這些動作14次:

  1. 拆字,「strsplit」、
  2. 抓全部,「[[1]]」、
  3. 抓到它,「[2]」。

請看R的表演,用「迴圈」(sapply)抓全部欄位名稱。

colsBoston <- sapply(colsBoston, function(w){
  strsplit(w, " ")[[1]][2]
})
colsBoston
##                                          CRIM     per capita crime rate by town 
##                                                                          "CRIM" 
##       ZN       proportion of residential land zoned for lots over 25,000 sq.ft. 
##                                                                            "ZN" 
##                       INDUS    proportion of non-retail business acres per town 
##                                                                         "INDUS" 
##  CHAS     Charles River dummy variable (= 1 if tract bounds river; 0 otherwise) 
##                                                                          "CHAS" 
##                     NOX      nitric oxides concentration (parts per 10 million) 
##                                                                           "NOX" 
##                                   RM       average number of rooms per dwelling 
##                                                                            "RM" 
##                 AGE      proportion of owner-occupied units built prior to 1940 
##                                                                           "AGE" 
##                   DIS      weighted distances to five Boston employment centres 
##                                                                           "DIS" 
##                              RAD      index of accessibility to radial highways 
##                                                                           "RAD" 
##                               TAX      full-value property-tax rate per $10,000 
##                                                                           "TAX" 
##                                            PTRATIO  pupil-teacher ratio by town 
##                                                                       "PTRATIO" 
##         B        1000(Bk - 0.63)^2 where Bk is the proportion of blacks by town 
##                                                                             "B" 
##                                       LSTAT    % lower status of the population 
##                                                                         "LSTAT" 
##                        MEDV     Median value of owner-occupied homes in $1000's 
##                                                                          "MEDV"
colsBoston <- unname(colsBoston) # 無名化。
colsBoston
##  [1] "CRIM"    "ZN"      "INDUS"   "CHAS"    "NOX"     "RM"      "AGE"    
##  [8] "DIS"     "RAD"     "TAX"     "PTRATIO" "B"       "LSTAT"   "MEDV"

大成功!

sapply」非常好用。如果讀者諸君想進一步「升級」,請參考以下的官方使用手冊:

knitr::include_url("https://www.rdocumentation.org/packages/base/versions/3.6.2/topics/lapply")

9.4.7boston變成data.frame

回顧到目前為止,「我們的boston」長什麼樣子?

class(boston)
## [1] "matrix" "array"

也檢查「MASSBoston」長什麼樣子?

class(MASS::Boston)
## [1] "data.frame"

把「我們的boston」的樣子變成「MASSBoston」的樣子:

boston <- as.data.frame(boston)
class(boston)
## [1] "data.frame"

繼續揭露「我們的boston」跟「MASSBoston」之間的「同」與「異」:

sum(dim(boston) == dim(MASS::Boston))
## [1] 2
sum(rownames(boston) == rownames(MASS::Boston))
## [1] 506
sum(colnames(boston) == colnames(MASS::Boston))
## [1] 0

最後的答案「0」,意味著

「我們的boston」的「變數名稱」跟「MASSBoston」的「變數名稱」,完全不一樣!

colnames(boston) # 這是R的傑作!
##  [1] "V1"  "V2"  "V3"  "V4"  "V5"  "V6"  "V7"  "V8"  "V9"  "V10" "V11" "V12"
## [13] "V13" "V14"
colnames(MASS::Boston) # 不再是大寫,而是小寫的變數名稱。
##  [1] "crim"    "zn"      "indus"   "chas"    "nox"     "rm"      "age"    
##  [8] "dis"     "rad"     "tax"     "ptratio" "black"   "lstat"   "medv"

我們這麼改:

colnames(boston) <- tolower(colsBoston)
head(boston, 3)
##      crim zn indus chas   nox    rm  age    dis rad tax ptratio      b lstat
## 1 0.00632 18  2.31    0 0.538 6.575 65.2 4.0900   1 296    15.3 396.90  4.98
## 2 0.02731  0  7.07    0 0.469 6.421 78.9 4.9671   2 242    17.8 396.90  9.14
## 3 0.02729  0  7.07    0 0.469 7.185 61.1 4.9671   2 242    17.8 392.83  4.03
##   medv
## 1 24.0
## 2 21.6
## 3 34.7
head(MASS::Boston, 3)
##      crim zn indus chas   nox    rm  age    dis rad tax ptratio  black lstat
## 1 0.00632 18  2.31    0 0.538 6.575 65.2 4.0900   1 296    15.3 396.90  4.98
## 2 0.02731  0  7.07    0 0.469 6.421 78.9 4.9671   2 242    17.8 396.90  9.14
## 3 0.02729  0  7.07    0 0.469 7.185 61.1 4.9671   2 242    17.8 392.83  4.03
##   medv
## 1 24.0
## 2 21.6
## 3 34.7
tail(boston, 3)
##        crim zn indus chas   nox    rm  age    dis rad tax ptratio      b lstat
## 504 0.06076  0 11.93    0 0.573 6.976 91.0 2.1675   1 273      21 396.90  5.64
## 505 0.10959  0 11.93    0 0.573 6.794 89.3 2.3889   1 273      21 393.45  6.48
## 506 0.04741  0 11.93    0 0.573 6.030 80.8 2.5050   1 273      21 396.90  7.88
##     medv
## 504 23.9
## 505 22.0
## 506 11.9
tail(MASS::Boston, 3)
##        crim zn indus chas   nox    rm  age    dis rad tax ptratio  black lstat
## 504 0.06076  0 11.93    0 0.573 6.976 91.0 2.1675   1 273      21 396.90  5.64
## 505 0.10959  0 11.93    0 0.573 6.794 89.3 2.3889   1 273      21 393.45  6.48
## 506 0.04741  0 11.93    0 0.573 6.030 80.8 2.5050   1 273      21 396.90  7.88
##     medv
## 504 23.9
## 505 22.0
## 506 11.9

請讀者諸君確認成果!

「差一點」一模一樣!

colnames(boston)[12] <- "black"
sum(colnames(boston) == colnames(MASS::Boston))
## [1] 14

最後再檢查一件事:

sapply(boston, class)
##      crim        zn     indus      chas       nox        rm       age       dis 
## "numeric" "numeric" "numeric" "numeric" "numeric" "numeric" "numeric" "numeric" 
##       rad       tax   ptratio     black     lstat      medv 
## "numeric" "numeric" "numeric" "numeric" "numeric" "numeric"
sapply(MASS::Boston, class)
##      crim        zn     indus      chas       nox        rm       age       dis 
## "numeric" "numeric" "numeric" "integer" "numeric" "numeric" "numeric" "numeric" 
##       rad       tax   ptratio     black     lstat      medv 
## "integer" "numeric" "numeric" "numeric" "numeric" "numeric"

再一次確認一件事,

「我們的boston」的「變數屬性(class)」跟「MASSBoston」的「變數屬性(class)」,也「差兩點」完全一樣,不是「numeric(實數型)」的變數,就是「integer(整數型)」的變數!

boston[,"chas"] <- as.integer(boston[,"chas"])
boston[,"rad"] <- as.integer(boston[,"rad"])
sum(sapply(boston, class) == sapply(MASS::Boston, class))
## [1] 14

9.4.8 課堂練習

  1. 整理上述作者小編提供的程式碼,前進並且用R下載boston_corrected提供的數據集。這是波士頓數據集的完整版。
  2. 檢查前一題的成果,看是否跟套件「spData」(spData)抓到的「一模一樣」?

9.4.9 小結語

總整理整個「耙蟲」過程的程式碼,請注意「#」之後的「註解」:

# 大步一:下載。
URL <- "http://lib.stat.cmu.edu/datasets/boston"
x <- read.delim(URL)
# 大步二:抓變數名稱。
colsBoston <- as.character(x[6:19,])
colsBoston <- sapply(colsBoston, function(w){
  strsplit(w, " ")[[1]][2]
})
colsBoston <- unname(colsBoston)
# 大步三:抓數字。
x <- x[-(1:19),,drop = FALSE]
rownames(x) <- 1:dim(x)[1]
colnames(x) <- "Boston"
### 當然我們需要先告訴R,如何從文字串取出眾多數字。
x <- as.numeric(
  Numextract(
    paste(x[seq(1,dim(x)[1],2),"Boston"], x[seq(2,dim(x)[1],2),"Boston"])
    )
  )
boston <- matrix(x, ncol = 14, byrow = TRUE)
boston <- as.data.frame(boston)
### 根據「MASS::Boston」的規則改變boston的變數名稱。
colnames(boston) <- tolower(colsBoston)
colnames(boston)[12] <- "black"
boston[,"chas"] <- as.integer(boston[,"chas"])
boston[,"rad"] <- as.integer(boston[,"rad"])

最後,一定要,檢查成果:

sum(dim(boston) == dim(MASS::Boston))
## [1] 2
sum(rownames(boston) == rownames(MASS::Boston))
## [1] 506
sum(colnames(boston) == colnames(MASS::Boston))
## [1] 14
sum(sapply(boston, class) == sapply(MASS::Boston, class))
## [1] 14
sum(boston == MASS::Boston) == 14*506 # 一定要是「14 x 506」
## [1] TRUE

挑戰成功!

9.5 學期小專題

9.5.1 開啟專案暨引進專案管理

開啟:

  1. FMsources
  2. FMfinal
  3. FMfun

等三個專案,其中「FMsources」專職保留原始數據;「FMfinal」專職個人期末報告;「FMfun」專職任何學生個人有興趣的題目。以上這三個專案都是多變量分析這一門課的專案,為了可以跟其他課程區隔開來,通常我們會給多變量分析一個專用的檔案夾,所以在開啟上述三個專案之前,請先開啟

MVA

這一個檔案夾。然後,在這一個檔案夾底下開啟上述三個專案。一般而言,不管是哪一種企圖的專案,除非有更好的理由,作者小編都會開啟「Shiny Web Application」專案,因為

接下來,建置數個各司其職的檔案夾:

  1. data:放數據集。
  2. Rscript:放程式碼。
  3. output:
  4. output/data:放表、報表。
  5. output/figures:放圖。
  6. doc:放文件。
  7. www:放參考資料,諸如PDF檔案。
  8. UFO:這是垃圾桶。

我們可以手動完成以上全部「開啟」的動作,也可以在程式的協助下完成整個開啟任務。請參考以下作者小編的作法:

  1. 由於作者小編依靠一部「shiny server」開發各種程式,所以先手動在「ShinyApps」下手動開啟「MVA」檔案夾。
  2. 手動在「MVA」檔案夾之下,開啟第一個「Shiny Web Application」專案,「FMsources」。
  3. 手動在「MVA」檔案夾之下,開啟第二個「Shiny Web Application」專案,「FMfinal」。
  4. 手動在「MVA」檔案夾之下,開啟第三個「Shiny Web Application」專案,「FMfun」。
  5. 設定工作檔案夾。注意,必須根據個人現況或是個人習慣改掉「~/ShinyApps」。還有,不一樣的作業系統(OS,諸如,Windows 10, MacOS X, Linux, …),路徑寫法不盡相同。
setwd("~/ShinyApps/MVA")
  1. 開啟空白「R Script」,並且命名為「佈局MVA學生版.R」。
  2. 然後,適當改寫以下程式碼,讓程式幫你完成剩下來的工作:
projects <- list.files("~/ShinyApps/MVA")
projects <- projects[-grep("佈局MVA學生版.R", projects)]

for (i in 1:length(projects)) {
  
  DIR <- paste0("/home/fcu/ShinyApps/MVA",
                "/",
                projects[i],
                "/",
                "data")
  
  if(!dir.exists(DIR)){
    dir.create(DIR)
    dir.create(sub("data", "Rscript", DIR))
    dir.create(sub("data", "output", DIR))
    dir.create(sub("data", "output/data", DIR))
    dir.create(sub("data", "output/figures", DIR))
    dir.create(sub("data", "doc", DIR))
    
    dir.create(sub("data", "UFO", DIR))
    dir.create(sub("data", "www", DIR))
    
    from <- sub("data", "app.R", DIR)
    to <- sub("data", "UFO/app.R", DIR)
    file.copy(from = from, to = to)
    file.remove(from)
  }
  
}
  1. 順利成功之後,開啟、前進「FMfinal」專案。

9.5.2 準備數據

順應2021年多變量分析的課程設計,第三版線上教室在「共同學習專區」提供「下載資料區」,讓大家在那裏下載上課學習、課堂練習、課外作業,甚至是各種考試所需的數據集。比如說,第一週可以下載以下的數據集:

1+1
## [1] 2

其中,

9.5.2.1 前進下載並置入專案FMsources

list.files("/home/fcu/ShinyApps/MVA/FMsources/data")
## character(0)

9.5.2.2 問題一:從數據庫拷貝數據到創作專案

9.6 課後練習題