#acl +All:read #format wiki #language ko #pragma description 기초의학통계학 및 실험; = Subsetting Data = 외부에서 불러 온 Data는 내가 원하지 않는 data도 다량 포함하고 있습니다. * 분석에 필요한 data만 추려내는 과정이 반드시 필요합니다. * MS excel 같은 GUI(그래픽) 방식의 소프트웨어라면 드래그와 클릭으로 선택합니다. * 편하지만 데이타가 많을 때는 시간이 많이 걸리고 눈이랑 손가락이 아파집니다. * 집중하지 않으면 실수합니다 ,,(유권자 수와 투표용지가 늘었다 줄었다 할 수 있습니다),,. * R은 여러 조건을 지정해서 추려내는데, 직관적이지 않지만 많은 데이타를 다룰 때 특히 유용합니다. * 조건 입력할 때만 정신 차리면 됩니다. * 만들어 둔 조건은 약간의 수정으로 재활용 할 수 있습니다. == 행/열로 지정하기 == === Data 준비 === [[http://data.seoul.go.kr/dataList/OA-2218/S/1/datasetView.do|서울시 일별 평균 대기오염도 정보]]페이지에서 [내려받기(CSV)]를 클릭하여 자료를 내려받습니다. {{{#!highlight r df <- read.csv(file.choose(), fileEncoding = 'euc-kr') # file.choose()의 결과물을 read.csv()에 넣습니다. # fileEncoding 옵션은 macOS에서 필요합니다. Windows에서는 있어도 없어도 됩니다. str(df) head(df) }}} {{{#!highlight rout numbers=disable > 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( : ) 표기로 범위를 지정할 수 있습니다. * 빈칸으로 냅두면 그 행,,(혹은 열),, 전체가 지정됩니다. 다음은 어떤 결과가 나오겠는지 먼저 예상하고, 실행 해 보세요. {{{#!highlight r df[3,2] df[c(3:8),2] df[c(3:8),c(1,2,4)] df[ ,c(1,2,4)] }}} == TRUE/FALSE 조건으로 나누기 == === 조건 연산자 사용 === 다음은 df에 저장된 data.frame에서 이름이 '측정소명'인 vector를 화면에 출력합니다. {{{#!highlight r df$측정소명 }}} {{{#!highlight rout numbers=disable > 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를 출력합니다. {{{#!highlight r df$측정소명 == '관악구' }}} {{{#!highlight rout numbers=disable [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 됩니다. {{{#!highlight r GA <- df[df$측정소명 == '관악구', ] head(GA) }}} T/F vector로 지정하는 법과 index를 지정하는 법을 혼합해도 됩니다. {{{#!highlight r # 측정소가 관악구인 row의 1,2,7번째 column 지정 GA_dust <- df[df$측정소명 == '관악구', c(1,2,7)] head(GA_dust, 10) }}} ==== %in% 연산자 ==== A가 B에 포함되어 있는지가 궁금할 때는 ```A %in% B``` 이렇게 씁니다. * ```A %in% B```: A가 B에 포함되어 있으면 TRUE 아니면 FALSE가 됩니다. * 1:1로 동일한지 비교하는 ```A == B``` 식 여러 개를 한 줄의 코드로 해결하는 것이 장점입니다. {{{#!highlight r df$측정소명 %in% c('관악구','강동구') }}} {{{#!highlight rout numbers=disable [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$측정소명```이 강동구) 다음은 어떤 결과가 나올까요? {{{#!highlight r # 측정소가 관악구인 row의 1,2,7번째 column 지정 GAGD_dust <- df[df$측정소명 %in% c('관악구','강동구'), c(1,2,7)] head(GAGD_dust, 10) }}} === grep( ) 사용 === grep(찾을 패턴, 데이타) 이렇게 쓰면 데이타에 패턴이 보이면 TRUE, 아니면 FALSE 가 됩니다. * 찾을 패턴에 [[https://regexr.com/|정규식 (regular expression)]] 사용 가능합니다. * [[https://regexr.com/|정규식]]은 패턴을 표시하는 문법입니다. 매우 강력합니다. [[http://data.seoul.go.kr/dataList/OA-2218/S/1/datasetView.do|서울시 일별 평균 대기오염도 정보]]에서 2019년 11월의 데이타만 subset하고 싶을 때 %in% 를 사용한다면 이렇게 합니다. {{{#!highlight r df[df$측정일시 %in% c(20191101, 20191102, 20191103, ...(모두 다 적어)... 20191130), ] }}} grep( )을 사용하면 이렇게 합니다. {{{#!highlight r df[ grep('201911', df$측정일시) , ] # 측정일시에 201911이 보이면 TRUE }}} [[https://regexr.com/|정규식]]을 사용하면 더 확실하게 하면 이렇게 합니다. * 패턴 앞에 ^를 넣으면 "처음에 보일 때"라는 뜻입니다. * 200301 이라는 패턴은 2020년 3월 1일에도 나오고 2003년 1월에도 나오기 때문에 구분 해 주는 것이 좋습니다. * 반대로 패턴 뒤에 $를 넣으면 "마지막에 보일 때"라는 뜻입니다. * [[https://regexr.com/|정규식]]의 문법은 너무 많으니 각자 찾아보세요~ {{{#!highlight r df[ grep('^201911', df$측정일시) , ] # 측정일시 처음이 201911이면 TRUE }}} 2019년 11월의 미세먼지 컬럼만 따오고 싶으면 이렇게 합니다. {{{#!highlight r df[ grep('^201911', df$측정일시) , '미세먼지.....' ] # 주의: 특수문자 때문에 미세먼지 컬럼 이름이 '미세먼지.....' 이렇습니다. }}} == 데이타 전처리 마무리 == Corona19 로 우리가 얻은 ,,몇가지 안 되는,, 장점 중 하나는 깨끗한 공기입니다. 진짜 그런지 봐 봅시다. * 2019년 11월과 2020년 5월의 서울 각 자치구에서 관측한 pm10 미세먼지 수치를 비교합니다. {{{#!highlight r df <- read.csv(file.choose(), fileEncoding = 'euc-kr') # 측정소명을 보면 OO구와 OO대로 이렇게 섞여 있습니다. # 예를 들어 종로구와 종로가 따로 입력되어 있으면 종로구 공기질이 두번 들어가서 over-representing 될 수 있으므로 # 측정소가 OO구인 데이타만 취하고 나머지는 버리겠습니다. # df 중 측정소명이 '구'로 끝나는 패턴만 따로 모아서 df 를 update 합니다. df <- df[ grep('구$', df$측정소명), ] dust19 <- data.frame(month = as.factor('2019.11'), pm10 = df[ grep('^201911', df$측정일시) , '미세먼지.....' ] ) head(dust19) }}} {{{#!highlight rout numbers=disable 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을 위아래로 붙혀줍니다 {{{#!highlight r dust20 <- data.frame(month = as.factor('2020.05'), pm10 = df[ grep('^202005', df$측정일시) , '미세먼지.....' ] ) dust = rbind(dust19, dust20) # rbind( ): row 방향으로 붙힙니다. summary(dust) }}} 컬럼을 (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 데이타도 빼야 합니다. {{{#!highlight r boxplot(pm10 ~ month, data=dust, col=c('brown2','deepskyblue')) # 참고로 r에서 사용 가능한 colors list를 보고 싶으면 다음을 실행하세요. colors() }}} {{attachment:pm10_boxplot.png}} [[../T-Test|이 차이가 통계적으로 유의미한 차이인지 궁금하다면??]] ----- <>