Home ChungLab Wiki
    • 설명
    • 못 고치는 문서
    • Menu
      • Navigation
      • RecentChanges
      • FindPage
      • 사이트맵
      • Help
      • HelpContents
      • HelpOnMoinWikiSyntax
      • 보기
      • 첨부
      • 정보
      • 원문 보기
      • 인쇄용 화면
      • 수정
      • 로드
      • 저장
    • 로그인

    Navigation

    • FindPage
    • HelpContents
    • FunReading
    이 위키는 메일 처리를 할 수 없도록 되어 있습니다. 위키 관리자에게 연락하여 메일을 쓸 수 있도록 요청하세요.
    • Class
    • Statistics
    • RBasic3

    Subsetting Data

    외부에서 불러 온 Data는 내가 원하지 않는 data도 다량 포함하고 있습니다.

    • 분석에 필요한 data만 추려내는 과정이 반드시 필요합니다.
    • MS excel 같은 GUI(그래픽) 방식의 소프트웨어라면 드래그와 클릭으로 선택합니다.
      • 편하지만 데이타가 많을 때는 시간이 많이 걸리고 눈이랑 손가락이 아파집니다.
      • 집중하지 않으면 실수합니다 (유권자 수와 투표용지가 늘었다 줄었다 할 수 있습니다).

    • R은 여러 조건을 지정해서 추려내는데, 직관적이지 않지만 많은 데이타를 다룰 때 특히 유용합니다.
      • 조건 입력할 때만 정신 차리면 됩니다.
      • 만들어 둔 조건은 약간의 수정으로 재활용 할 수 있습니다.

    행/열로 지정하기

    Data 준비

    서울시 일별 평균 대기오염도 정보페이지에서 [내려받기(CSV)]를 클릭하여 자료를 내려받습니다.

       1 df <- read.csv(file.choose(), fileEncoding = 'euc-kr')
       2 # file.choose()의 결과물을 read.csv()에 넣습니다.
       3 # fileEncoding 옵션은 macOS에서 필요합니다. Windows에서는 있어도 없어도 됩니다.
       4 
       5 str(df)
       6 head(df)
    

    > 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( : ) 표기로 범위를 지정할 수 있습니다.
    • 빈칸으로 냅두면 그 행(혹은 열) 전체가 지정됩니다.

    다음은 어떤 결과가 나오겠는지 먼저 예상하고, 실행 해 보세요.

       1 df[3,2]
       2 df[c(3:8),2]
       3 df[c(3:8),c(1,2,4)]
       4 df[      ,c(1,2,4)]
    

    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 됩니다.

       1 GA <- df[df$측정소명 == '관악구',  ]
       2 head(GA)
    

    T/F vector로 지정하는 법과 index를 지정하는 법을 혼합해도 됩니다.

       1 # 측정소가 관악구인 row의 1,2,7번째 column 지정
       2 GA_dust <- df[df$측정소명 == '관악구', c(1,2,7)]
       3 head(GA_dust, 10)
    

    %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$측정소명이 강동구)

    다음은 어떤 결과가 나올까요?

       1 # 측정소가 관악구인 row의 1,2,7번째 column 지정
       2 GAGD_dust <- df[df$측정소명 %in% c('관악구','강동구'), c(1,2,7)]
       3 head(GAGD_dust, 10)
    

    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월의 미세먼지 컬럼만 따오고 싶으면 이렇게 합니다.

       1 df[ grep('^201911', df$측정일시) , '미세먼지.....'  ]
       2 # 주의: 특수문자 때문에 미세먼지 컬럼 이름이 '미세먼지.....' 이렇습니다.
    

    데이타 전처리 마무리

    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을 위아래로 붙혀줍니다

       1 dust20 <- data.frame(month = as.factor('2020.05'),
       2                      pm10 = df[ grep('^202005', df$측정일시) , '미세먼지.....'  ]
       3                      )
       4 
       5 dust = rbind(dust19, dust20) # rbind( ): row 방향으로 붙힙니다.
       6 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 데이타도 빼야 합니다.

       1 boxplot(pm10 ~ month, data=dust, col=c('brown2','deepskyblue'))
       2 
       3 # 참고로 r에서 사용 가능한 colors list를 보고 싶으면 다음을 실행하세요.
       4 colors()
    

    pm10_boxplot.png

    이 차이가 통계적으로 유의미한 차이인지 궁금하다면??


    /CentralLimitTheorem   /ChiSquared   /Clustering   /InstallR   /InstallRStudio   /Lies   /NormalDistribution   /RBasic   /RBasic1   /RBasic2   /RBasic3   /RCloud   /RIntro   /RKorean   /T-Test  
    Copyright © ChungLab. Built on MoinMoin and Bootstrap. All Rights Reserved.