-
Notifications
You must be signed in to change notification settings - Fork 6
/
R강의2.Rmd
374 lines (275 loc) · 12.3 KB
/
R강의2.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
---
title: "데이타 변환 및 집계"
author: "Keon-Woong Moon"
output: html_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE,comment=NA)
```
# 결측치(NA, Not Available)와 NULL
R에서 결측치는 NA이다.
이것은 NULL값과 다르다. NA값은 하나의 값이 결측치라는 얘기이고, NULL은 NULL 객체를 뜻하며 객체를 정의되지 않은 상태로 만들 때 쓴다.
```{r,error=TRUE}
a=c(NA)
a
b=c()
b
```
NA값이 있을 경우 평균 등의 함수는 계산할 수 없으므로 Error를 내게 되며, 결측치를 빼고 계산하려면 na.rm=TRUE 옵션을 주고 계산하여야 한다.
```{r,error=TRUE}
a=c(1,2,3,NA,8,NA,10) # a에 2개의 결측치(누락된 값)가 있다.
mean(a)
mean(a,na.rm=TRUE) # NA값을 제거(remove)하고 계산
```
is.na() 함수는 객체의 원소 하나가 NA인 경우 참이 되며, 결측치의 개수를 세려면 sum(is.na(a))등과 같이 사용할 수 있다.
```{r}
is.na(a)
sum(is.na(a)) # is.na() 함수의 값은 NA인 경우 1(즉 NA값의 개수를 센다).
a=c(a,NULL,8,NULL);a # NULL을 추가해도 에러는 나지 않지만 변화가 없다.
(a=c(a,NA)) # NA를 추가하면 결측치 데이터 하나가 추가된다.
```
### 누락된 값(NA)은 전염된다
R의 비교문에서 가장 혼동하기 쉬운 것은 누락치(NA, not available)이다. NA는 전염력이 있다. NA 를 연산하는 경우 모두 NA 가 된다.
```{r}
NA > 5
10 == NA
NA + 10
NA / 2
```
---
가장 혼동하기 쉬운 것은 다음 비교이다.
```{r}
NA == NA
```
이것을 이해하기 위해서는 다음 예를 보자.
```{r}
# 철수의 나이를 x라고 하고 나이를 모른다고 하자
x <- NA
# 영희의 나이를 y라고 하고 나이를 모른다고 하자
y <- NA
#철수와 영희가 나이가 같은가?
x == y
# 모른다!!
```
--
철수의 나이가 누락되어 있는가?
```{r}
is.na(x)
```
a=NULL은 a라는 객체 자체를 없애는 명령이다.
```{r}
a=NULL # a라는 객체 자체를 없애는 명령이다.
a
```
### NA와 NULL 검사
NA와 NULL은 ==를 사용하여 비교할 수 없다. is.na()와 is.null() 을 사용한다.
```{r}
a=NA
a==NA
is.na(a)
b<-NULL
b==NULL
is.null(b)
```
# R의 명령어: 함수와 제어문
우리가 R을 배우는 목적은 데이터를 분석하고 그래프를 그리는 일을 R에게 시키기 위해서이다. R의 기능은 무궁무진하지만 우리가 무슨 일을 하고 싶어하는지는 알아내지 못한다. 또한 R이 한국 사람이 아니므로 우리나라 말로 일을 시키면 알아듣지 못한다. 따라서 우리가 R에게 일을 시키기 위해서는 R이 알아들을 수 있는 명령어로 명령을 내려야 한다. 이번 장에서는 R에게 일을 시키기 위한 기본 명령어(함수)들과 제어문(조건에 따라 다른 일 시키기)을 배워보기로 한다.
## 함수
R에서 쓰는 명령어의 대부분은 함수로 되어있다. 앞장에서 본 변수 Height의 예를 들어보면 다음과 같다.
```{r}
Height=c(168,173,160,145,180) # Height변수에 5명의 키 저장
result=mean(Height) # 평균
result
```
여기서 Height변수를 초기화할 때 사용한 c, 평균을 구할 때 쓴 mean, 결과를 표시할 때 쓴 print 등이 모두 함수이다. 평균을 구하는 mean을 예로 들면 함수로 내리는 명령은 평균을 구하라는 명령인데 함수의 이름은 mean이고 하는 일은 어떤 값을 가진 대상(객체)을 받아들여 평균값을 반환한다. 여기서는 함수에 Height라고 하는 numeric vector를 전달해 주었는데 함수에 전달해 주는 값을 인수라고 한다. 또한 함수는 어떤 심부름을 하면 꼭 무언가를 갖고 오는데 이를 반환값이라고 한다. mean 함수의 경우 평균값을 갖고 온다. 단 여기에는 제한이 있는데 하나의 함수는 한 번에 하나의 반환값만 가져올 수 있다. 이러한 제한을 극복하기 위해 반환할 값이 같은 종류의 데이터라면 벡터로 만들어 가져오면 되고, 만일 가져올 것이 많다면 큰 하나의 보따리에 여러 개를 넣어서 리스트로 만들어 하나의 리스트만 가져오면 된다.
### 새로운 함수 만들기
R에는 쓸모 있는 함수가 아주 많지만 필요할 경우 새로운 함수를 만들어야 할 때가 있다. 이때는 함수의 이름을 정한 후 어떤 인수를 받아들일지, 어떤 결과를 반환할지 생각한 후 새롭게 함수를 정의하면 된다. 예를 들어 어떤 값의 세제곱을 반환하는 함수 mypower를 만든다면 다음과 같이 정의한다.
```{r}
mypower=function(x){ # 새로운 함수 정의
result=c(x^3)
return(result) # 결과 반환
}
mypower(3)
```
위의 예를 보면 mypower라는 함수를 새로 정의하고 있는데, 이는 어떤 숫자를 받아들여 세제곱값을 result 에 넣어 결과를 반환해준다.
### 연산자도 함수이다
사실 우리가 쓰는 연산자도 함수이다. 다음의 예를 보자.
```{r}
1+2
```
1+2와 같이 수학식에 쓰는 더하기, 빼기, 곱하기, 나누기 등도 사실은 함수이다. 연산자는 두 개의 인수를 받아들여서 결과를 반환하는 함수이다. 다음의 예를 보면 알수 있다.
```{r}
'+'(1,2) # 1+2와 같다.
'*'(3,4) # 3*4와 같다.
'/'(9,2) # 9/2와 같다.
```
## 제어문
R에서 사용하는 명령어에는 함수 외에도 제어문이라는 것이 있다. 신호등을 예로 들어보자. 신호등이 녹색불이면 직진하고 좌회전 불이 들어오면 좌회전하고 빨간 불이 들어오면 정지한다. 즉 신호등은 교통의 흐름을 제어한다. R에서도 이렇게 흐름을 제어하는 명령들이 있는데 이를 제어문이라고 한다.
### if... else...
가장 많이 사용하는 제어문 중의 하나는 if... else ...이다. 다음과 같이 쓴다.
```{r,eval=FALSE}
if(조건문) {
... # 조건문이 참일때 실행할 명령들
...
} else {
... # 조건문이 거짓일때 실행할 명령들
...
}
```
예를 하나 들어보자. 앞서 만든 mypower 함수는 숫자를 받아들여 세제곱값을 반환하는데, 만일 숫자가 아닌 문자를 인수로 주면 어떻게 될까? 다음의 예를 보자.
```{r,error=TRUE}
mypower("moon")
```
위의 함수는 에러 메시지를 내게 된다. 이 함수를 개선시키기 위해 먼저 인수가 숫자인지 아닌지 판단해서 숫자이면 원래 하는 일을 하고 숫자가 아니면 NA값을 반환하게 하려면 다음과 같이 한다. 숫자인지 아닌지 판단하는 명령(함수)은 is.numeric()인데 숫자인 경우 1이 되고 숫자가 아니면 0이 된다.
```{r}
# 개선된 mypower함수
mypower=function(x){ # 새로운 함수 정의
if(is.numeric(x)) { # 숫자인지 판단
result=c(x^3) # 숫자인 경우 세제곱 값 구함
}
else{
result=NA # 숫자가 아닌 경우 실행
}
result # 결과 반환
}
mypower(3)
mypower("moon")
```
이와 같이 개선된 mypower 함수는 인수가 숫자가 아닌 다른 값이 오더라도 에러가 나지 않는다.
### ifelse() 함수
if...else... 제어문 대신 쓸 수 있는 ifelse()함수가 있다. 이 함수는 if...else...제어문과 같은 기능을 하면서 실행속도가 훨씬 빠르기 때문에 가능하면 ifelse()함수를 쓰는 것을 권장한다. 다음과 같이 쓴다.
```{r,eval=FALSE}
result=ifelse(조건문,참일 경우 값,거짓일 경우 값)
```
위의 mypower() 함수를 ifelse() 함수로 바꾸면 다음과 같다.
```{r}
# ifelse()함수로 개선된 mypower함수
mypower2=function(x){
ifelse(is.numeric(x),c(x^3),NA)
}
mypower2(3)
mypower2("moon")
```
하지만 if...else... 구문과 달리 ifelse 함수는 하나의 값만 반환한다. 예를 들어 mypower() 함수와 mypower2()함수에 숫자를 하나 넣을 때와 숫자의 벡터를 넣을때 값이 달라진다.
```{r}
x=3
mypower(x)
mypower2(x)
x=c(2,3,4)
mypower(x)
mypower2(x)
```
역시 ifelse함수는 하나의 값만 반환된다. 여러 개의 값을 반환하기를 원할 경우 if...else... 구문을 써야 하고 하나의 값만 필요한 경우 ifelse함수를 쓰는 것이 좋다.
### for 반복문
반복문 중 대표적인 것은 for를 이용한 반복문이다. 다음과 같이 쓴다.
```{r,eval=FALSE}
for ( 루프변수 in 리스트) {
... # 반복할 구문
...
}
```
위의 for 구문은 루프변수가 리스트에 있는 첫 번째 값부터 마지막 값까지 변하면서 리스트의 길이만큼 반복된다. 예를 들어 계승(팩토리얼)을 구할 때는 다음과 같이 한다.
```{r}
myfact=function(x) {
result=1
for(i in 1:x) {
result=result*i
}
result
}
myfact(3)
myfact(5)
```
for 루프의 리스트에는 벡터, 함수 등을 지정할 수도 있다. 다음은 for 루프를 이용하여 plot() 함수에 인수를 전달하여 그래프를 그리고 있다.
```{r,fig.show='hold'}
par(mfrow=c(2,2))
x=list(1:6, sin, function(x) {x^2+2*x}, dnorm)
for(i in x) plot(i,xlim=c(0,2*pi))
par(mfrow=c(1,1))
```
### while 문
같은 반복이라도 어떤 조건이 성립할 동안 계속 어떤 계산을 반복할 때는 while() 문을 사용하는 것이 좋다. 단 조건식이 계속 TRUE인 경우 프로그램이 무한 반복되므로 주의하여야 한다. 또한 조건식이 처음부터 거짓이라면 한 번도 실행되지 않는다.
```{r,eval=FALSE}
while (조건식) { # 조건이 만족할 동안
... # 반복할 구문
... # 조건식이 처음부터 거짓이라면 한 번도 실행되지 않는다.
}
```
다음의 예를 보자.
```{r}
i=1
while(i<5) {
i=i+1
}
i
```
처음 i는 1이었으며 while 문을 안의 i=i+1이 반복되어 결국 i의 값이 5가 되었다.
### break
for나 while 등 반복문을 실행하는 도중에 break를 만나면 강제로 반복문에서 탈출한다. 예를 들어 조금 전의 예제를 break 문을 써서 다음과 같이 할 수 있다.
```{r}
i=1
while(1) { # 항상 실행
if(i==5) break # i가 5면 반복문에서 탈출
i=i+1
}
i
```
### next
for나 while 등 반복문을 실행하는 도중에 next를 만나면 강제로 다음 반복으로 넘어간다. 예를 들어 1부터 100까지 홀수의 합을 구하고 싶으면 다음과 같이 하면 된다.
```{r}
oddSum=0 # 홀수의 합을 저장할 변수, 0으로 초기화
for(i in 1:100) { # 1부터 100까지 반복
if(i%%2==0) next # 짝수(2로 나눈 나머지가 0)이면 다음 반복으로 넘어간다.
oddSum=oddSum+i
}
oddSum
```
### repeat
repeat 문을 사용하면 식이 무제한으로 반복된다. while(1)과 같다.
```{r}
i=1
repeat { # 항상 실행
if(i==5) break # i가 5면 반복문에서 탈출
i=i+1
}
i
```
# 데이타 변환 및 집계
(문제)
학생 10명이 있다.영어 시험 결과를 eng, 수학 시험 결과를 math라고 하자.
영어시험은 2번과 9번 학생이 결시하였고 9번 학생이 수학시험을 결시하였다. R에서 결측치는 NA이다.
```{r}
번호=1:10
영어=c(70,NA,80,85,90,90,60,55,NA,90)
수학=c(80,73,84,90,95,85,60,50,NA,100)
data=data.frame(번호,영어,수학)
data
```
영어와 수학시험의 결시자가 몇 명인지 계산하고 결시자를 뺀 평균을 계산하여 다음과 같이 보고하라.
```{r,echo=FALSE,message=FALSE}
require(tidyverse)
data %>%
gather(key="과목",value="성적",영어,수학) %>%
group_by(과목) %>%
summarize(
총원=n(),
결시자=sum(is.na(성적)),
총점=sum(성적,na.rm=TRUE),
평균=mean(성적,na.rm=TRUE)
)
```
(답)
```{r,eval=FALSE}
require(tidyverse)
data %>%
gather(key="과목",value="성적",영어,수학) %>%
group_by(과목) %>%
summarize(
총원=n(),
결시자=sum(is.na(성적)),
총점=sum(성적,na.rm=TRUE),
평균=mean(성적,na.rm=TRUE)
)
```
# 오늘의 숙제
위의 성적 data를 사용하여 누락된 값을 각 과목의 평균치로 채워 넣으세요.
(힌트) is.na()와 apply()함수를 사용하세요.