1 課程目標

本週上課將介紹R的控制流程,也就是條件判斷與迴圈,讓電腦可以按照給定的邏輯,反覆執行指令,一直到滿足條件為止,這對於運算以及擷取資料非常有幫助。例如我們隨機找到10位民眾,想依據民眾的年齡是否在18歲以上,判斷是否應該訪問他們,並且顯示成為一個資料框,可寫語法運算如下:

age<-c(34, 12, 19, 21, 22, 30, 16, 18, 17, 39)
 f<-function(x){
  interview<-ifelse(x>=18, "Yes", "No")
  return(data.frame(Age=x, Interview=interview, row.names=NULL))
 }
 
f(age)
##    Age Interview
## 1   34       Yes
## 2   12        No
## 3   19       Yes
## 4   21       Yes
## 5   22       Yes
## 6   30       Yes
## 7   16        No
## 8   18       Yes
## 9   17        No
## 10  39       Yes

在介紹R的控制流程之前,先介紹資料的子集合。


2 資料的子集合 (Subsetting)

從一筆資料取出部分資料,稱謂資料的子集合動作(Subsetting)。子集合可幫助 後續的資料分析,因為很多資料裡面只有一部分是我們所需要的。
在DSC2014Tutorial的套件中,輸入slides(‘Basic’),就可以學習到許多資料子集合的技巧。在此簡單敘述。

2.1 向量子集合

向量的子集合方式可以分成用索引以及which()函數方式:

2.1.1 索引

向量的數學表示方式為 \(x_{i}\),其中下標i表示向量的第幾個元素,我們稱之為索引。從向量取出部分元素的方法就是以括號的方式,例如:

state.name[1]
## [1] "Alabama"
state.abb[1:4]
## [1] "AL" "AK" "AZ" "AR"
sleep$extra[nrow(sleep)]
## [1] 3.4

2.1.2 which()

which()適用於條件篩選向量內的元素,例如我們想找出哪些美國的州名是以B跟C開頭,可寫語法如下:

state.abb[1:10]
##  [1] "AL" "AK" "AZ" "AR" "CA" "CO" "CT" "DE" "FL" "GA"
state.abb.abb<-substr(state.abb, 1,1)
state.abb[which(state.abb.abb=="B")]
## character(0)
state.abb[which(state.abb.abb=="C")]
## [1] "CA" "CO" "CT"

這裡使用到substr(A, i, j)這個指令,A是字串向量,i是開始擷取向量內的文字的順位,j是結束擷取的位置,我們擷取每一個州名稱的第一個字母,然後存成用state.abb.abb這個向量,再用which()函數,對原本的state.abb向量配對。
請嘗試找出有幾個州的面積大於10萬平方英哩

2.2 列表子集合

請先建立一個列表:

ListA<-list(height=90, width=120, string=state.abb[1:2], data=state.area)
class(ListA)
## [1] "list"

2.2.1 索引

從列表中以索引方式取出資料,格式可以是列表或者是數值、字串

ListA[c(1,2)]
## $height
## [1] 90
## 
## $width
## [1] 120
ListA[[3]]
## [1] "AL" "AK"
ListA[4]
## $data
##  [1]  51609 589757 113909  53104 158693 104247   5009   2057  58560  58876
## [11]   6450  83557  56400  36291  56290  82264  40395  48523  33215  10577
## [21]   8257  58216  84068  47716  69686 147138  77227 110540   9304   7836
## [31] 121666  49576  52586  70665  41222  69919  96981  45333   1214  31055
## [41]  77047  42244 267339  84916   9609  40815  68192  24181  56154  97914

或者直接使用列表中的名稱:

ListA["height"]
## $height
## [1] 90
ListA[["width"]]
## [1] 120

2.2.2 which()

使用which()篩選列表的資料之前,需要先轉換列表為資料框,然後把因素轉換為數值,而R會按照因素的層級排序,從1開始:

