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

    Navigation

    • FindPage
    • HelpContents
    • FunReading

    Upload page content

    You can upload content for the page named below. If you change the page name, you can also upload content for another page. If the page name is empty, we derive the page name from the file name.

    File to load page content from
    Page name
    설명

    • 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.