Subsetting Data
외부에서 불러 온 Data는 내가 원하지 않는 data도 다량 포함하고 있습니다.
- 분석에 필요한 data만 추려내는 과정이 반드시 필요합니다.
- MS excel 같은 GUI(그래픽) 방식의 소프트웨어라면 드래그와 클릭으로 선택합니다.
- 편하지만 데이타가 많을 때는 시간이 많이 걸리고 눈이랑 손가락이 아파집니다.
집중하지 않으면 실수합니다 (유권자 수와 투표용지가 늘었다 줄었다 할 수 있습니다).
- R은 여러 조건을 지정해서 추려내는데, 직관적이지 않지만 많은 데이타를 다룰 때 특히 유용합니다.
- 조건 입력할 때만 정신 차리면 됩니다.
- 만들어 둔 조건은 약간의 수정으로 재활용 할 수 있습니다.
행/열로 지정하기
Data 준비
서울시 일별 평균 대기오염도 정보페이지에서 [내려받기(CSV)]를 클릭하여 자료를 내려받습니다.
> str(df)
'data.frame': 18094 obs. of 8 variables:
$ 측정일시 : int 20200922 20200922 20200922 20200922 20200922 20200922 20200922 20200922 20200922 20200922 ...
$ 측정소명 : chr "강남구" "강남대로" "강동구" "강변북로" ...
$ 이산화질소농도.ppm.: num 0.016 0.03 0.015 0.011 0.011 0.017 NA 0.02 0.01 0.009 ...
$ 오존농도.ppm. : num 0.022 0.011 0.02 0.027 0.017 0.023 NA 0.021 0.034 0.018 ...
$ 일산화탄소농도.ppm.: num 0.3 0.6 0.3 0.4 0.3 0.2 NA 0.4 0.4 0.4 ...
$ 아황산가스.ppm. : num 0.003 0.003 0.002 0.003 0.002 0.003 NA 0.002 0.003 0.002 ...
$ 미세먼지..... : int 13 14 14 9 17 13 NA 15 NA 12 ...
$ 초미세먼지..... : int 9 10 8 6 9 8 NA 7 3 4 ...
> head(df)
측정일시 측정소명 이산화질소농도.ppm. 오존농도.ppm. 일산화탄소농도.ppm. 아황산가스.ppm. 미세먼지..... 초미세먼지.....
1 20200922 강남구 0.016 0.022 0.3 0.003 13 9
2 20200922 강남대로 0.030 0.011 0.6 0.003 14 10
3 20200922 강동구 0.015 0.020 0.3 0.002 14 8
4 20200922 강변북로 0.011 0.027 0.4 0.003 9 6
5 20200922 강북구 0.011 0.017 0.3 0.002 17 9
6 20200922 강서구 0.017 0.023 0.2 0.003 13 8
data.frame[행,열]
data.frame[행,열] 순서로 위치(index)를 지정하여 원하는 데이터를 지정할 수 있습니다.
- 함수에 값을 넣을 때는 ( ) 괄호를 쓰지만, 행/열 index를 표시할 때는 [ ] 괄호를 씁니다.
- 이중모음 쓸 때 가로획을 먼저 쓰듯이 행 index를 먼저 씁니다.
- c( , , , ) 혹은 c( : ) 표기로 범위를 지정할 수 있습니다.
빈칸으로 냅두면 그 행(혹은 열) 전체가 지정됩니다.
다음은 어떤 결과가 나오겠는지 먼저 예상하고, 실행 해 보세요.
TRUE/FALSE 조건으로 나누기
조건 연산자 사용
다음은 df에 저장된 data.frame에서 이름이 '측정소명'인 vector를 화면에 출력합니다.
1 df$측정소명
> df$측정소명
[1] "강남구" "강남대로" "강동구" "강변북로" "강북구"
[6] "강서구" "공항대로" "관악구" "관악산" "광진구"
[11] "구로구" "궁동" "금천구" "남산" "노원구"
[16] "도봉구" "도산대로" "동대문구" "동작구" "동작대로"
[21] "마포구" "마포아트센터" "북한산" "서대문구" "서울숲"
[26] "서초구" "성동구" "성북구" "세곡" "송파구"
[31] "시흥대로" "신촌로" "양천구" "영등포구" "영등포로"
[36] "올림픽공원" "용산구" "은평구" "자연사박물관" "정릉로"
[41] "종로" "종로구" "중구" "중랑구" "천호대로"
[46] "청계천로" "한강대로" "행주" "홍릉로" "화랑로"
[51] "강남구" "강남대로" "강동구" "강변북로" "강북구"
[56] "강서구" "공항대로" "관악구" "관악산" "광진구"
[61] "구로구" "궁동" "금천구" "남산" "노원구"
[66] "도봉구" "도산대로" "동대문구" "동작구" "동작대로"
.......(중략).......
[951] "강남구" "강남대로" "강동구" "강변북로" "강북구"
[956] "강서구" "공항대로" "관악구" "관악산" "광진구"
[961] "구로구" "궁동" "금천구" "남산" "노원구"
[966] "도봉구" "도산대로" "동대문구" "동작구" "동작대로"
[971] "마포구" "마포아트센터" "북한산" "서대문구" "서울숲"
[976] "서초구" "성동구" "성북구" "세곡" "송파구"
[981] "시흥대로" "신촌로" "양천구" "영등포구" "영등포로"
[986] "올림픽공원" "용산구" "은평구" "자연사박물관" "정릉로"
[991] "종로" "종로구" "중구" "중랑구" "천호대로"
[996] "청계천로" "한강대로" "행주" "홍릉로" "화랑로"
[ reached getOption("max.print") -- omitted 17094 entries ]
비교 연산자
R에서 vector에 계산을 지시하면 element-wise 계산을 합니다.
- 예를 들어, numeric vector에 +1을 지시하면 그 vector의 모든 element에 +1을 합니다.
비교 연산자는 계산 결과가 TRUE/FALSE 로 나옵니다.
> (혹은 <) 연산자는 좌변(혹은 우변)이 크면 TRUE, 아니면 FALSE라는 결과를 냅니다.
R에서 등호는 ==입니다. 입력 기호 =와 차별하기 위해서입니다. 대부분의 프로그래밍 언어에서 그렇습니다.
다음은 df$측정소명 에 저장된 element가 관악구와 같은지 하나하나 비교하여 TRUE 혹은 FALSE를 출력합니다.
1 df$측정소명 == '관악구'
[1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
[15] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[29] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[43] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[57] FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
.......(중략).......
[953] FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[967] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[981] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[995] FALSE FALSE FALSE FALSE FALSE FALSE
[ reached getOption("max.print") -- omitted 17094 entries ]
- 8번째, 58번째, .... (중략) ..... 958번째에 TRUE 가 보입니다.
- 18,094 entries 를 다 확인하지 말고 R을 믿어줍시다.
이 T/F 벡터를 행index로 지정하면 TRUE 값에 해당하는 행만 subset 됩니다.
T/F vector로 지정하는 법과 index를 지정하는 법을 혼합해도 됩니다.
%in% 연산자
A가 B에 포함되어 있는지가 궁금할 때는 A %in% B 이렇게 씁니다.
A %in% B: A가 B에 포함되어 있으면 TRUE 아니면 FALSE가 됩니다.
1:1로 동일한지 비교하는 A == B 식 여러 개를 한 줄의 코드로 해결하는 것이 장점입니다.
1 df$측정소명 %in% c('관악구','강동구')
[1] FALSE FALSE TRUE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
[15] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[29] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[43] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE
[57] FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
.......(중략).......
[953] TRUE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[967] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[981] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[995] FALSE FALSE FALSE FALSE FALSE FALSE
[ reached getOption("max.print") -- omitted 17094 entries ]
8번째, 58번째, .... (중략) ..... 958번째에 TRUE 는 원래 보이고 (df$측정소명이 관악구)
3번째, 53번째, .... (중략) ..... 953번째에 추가로 TRUE가 보입니다 (df$측정소명이 강동구)
다음은 어떤 결과가 나올까요?
grep( ) 사용
grep(찾을 패턴, 데이타) 이렇게 쓰면 데이타에 패턴이 보이면 TRUE, 아니면 FALSE 가 됩니다.
찾을 패턴에 정규식 (regular expression) 사용 가능합니다.
정규식은 패턴을 표시하는 문법입니다. 매우 강력합니다.
서울시 일별 평균 대기오염도 정보에서 2019년 11월의 데이타만 subset하고 싶을 때 %in% 를 사용한다면 이렇게 합니다.
1 df[df$측정일시 %in% c(20191101, 20191102, 20191103, ...(모두 다 적어)... 20191130), ]
grep( )을 사용하면 이렇게 합니다.
1 df[ grep('201911', df$측정일시) , ] # 측정일시에 201911이 보이면 TRUE
정규식을 사용하면 더 확실하게 하면 이렇게 합니다.
- 패턴 앞에 ^를 넣으면 "처음에 보일 때"라는 뜻입니다.
- 200301 이라는 패턴은 2020년 3월 1일에도 나오고 2003년 1월에도 나오기 때문에 구분 해 주는 것이 좋습니다.
- 반대로 패턴 뒤에 $를 넣으면 "마지막에 보일 때"라는 뜻입니다.
정규식의 문법은 너무 많으니 각자 찾아보세요~
1 df[ grep('^201911', df$측정일시) , ] # 측정일시 처음이 201911이면 TRUE
2019년 11월의 미세먼지 컬럼만 따오고 싶으면 이렇게 합니다.
데이타 전처리 마무리
Corona19 로 우리가 얻은 몇가지 안 되는 장점 중 하나는 깨끗한 공기입니다. 진짜 그런지 봐 봅시다.
- 2019년 11월과 2020년 5월의 서울 각 자치구에서 관측한 pm10 미세먼지 수치를 비교합니다.
1 df <- read.csv(file.choose(), fileEncoding = 'euc-kr')
2
3 # 측정소명을 보면 OO구와 OO대로 이렇게 섞여 있습니다.
4 # 예를 들어 종로구와 종로가 따로 입력되어 있으면 종로구 공기질이 두번 들어가서 over-representing 될 수 있으므로
5 # 측정소가 OO구인 데이타만 취하고 나머지는 버리겠습니다.
6 # df 중 측정소명이 '구'로 끝나는 패턴만 따로 모아서 df 를 update 합니다.
7 df <- df[ grep('구$', df$측정소명), ]
8
9 dust19 <- data.frame(month = as.factor('2019.11'),
10 pm10 = df[ grep('^201911', df$측정일시) , '미세먼지.....' ]
11 )
12 head(dust19)
month pm10
1 2019.11 51
2 2019.11 52
3 2019.11 45
4 2019.11 67
5 2019.11 46
6 2019.11 35
- 첫번째 컬럼을 month라고 하고 2019.11를 반복해서 입력했습니다.
- '2019.11'을 그냥 character로 놔둬도 되지만, 여기서는 분류의 의미로 쓰이므로 as.factor( )로 인식시켰습니다.
- 두번째 컬럼을 pm10 이라고 하고 관측된 미세먼지 수치를 입력했습니다.
동일한 방법으로 dust20 data.frame을 만들고 dust19와 dust20을 위아래로 붙혀줍니다
컬럼을 (month, pm10) 대신 (pm10@2019, pm10@2020)으로 구성하는 것은 직관적이지만 더 귀찮은 오류의 가능성을 키웁니다.
- 독립변수를 완전한 하나의 컬럼(month), 종속변수를 완전한 하나의 컬럼(pm10)으로 하는 게 더 data-oriented 방법입니다.
- 그러면 종속변수가 하나 더 생겨도 문제 없습니다.
- pm2.5 데이타를 추가하는 상상을 해 보면, 그냥 pm2.5라는 이름의 컬럼 하나 더 추가하면 됩니다.
- 혹은 pm10@2019, pm10@2020, pm2.5@2019, pm2.5@2020 이렇게 추가하시게요...?
- 그룹간 sample 수에 차이가 나도 문제 없습니다.
- 현재 dust19는 750 row, dust20은 775 row 입니다.
- row 갯수가 차이나면 같은 data.frame으로 못 묶습니다 (한쪽만 빈 row인 데이타프레임....?)
- 실제 관측하다보면 같은 sample size로 맞추기는 매우 어렵습니다.
- 만약 paired data라면 before vs. after 구도로 data.frame을 작성해도 괜찮습니다.
before & after를 비교하는 paired data라면 row number가 필연적으로 같습니다.
- 그리고 paired t-test는 before - after(차이)가 0인가? 를 검정하는 1 sample t-test의 변형입니다.
그러니 before & after data가 side by side로 매치되어 있어야 합니다.
- 수술전 환자가 수술후 안 나타나면 (사망? 도망? 단순 불참?), 그 환자의 before 데이타도 빼야 합니다.