dfA<-data.frame(value=unlist(ListA))
dfA$value
##  [1] 90     120    AL     AK     51609  589757 113909 53104  158693 104247
## [11] 5009   2057   58560  58876  6450   83557  56400  36291  56290  82264 
## [21] 40395  48523  33215  10577  8257   58216  84068  47716  69686  147138
## [31] 77227  110540 9304   7836   121666 49576  52586  70665  41222  69919 
## [41] 96981  45333  1214   31055  77047  42244  267339 84916  9609   40815 
## [51] 68192  24181  56154  97914 
## 54 Levels: 104247 10577 110540 113909 120 1214 121666 147138 ... AL
dfA$data<-as.numeric(dfA$value)
dfA$data[1:6]
## [1] 48  5 54 53 25 34
dfA[which(dfA$data<10),]
##         value data
## width     120    5
## data3  113909    4
## data5  158693    9
## data6  104247    1
## data20  10577    2
## data26 147138    8
## data28 110540    3
## data31 121666    7
## data39   1214    6

以上是一維的資料,接下來介紹二維的資料子集合。


2.3 矩陣、陣列子集合

2.3.1 索引

矩陣可以表示為:
\[\begin{bmatrix} x_{11} & x_{12} & x_{13} & \dots & x_{1c} \\ x_{21} & x_{22} & x_{23} & \dots & x_{2c} \\ \ldots \\ x_{r1} & x_{r2} & x_{r3} & \dots & x_{rc} \end{bmatrix}\]

\(x_{11},\ldots, x_{r1}\)來自同樣的行(column),而\(x_{11},\ldots, x_{1c}\)來自同樣的列,所以前者可以用\(x_{,1}\)表示,後者\(x_{1,}\)表示。
假設有一個\(3\times 3\)的矩陣,我們用括號來取出其中的一個或是多個數值,或者加以替換:

m1<-matrix(c(1:9), 3, 3)
m1
##      [,1] [,2] [,3]
## [1,]    1    4    7
## [2,]    2    5    8
## [3,]    3    6    9
print(m1[2,2]) #1
## [1] 5
print(m1[c(1:2)]) #2
## [1] 1 2
print(m1[c(1,2),c(1,2)]) #3
##      [,1] [,2]
## [1,]    1    4
## [2,]    2    5
print(m1[c(1,3),c(1,3)]) #4
##      [,1] [,2]
## [1,]    1    7
## [2,]    3    9
print(m1[,1]) #5
## [1] 1 2 3
m1[3,3]<-"Hello" #6
m1
##      [,1] [,2] [,3]   
## [1,] "1"  "4"  "7"    
## [2,] "2"  "5"  "8"    
## [3,] "3"  "6"  "Hello"

以上面語法中第3題的[c(1,2),c(1,2)]為例,矩陣對應索引的方式為(1,1),(2,1),(1,2),(2,2),因此得到

\[\begin{bmatrix} 1 & 4 \\ 2 & 5 \end{bmatrix}\]

2.3.2 which()

對於矩陣或是陣列,R可以傳回每一個符合條件的行與列的對應資料,例如:

T <- array(1:20, dim=c(4,5))
which(T >= 15, arr.ind = T)
##      row col
## [1,]   3   4
## [2,]   4   4
## [3,]   1   5
## [4,]   2   5
## [5,]   3   5
## [6,]   4   5
T[which(T[,5]>=15), ]
##      [,1] [,2] [,3] [,4] [,5]
## [1,]    1    5    9   13   17
## [2,]    2    6   10   14   18
## [3,]    3    7   11   15   19
## [4,]    4    8   12   16   20

2.4 資料框子集合

資料框與矩陣類似,都有兩個象限,所以子集合的方法相近。

2.4.1 索引

索引對象可以是資料框的行或是列的編號,也可以是欄位名稱,注意逗號前面是列,後面是行:

data(sleep)
names(sleep)
## [1] "extra" "group" "ID"
sleep[1:3, ]
##   extra group ID
## 1   0.7     1  1
## 2  -1.6     1  2
## 3  -0.2     1  3
sleep[, "extra"]
##  [1]  0.7 -1.6 -0.2 -1.2 -0.1  3.4  3.7  0.8  0.0  2.0  1.9  0.8  1.1  0.1
## [15] -0.1  4.4  5.5  1.6  4.6  3.4

2.4.2 %in%

當資料有一欄編號,可以用\(\%\textrm{in}\%\)方式擷取特定列的資料:

head(sleep)
##   extra group ID
## 1   0.7     1  1
## 2  -1.6     1  2
## 3  -0.2     1  3
## 4  -1.2     1  4
## 5  -0.1     1  5
## 6   3.4     1  6
sleep[sleep$ID %in% c(1,2,3), ] #select by ID
##    extra group ID
## 1    0.7     1  1
## 2   -1.6     1  2
## 3   -0.2     1  3
## 11   1.9     2  1
## 12   0.8     2  2
## 13   1.1     2  3
sleep[sleep$ID %in% c(1,2,3) & sleep$group %in% c(1), ] # two conditions
##   extra group ID
## 1   0.7     1  1
## 2  -1.6     1  2
## 3  -0.2     1  3

\(\%\textrm{in}\%\)的意義是「屬於」,例如:

A<-c(0:5); B<-c(1:20)
A%in%B
## [1] FALSE  TRUE  TRUE  TRUE  TRUE  TRUE

應用到資料框的子集合,則是列的序號對應為真則留下,為偽則不留下。

2.4.3 which()

which()可容納超過一個以上的條件,而且可以用「且」或是「或」連結條件與條件。針對資料框,可以比對出符合每一個條件的「列」,並且傳回向量。研究者可以由此取出對應的列。

cond <- which(sleep$extra>0.5 & sleep$group==1)
sleep[cond, ]
##    extra group ID
## 1    0.7     1  1
## 6    3.4     1  6
## 7    3.7     1  7
## 8    0.8     1  8
## 10   2.0     1 10

比較\(\%\textrm{in}\%\)以及which(),可得到相同結果:

mtcars[mtcars$cyl %in% c(4,8), ]
##                      mpg cyl  disp  hp drat    wt  qsec vs am gear carb
## Datsun 710          22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
## Hornet Sportabout   18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2
## Duster 360          14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4
## Merc 240D           24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2
## Merc 230            22.8   4 140.8  95 3.92 3.150 22.90  1  0    4    2
## Merc 450SE          16.4   8 275.8 180 3.07 4.070 17.40  0  0    3    3
## Merc 450SL          17.3   8 275.8 180 3.07 3.730 17.60  0  0    3    3
## Merc 450SLC         15.2   8 275.8 180 3.07 3.780 18.00  0  0    3    3
## Cadillac Fleetwood  10.4   8 472.0 205 2.93 5.250 17.98  0  0    3    4
## Lincoln Continental 10.4   8 460.0 215 3.00 5.424 17.82  0  0    3    4
## Chrysler Imperial   14.7   8 440.0 230 3.23 5.345 17.42  0  0    3    4
## Fiat 128            32.4   4  78.7  66 4.08 2.200 19.47  1  1    4    1
## Honda Civic         30.4   4  75.7  52 4.93 1.615 18.52  1  1    4    2
## Toyota Corolla      33.9   4  71.1  65 4.22 1.835 19.90  1  1    4    1
## Toyota Corona       21.5   4 120.1  97 3.70 2.465 20.01  1  0    3    1
## Dodge Challenger    15.5   8 318.0 150 2.76 3.520 16.87  0  0    3    2
## AMC Javelin         15.2   8 304.0 150 3.15 3.435 17.30  0  0    3    2
## Camaro Z28          13.3   8 350.0 245 3.73 3.840 15.41  0  0    3    4
## Pontiac Firebird    19.2   8 400.0 175 3.08 3.845 17.05  0  0    3    2
## Fiat X1-9           27.3   4  79.0  66 4.08 1.935 18.90  1  1    4    1
## Porsche 914-2       26.0   4 120.3  91 4.43 2.140 16.70  0  1    5    2
## Lotus Europa        30.4   4  95.1 113 3.77 1.513 16.90  1  1    5    2
## Ford Pantera L      15.8   8 351.0 264 4.22 3.170 14.50  0  1    5    4
## Maserati Bora       15.0   8 301.0 335 3.54 3.570 14.60  0  1    5    8
## Volvo 142E          21.4   4 121.0 109 4.11 2.780 18.60  1  1    4    2
mtcars[which(mtcars$cyl==4 | mtcars$cyl==8) , ]
##                      mpg cyl  disp  hp drat    wt  qsec vs am gear carb
## Datsun 710          22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
## Hornet Sportabout   18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2
## Duster 360          14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4
## Merc 240D           24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2
## Merc 230            22.8   4 140.8  95 3.92 3.150 22.90  1  0    4    2
## Merc 450SE          16.4   8 275.8 180 3.07 4.070 17.40  0  0    3    3
## Merc 450SL          17.3   8 275.8 180 3.07 3.730 17.60  0  0    3    3
## Merc 450SLC         15.2   8 275.8 180 3.07 3.780 18.00  0  0    3    3
## Cadillac Fleetwood  10.4   8 472.0 205 2.93 5.250 17.98  0  0    3    4
## Lincoln Continental 10.4   8 460.0 215 3.00 5.424 17.82  0  0    3    4
## Chrysler Imperial   14.7   8 440.0 230 3.23 5.345 17.42  0  0    3    4
## Fiat 128            32.4   4  78.7  66 4.08 2.200 19.47  1  1    4    1
## Honda Civic         30.4   4  75.7  52 4.93 1.615 18.52  1  1    4    2
## Toyota Corolla      33.9   4  71.1  65 4.22 1.835 19.90  1  1    4    1
## Toyota Corona       21.5   4 120.1  97 3.70 2.465 20.01  1  0    3    1
## Dodge Challenger    15.5   8 318.0 150 2.76 3.520 16.87  0  0    3    2
## AMC Javelin         15.2   8 304.0 150 3.15 3.435 17.30  0  0    3    2
## Camaro Z28          13.3   8 350.0 245 3.73 3.840 15.41  0  0    3    4
## Pontiac Firebird    19.2   8 400.0 175 3.08 3.845 17.05  0  0    3    2
## Fiat X1-9           27.3   4  79.0  66 4.08 1.935 18.90  1  1    4    1
## Porsche 914-2       26.0   4 120.3  91 4.43 2.140 16.70  0  1    5    2
## Lotus Europa        30.4   4  95.1 113 3.77 1.513 16.90  1  1    5    2
## Ford Pantera L      15.8   8 351.0 264 4.22 3.170 14.50  0  1    5    4
## Maserati Bora       15.0   8 301.0 335 3.54 3.570 14.60  0  1    5    8
## Volvo 142E          21.4   4 121.0 109 4.11 2.780 18.60  1  1    4    2

3 條件判斷

3.1 ifelse()

ifelse()適用在「非A即B」的邏輯,也就是若A為真則進行某一動作,若A不為真則不進行。
例如面訪員到某一個家戶訪問,裡面有四人,訪員需要戶中抽樣,但是前提是只訪問18歲以上成年人,語法可以這樣寫:

x=c(20, 50, 16, 18)
interview<-ifelse(x>=18, "Yes", "No")
print(interview)
## [1] "Yes" "Yes" "No"  "Yes"

ifelse()可以判斷一個向量的真偽,其功能類似先前提到的變數轉換:

vote <- rep(NA, 3)
vote[x>=18]<-"Yes"
vote[x<18]<-"No"
vote
## [1] "Yes" "Yes" "No"  "Yes"

ifelse()也可以轉換字串為字串或者是數字,也可以把日期轉換為字串或是數字。

S<-c("2018-01-01", "2018-02-01", "2018-03-01", "2018-04-01")
S <- as.Date(S, format='%Y-%m-%d')
day<-as.Date("2018/02/28", format='%Y/%m/%d')
new.S<-ifelse(difftime(day, S)>=0, "Earlier", "Later")
new.S
## [1] "Earlier" "Earlier" "Later"   "Later"

當條件成立時的執行的動作可以是函數,例如在日光節約時間結束之後,下午茶時間延後一個小時。假設日光節約時間在11月5日結束,三個日期的下午茶時間可計算如下:

ds<-as.POSIXct("2018-11-05 00:00:00", format = "%Y-%m-%d %H:%M:%S", tz="Asia/Taipei")

teatime<-as.POSIXct(c("2018-07-01 13:50:00", "2018-12-01 14:10:00", "2019-02-02 14:15:00"), 
                    format = "%Y-%m-%d %H:%M:%S", tz="Asia/Taipei")

hrs <- function(u) {
 x <- u * 3600
 return(x)
}

ifelse(difftime(teatime, ds)>=0, paste(teatime+hrs(1)), paste(teatime))
## [1] "2018-07-01 13:50:00" "2018-12-01 15:10:00" "2019-02-02 15:15:00"

請注意時間的格式,以及設定時區的函數as.POSIXct()。另外,paste()函數可貼上計算結果。

3.2 if-else

if-else 可以幫助我們建立一個條件式的函數,當函數內的向量滿足某個條件,便會進行某一個動作。

temperature<-30 
if (temperature>28){
   cat ("Turn on air condition")
}else {
  cat ("Turn off air condition")
}
## Turn on air condition

也可以進行運算

speed<-50 
if (speed>=70){
     speed
}else {
    speed*1.6
}
## [1] 80

如果進行迴圈的條件是向量中的某一個或是所有元素符合某項條件,其他元素也會隨之進行我們希望的動作。例如警察測量許多輛車的速度,如果是任何一輛車超過時速110就顯示最高速的那一輛車速,反之顯示最慢的那一輛車速:

speed<-c(77, 90, 100, 120, 155) 
if (any(speed>=110)){
     y=max(speed)
} else{
    y=min(speed)
}
 y
## [1] 155

此處使用any()這個函數,表示當物件或是多個物件滿足某一條件則回傳TRUE。例如:

A<-c(-1, 1.5); B<-c(2); C<-c('OK')
any (A>0)
## [1] TRUE
any (is.numeric(A) & is.numeric(B) & is.numeric(C))
## [1] FALSE
any (is.numeric(A) | is.numeric(B) | is.numeric(C))
## [1] TRUE

請練習如果「所有」車輛超過時速110,警察就會開罰單,顯示最快的車速,否則都不會開單。車速是130, 115, 120。

3.3 if-else if-else

if-else if-else 可以幫助我們建立一個條件式的函數,當函數內的向量滿足第一個條件,便會進行第一個動作, 滿足第二個條件,便會進行第二個動作,一直到結束。例如電影的長度如果長過180分鐘則是太長,165分鐘是長,不到165分鐘是短。

movie<-170 
if(movie>=180){
     cat('Very long')
    }else if(movie>=165){
    cat('Long')
  }else{
          cat('Short')
}
## Long

在寫if-else時,請注意}應該與else或是else if連在一起。
假設在入住前3個月(90天)訂飯店為原價的85折,2個月(60天)訂飯店為原價的9折,入住前1周到兩個月是原價,入住日期為1周內則是原價加上兩成。如果入住日期為今年5月20日,原價是3000元,請練習今天的日期以及隔兩週後的今天訂房的話,分別需要多少錢?


4 迴圈

4.1 for

for迴圈的功能是系統根據起始的條件,反覆進行同一動作。
例如重複顯示一個句子\(n\)次:

for (U in 1:5){
  cat("All work and no play","\n")
}
## All work and no play 
## All work and no play 
## All work and no play 
## All work and no play 
## All work and no play

在這個迴圈中,U是一個變數,雖然U沒有出現在後面的語法,但是系統會執行該語法所設定的次數。
也可以貼上次數:

for (i in 1:5){
  cat("All work and no play", paste(i), "times \n")
}
## All work and no play 1 times 
## All work and no play 2 times 
## All work and no play 3 times 
## All work and no play 4 times 
## All work and no play 5 times

在這個迴圈中,我們用paste()這個函數貼上\(i\)這個變數的值。
又例如從1加到10:

sum<-0
for (i in 1:10){
  sum = sum + i
  }
print(sum)
## [1] 55

4.1.1 隨機產生亂數

擲三顆公正的六面骰子1000次並且加總點數,將每一次得到的總和畫成長條圖:

set.seed(02138)
dice <- seq(1:6)
x <- c()
for (i in 1:1000){
  x[i]<-sum(sample(dice, 1), sample(dice, 1), sample(dice, 1)) 
}
# graphic
df<-data.frame(Dice=x)
library(ggplot2)
g <- ggplot(aes(Dice), data=df) + 
  geom_histogram(binwidth = 0.8, fill='lightgreen', aes(y=..density..), position="identity")
g

可以看出點數的總和近似常態分佈,集中在10點附近。
這個迴圈運用到「索引」的概念,紀錄每一次抽樣並且加總的結果,但是不需要顯示在螢幕上,而是成為一個向量,作為後續統計的資料。

4.1.2 for 與函數

例如撲克牌點數為1到13點,抽出三張牌,如果前兩張的點數總和小於17,而且其中一張牌小於10,那麼就抽第三張,然後顯示三張的總和;如果不符合前一個條件,那麼就顯示兩張的總和。當我們給定隨機亂數的數字,我們設定的條件式函數根據隨機亂數得到的結果輸出。

set.seed(02138)
card<-function(x) {
set.seed(x)
for (i in 1:3)
  x[i]<-sample(1:13, 1)
  if (x[1]+x[2]<17 & x[1]<10 | x[2]<10 ){
  print(x[1:3])
  cat(sum(x[1:3]),"is sum of three cards \n")
    }else {
        print(x[1:2])
        cat(sum(x[1:2]), "is sum of the first 2 cards \n")
        }
  }
card(100); card(5003); card(02138)
## [1] 5 4 8
## 17 is sum of three cards
## [1]  7 12
## 19 is sum of the first 2 cards
## [1] 7 8 5
## 20 is sum of three cards

上述的程式可以產生一個自訂函數card(),該函數的參數x是任意整數,將產生隨機亂數。這個迴圈同樣運用到「索引」的概念。

4.1.3 for 與 if-else if-else

某飯店單人房原價是3000元。假設在入住前3個月(90天)訂房為原價的85折,不到3個月但是超過2個月(60天)訂房為原價的9折,不到2個月但是超過1個月(30天)是原價,入住前1個月內則是原價加上兩成。如果入住日期為今年的12月31日、4月20日、5月20日、6月1日、6月30日以及隔一週的今天,請練習如果今天的日期訂房的話,分別需要多少錢?

如果把最早的日期放在第一個,迴圈會立刻停止

checkin<-c(checkin, today+7)
hotel(checkin)
## [1] "2018-12-31"
## -80 days: 3600 
## [1] "2018-04-20"
## -335 days: 3600 
## [1] "2018-05-20"
## -305 days: 3600 
## [1] "2018-06-01"
## -293 days: 3600 
## [1] "2018-06-30"
## -264 days: 3600 
## [1] "2019-03-28"
## 7 days: 3600 
## [1] "2019-03-28"
## 7 days: 3600

4.1.4 雙重迴圈

如果需要兩個變數才能產生所需要的結果,可以考慮迴圈之中的迴圈,例如我們想知道11到20的乘法表,需要兩個變數相乘:

multiplication <- matrix(nrow=10, ncol=10)
for (i in 1:dim(multiplication)[1]){
  for (j in 1:dim(multiplication)[2]){
    multiplication[i,j] <- (i+10)*(j+10)
  }
}
rownames(multiplication)<-c(11:20)
colnames(multiplication)<-c(11:20)
multiplication
##     11  12  13  14  15  16  17  18  19  20
## 11 121 132 143 154 165 176 187 198 209 220
## 12 132 144 156 168 180 192 204 216 228 240
## 13 143 156 169 182 195 208 221 234 247 260
## 14 154 168 182 196 210 224 238 252 266 280
## 15 165 180 195 210 225 240 255 270 285 300
## 16 176 192 208 224 240 256 272 288 304 320
## 17 187 204 221 238 255 272 289 306 323 340
## 18 198 216 234 252 270 288 306 324 342 360
## 19 209 228 247 266 285 304 323 342 361 380
## 20 220 240 260 280 300 320 340 360 380 400

請嘗試用陣列加上迴圈產生三維的資料,例如我們模擬兩種隨機抽樣方式,第一種方式從5種隨機分佈每次抽出1, 10, 100, 200, 500, 1000個樣本,然後計算其平均值。第二種方式除了從5種隨機分佈每次抽出1, 10, 100, 200, 500, 1000個樣本,然後計算其平均值,還要重複以上步驟10, 100,1000次,分別計算平均值。

set.seed(02138)
sampleresult <- matrix(nrow=6, ncol=5)
R<-c(1, 10, 100, 200, 500, 1000)
L<-list(rnorm(1e+04,0,1), rnorm(1e+05,0,1), rnorm(1e+06,0,1), rnorm(1e+07,0,1), rnorm(1e+08,0,1))

for (i in 1:length(R)){
  for (j in 1:5){
    sampleresult[i,j] <- mean(sample (L[[j]], size=R[i], replace=T))
  }
}
sampleresult

#replication
sampleresult2 <- array(dim=c(6, 5, 3))
S<-c(10,100, 1000)
for (i in 1:length(R)){
  for (j in 1:5){
    for(s in 1:length(S)){
      su<-c();
    sampleresult2[i,j,s] <- mean({su[s]=mean(sample (L[[j]], size=R[i], replace=T))})
  }
  }
}
sampleresult2

4.1.5 清理資料

for迴圈可以幫助我們清理資料,例如讀取一筆23個縣市的統計資料:

library(foreign)
stat.dat<-read.csv("CS3171D1A.csv",header=TRUE,sep=";",dec=".",fileEncoding="BIG5")
head(stat.dat)
##                        X 臺北縣 宜蘭縣 桃園縣 新竹縣 苗栗縣 臺中縣 彰化縣
## 1 老年人口比率(65歲以上)     NA     NA     NA     NA     NA     NA     NA
## 2                   2000   6.37  10.20   7.46   9.69  10.98   7.16   9.42
## 3                   2001   6.44  10.49   7.49   9.91  11.21   7.32   9.73
## 4                   2002   6.55  10.82   7.51  10.17  11.57   7.50  10.03
## 5                   2003   6.67  11.17   7.56  10.39  11.87   7.68  10.31
## 6                   2004   6.86  11.54   7.62  10.58  12.19   7.90  10.65
##   南投縣 雲林縣 嘉義縣 臺南縣 高雄縣 屏東縣 臺東縣 花蓮縣 澎湖縣 基隆市
## 1     NA     NA     NA     NA     NA     NA     NA     NA     NA     NA
## 2  10.60  11.61  12.41  10.75   8.35  10.00  11.27  10.73  14.40   8.81
## 3  10.90  11.99  12.75  11.04   8.52  10.25  11.40  10.83  14.29   9.06
## 4  11.23  12.41  13.14  11.31   8.75  10.54  11.55  11.00  14.42   9.28
## 5  11.56  12.82  13.58  11.56   8.95  10.84  11.76  11.19  14.58   9.47
## 6  11.96  13.26  13.98  11.82   9.16  11.13  12.01  11.41  14.78   9.71
##   新竹市 臺中市 嘉義市 臺南市 臺北市 高雄市
## 1     NA     NA     NA     NA     NA     NA
## 2   8.46   6.49   8.67   7.69   9.67   7.16
## 3   8.50   6.60   8.85   7.85   9.94   7.41
## 4   8.59   6.79   9.15   8.06  10.25   7.63
## 5   8.69   6.94   9.46   8.24  10.58   7.93
## 6   8.81   7.15   9.70   8.46  10.92   8.24

這筆資料的最左邊一欄有一個變數名稱,但是不是位在第一列,而是在第二列,我們如何正確地讀取每一列的資料?
- 首先創造一個有23個元素的向量 - 對某一個變數進行23次的迴圈 - 第一個元素應該來自於資料的第二列、第二行

old.2000<-rep(NA, 23)  #讀取2010年老年人口比率
 for (u in 1:23){
   old.2000[u]<-stat.dat[2,u+1]
 }
old.2000
##  [1]  6.37 10.20  7.46  9.69 10.98  7.16  9.42 10.60 11.61 12.41 10.75
## [12]  8.35 10.00 11.27 10.73 14.40  8.81  8.46  6.49  8.67  7.69  9.67
## [23]  7.16

4.2 while

如果要系統在執行到滿足某一個條件時中斷,可以用while迴圈。例如:

power<-0
while (power <= 12) {
  if (2^power<1000){
    cat(2^power, "\n")
    }else{
        cat("Stop")
    }
  power <- power +1
}
## 1 
## 2 
## 4 
## 8 
## 16 
## 32 
## 64 
## 128 
## 256 
## 512 
## StopStopStop

如果用for迴圈,可以輸出\(2^{0}\)\(2^{12}\),但是無法像while中斷迴圈

 for (a in -1:11){
    a <- a +1
   print(2^a)
 }
## [1] 1
## [1] 2
## [1] 4
## [1] 8
## [1] 16
## [1] 32
## [1] 64
## [1] 128
## [1] 256
## [1] 512
## [1] 1024
## [1] 2048
## [1] 4096

4.3 break

break可以在滿足某項條件情況下中斷迴圈,以上面的迴圈為例,假設我們要在超過1000時中斷:

power<-0
while (power <= 12) {
  if (2^power<1000){
    cat(2^power, "\n")
    }else{
        cat("Stop")
        break
    }
  power <- power +1
}
## 1 
## 2 
## 4 
## 8 
## 16 
## 32 
## 64 
## 128 
## 256 
## 512 
## Stop

也可以應用在訂旅館的例子中,例如超過原價就停止計算房價:

today<-as.Date(Sys.Date(), format='%Y-%m-%d')

hotel <- function(checkin){
n <- length(checkin)
price <- 3000
diff <- difftime(checkin, today)  
for (i in 1:n)
    if (diff[i]>90){
          print(checkin[i])
             cat (round(diff[i]/30,1), "months:", price*0.85, "\n")
      }else if (diff[i]>=60){
              print(checkin[i])
              cat (round(diff[i]/30,1), "months:",price*0.9,"\n")
    }else if (diff[i]>=30){
            print(checkin[i])
            cat (round(diff[i]/30,1), "months:",price,"\n")
            
    }else{
            print(checkin[i])
            cat("Over the budget")
            break
            #cat (diff[i],  "days:",price*1.2, "\n")
    }
 }
checkin<-as.Date(c("2018-12-31", "2018-04-20","2018-05-20",
                   "2018-06-01","2018-06-30"), format='%Y-%m-%d')
checkin<-c(checkin, today+7)
hotel(checkin)
## [1] "2018-12-31"
## Over the budget

5 作業

  1. 請把美國的州名排成一個陣列,然後找出州名長度多於或等於13的州(提示:nchar()傳回字串的長度)

  2. 請讀取studentsfull.txt這個檔案,然後取出經濟系與化學系的學生資料。

  3. 老師決定把某次考試之中,60分以下開根號乘以10,60分以上維持原來批改分數,請寫一個程式幫老師轉換以下成績:34, 81, 55, 69, 77, 40, 49, 26,分數計算至小數點後第二位四捨五入。

  4. 請寫一個函數可以把兩個日期之間的差距轉換成月,並且以今天日期與2020年的7月31日之間的差距為例。

  5. 請練習讀取「失業率」,並且把2000年的各縣市的失業率與老年人口比率組成一個資料框。