분류 전체보기

3-2. 결측값(결측치)

2019. 2. 14. 22:10

결측값(결측치) in R

  • NA(Not Available), 결측치라고 하며 값이 표기되지 않은 값.
  • R에서 [행,열] 중 행 인덱싱 자리에 is.na(dataframe) + sum(결측치 개수), mean(결측률) 을 얻을 수 있다. * pandas : df.isnull().sum() , mean()

결측값(결측치)의 종류

EDA를 하면서 결측치가 있는 칼럼을 + 시각화하여 Random(관깊칼럼X) / No random(관깊칼럼O-> 범주별 대치, 예측)을 판단해야한다.

  1. Random : 무작위로 정보가 없는 결측치, 다른변수별로 보면 결측비율도 달라진다. -> 삭제(전체or부분) or 조건부 대치 or 예측모형으로 해당변수 예측 모두가능
    ex> 몸무게 결측치, 남자<여자의 결측 가능성이 높다. -> 범주별 대치

  2. No random : 결측값 자체가 (민감한 정보라서) 패턴을 가짐 -> 관깊칼럼범주별 수치(평균/중앙값/최빈값)으로 대치
    ex> 고소득자와 저소득자의 연봉은 결측치가 될 확률이 높다 -> 관계 깊은 칼럼인 사는곳별 평균/중앙값/최빈값 or예측값으로 대치

결측치를 탐색하는 방법 2가지 (표+시각화)

  1. : mice패키지 -> 결측치 1(아닌 것을 0)으로 표기한 뒤 x 갯수로 나타내어 조합별로도 확인가능
  2. 시각화
    1) cor(y) : 결측치 1(아닌 것을 0)에 대해 이변수간의 상관관계로서 결측치를 나타낸다.
    2) Vim패키지 : 결측치(빨간색)의 빈도 및 패턴을 시각화한다. 결측된 양을 scalebar로 나타내준다.

결측치 처리방법 3가지

  1. 삭제
    1) List Wise Deletion : row가 어느 한 칼럼(변수)라도 missing이면 삭제 -> 간단하지만, sample size가 떨어져 power가 낮아진다.
    2) Pair Wise Deletion : 관심있는 칼럼(변수)에 값이 있다면, 포함시킨다.

  2. 대치(Imputation)
    1) 최빈값(mode, qualitative) : 범주형에서 결측값이 발생시, 범주별 빈도가 가장 높은 값으로 대치한다. -> but bias가 많이 발생할 수 있다. ex> 남자, 여자 중에 남자의 빈도가 높다고 남자도 대치한다면, bias가 높아진다.
    2) 중앙값(median) : 숫자형(연속형)에서 결측값제외한 중앙값으로 대치방법
    3) 평균(mean) : 숫자형(연속형)에서 결측값제외한 평균으로 대치방법
    4) Similar case imputation : 조건부 대치
    5) Generalized Imputation : 회귀분석을 이용한 대치

  3. 예측모델(Prediction model)
    1) 결측값이 없는 칼럼(변수)들로 구성된 dataset으로 -> 결측값이 있는 칼럼을 예측하게 한다.
    2) 예측값으로 해당칼럼을 채워넣는다.


  4. 가장 흔히 쓰는 방법
    1) 하나라도 결측이 있는 변수 제외한 dataset생성
    2) imputation한 dataset 2개 정도 생성 (mean or median / regression / KNN 등으로)
    3) 1)과 2)의 dataset 3개에 대해서 결과값이 서로 일관성 있음을 보여준다. -> 결측대치방법이 senvitivity하지 않다는 것을 제시하는 것

결측값(결측치) 처리 가이드라인

  • 10% 미만: 삭제 or 대치
  • 10 ~ 20% : Hot deck(매년자료->해당년자료 추정) or regression or model based imputation
  • 20 ~ 50% 이상 : regression or model based imputation
  • 50% 이상 : 해당 칼럼(변수)자체 제거

결측값 삭제 or 대치의 한계점

  1. 삭제를 통해 n수가 달라진다 -> 집단의 특성이 달라질 수 있다. -> missing rate(10%미만)을 확인하고 삭제한다

  2. 대치는 틀린값으로 대체될 수 있다는 가능성이 항상 존재한다

3-1. 이상치

2019. 2. 14. 21:34

이상치(Outlier)에 대해

  • 이상치 처리는 데이터 분석 중 가장 많은 시간이 소요된다.
  • EDA & data cleaning, data preprocessing, data manupulation, data massage, data munging, data wrangling

이상치

  1. 정의 : 데이터 전체적인 패턴에서 동떨어져있는 관측 값

    • 영향점이라고도 불림.
    • 독립변수(x축)에 있는 이상치(영향점) : 지대점
    • 종속변수(y축)에 있는 이상치(영향점) : 아웃라이어
    • boxplot에서 가운데 box의 높이 : 3Q - 1Q = IQR, 가운데 50%를 의미
    • boxplot에서 box양쪽 막대 : 3Q + 1.5*IQR = Upper fence, 1Q-1.5*IQR = Lower fence
    • boxplot에서 Upper, Lower fence 보다 바깥에 있는 값 : 아웃라이어
  2. 단변수(boxplot)에서 안보이던 이상치(Outlier)가 이변수(산점도)에서 보일 수 있다.

    • 반드시 그림으로 그려봐야한다.
    • EDA를 통해 새로운 이상치를 발견할 수 있다.
  1. 종류 :
    1) 실수에 의한 아웃라이어 : 수집시 오류 + 측정단위 오류 + 실험시 에러
    2) 자연(real연구대상)적 발생 아웃라이어 : 직원들월급 중 임원들은 따로 빼서 분석해야함. 자연적으로는 너무 차이날 수 밖에 없음.
    3) 역코딩에 의한 아웃라이어 : 설문지에 의도적으로 1번과 5번 성향을 바꿔놨는데, 데이터 분석시 고려안함
    4) 의도적 아웃라이어 : 설문조사시 10대들은 알콜섭취량 일부러 낮게 적음
    5) sampling 아웃라이어 : 표본 중에 우연히 이상치요소들이 포함 ex> 사람들 키 조사하는데 농구선수도 포함됨
  1. 이상치(아웃라이어)의 영향력

    • 평균뿐만 아니라 분산(표준편차)가 엄청 커져 -> 엄청 불안정한 자료가 된다.
    • 중앙값(median)은 그나마 영향을 덜 받는다.

이상치 발견의 방법

  1. 시각화 : 단변수 boxplot -> 이변수 scatter plot
  2. Z(표준정규)분포상 3sd이상 떨어진 값들 : 3sd는 왼쪽 2.5%이하, 오른쪽 97.5%이상
    • 1sd사이 : 68.27%
    • 2sd사이 : 95.45%
    • 3sd사이 : 99.73%
  3. 도메인 knowledge를 특정기준으로 두고 파악 : ex 수축기 혈압 120 ~ 160 벗어나는 값들

이상치 제거 방법

  1. 삭제 : 입력시 error거나 outlier수가 굉장히 적을 경우는 그냥 삭제
  2. 대치(Imputation) : 다른 값으로 변경
    • 범주별 mean/median/mode 등으로 대치
    • 통계적모델링(종속변수로 취급하여, 다른변수들로 예측 후 대치)
    • Binning(숫자의 분포를 범주로 매핑)
  3. 스케일링(scaling) : 보통 right skewed(캐글에서 0.75이상)인 경우 logsquare root(제곱근)을 취한다
  4. 정규화(normalization) : MinMax, Z-score 등으로 정규화시킨다.

sql고수 효* 선생님께 sql을 다룰 수 있는 간단한 설명과 자료를 건네주셨다.
의료데이터이므로 민감한 자료라 보관을 잘하고 연습후 폐기를 잘해야할 것이다.
나는 이것을 sql로 EDA를 마치고 python으로 불러와서 EDA 및 feature engineering을 거친 뒤, 머신러닝 알고리즘으로 예측해보려는 시도를 해보겠다

보고서 작성이 금요일까진데,, 월요일부터 본격 시도를 하려고한다..
ㅠㅠ 시간이 많이 촉박하다..

통계적 추론 2가지

어떤 이론이 과학적이려면 반증 가능성을 가져야한다.
반증가능성이란? 가설이 실험or관찰에 의해서 반증될 가능성
쉽게 말하자면, H1을 증명하기 어려우니 H0가 아니다로 증명

통계적 추론
1.추정 : 표본으로 모수 등 모집단에 대한 어떤 값을 추즉하는 과정으로 추정값 + 오차범위(표본오차, 신뢰구간) 같이 제시
- 추정 : 모집단의 평균이 30이다.

2.가설검정 : 표본으로 모집단에 대한 주장or가설의 옳고 그름을 판정 하거나 대립가설(H1)을 채택 or 귀무가설(H0)을 기각
- 검정 : 모집단의 평균이 30보다 클 것이다.

가설검정의 4가지 절차

1.알고있던 귀무가설(H0)입증하고자하는 대립가설(H1) 세우기 - 보수적, 잘 알려진 것을 H0로 잡고, H0를 실험한다.
- 입증하고자하는 대립가설을 증명하기는 너무 어려우니 알고 있던 귀무가설이 참이라고 가정하고 실험해서 반증하자
- 반증의 기준은 알고 있던 H0의 결과(검정통계량)가 연구자가 정한 유의수준(H0실험 분포상 x축에 찍히는 임계치결정)보다 바깥쪽에 찍혀 검정통계량(H0의 결과)에 의한 유의 확률이 기각역에 포함되어 **우연에 의한것으로 판단** -> 입증하고자**하는 H1가 유의미하다는 것으로 반증**.

2.검정통계량산출(H0가 참이라고 가정하고 한 실험에서 나온 결과 - 해당 분포 그래프 상 x축에 찍히는 점)

3.유의수준 결정 by 연구자 ( 분포상 임계치(-x축에 찍히는 점)와 기각역(-임계치보다 바깥쪽)을 결정)
- 유의수준은 연구자가 결정하는 것으로 H0로 실험한 결과를 우연이 아니라고 해줄 수 있는 한계치. 보통 p-value : 0.05로 한다.
- 유의확률은 H0로 실험한 결과 나오는 확률이다.

4.검정결과 해석

가설검정에 대하여

가설검정 자체는 보수적인 실험으로서 반증가능성을 실험한다.
먼저, 알고 있던 귀무가설(H0)는 실험전까지 맞고 판단하면서 살아간다.
다음, 입증하고자 하는 대립가설(H1)은 실험 후, 알고있던 귀무가설이 아니라는 충분한 근거(유의확률이 유의수준보다 적게나왔을 때)가 있을 때 채택한다.

통계적 추론(값 추정과 가설 검정)의 목적은 data로 의사결정을 내리는 것이다.
통계적 추론(값 추정과 가설 검정)에서의 통계적(statistical)이라는 말은 표본을 이용해서 추론한다는 말이다.

통계라는 말에는 항상 에러가 우연히 발생(error)가 있으니, 우연이 아니라 정말 의미가 있다(effect)를 근거로 통계 분석(통계적 추론 중 가설검정)을 통해 시작한다.

즉, 실험 결과가 우연(error)냐 아니냐(effect)를 결정하는 것이 가설검정이다.

가설검정의 용어

  1. H0 : 알고 있던, 기존에 알려진 가설 = 귀무가설 => 유의확률(p-value) - (x축상 검정통계량, H0의 결과확률)가 유의수준(0.05)(x축상 임계치)보다 작을 때 기각
    -이것을 바탕으로 표본을 이용해 실험이 이루어진다. 분포, 그래프 전부다H0가 참이다라는 가정하에 그려진다.
  2. H1 : 입증하고자 하는, 알고자하는 가설 = 대립가설 => 유의확률(p-value)가 유의수준(0.05)보다 작을 때 채택
    연구자는 귀무가설을 기각하고 싶어하고, 대립가설을 채택하고 싶어 할 것이다.
  3. 제 1종 오류(a) : 귀무가설이 참인데 불구하고, 귀무가설을 기각 = 대립가설을 채택한 오류 => 제 2종 오류보다 심각 => 허용한계를 정해야함(보통 0.05)
    -효과/차이가 없다(H0)가 참인데 불구하고 기각 + 효과/차이가 있다고 판단. => 엄청난 부작용 발생가능성
  4. 유의 확률(p값,p-value, significance probability) : 제 1종 오류가 발생할 확률 = 귀무가설이 참인데 불구하고 알고자하는 대립가설을 채택할 확률 -> 이것의 허용치가 유의수준 -> 유의수준 허용치 안에 들어온다면, H0(귀무가설)기각 H1(대립가설 채택).
  5. 유의수준(Significant Level) : <귀무가설이 참이다 가정 하에> 데이터를 기반으로한(표본을 이용한) 실험결과(검정통계량)가 항상 옳은 것이 아니므로 연구가가 정한 유의확률(제1종오류가 발생할 확률 = 귀무가설이 참인데, 기각 + 대립가설을 채택하는 확률)의 최대허용한계, 최대값, 마지노선. 보통은 0.05로 잡는게 관례 => 실험의 분포 x축에 임계치(Critical value)가 결정되며, 그 이하가 H0의 기각역(Critical Region or reject region)이 됨
    -쉽게 말하면, 유의수준 0.05 = 100번 중 최대 5번까지는 실수(제1종오류=귀무가설 참인데 기각)가 발생하더라도 허용한다.
    -유의확률 0.03 = 100번 중 3번 실수(H1를 채택) -> 100번 중 5번(유의수준 0.05) 안에 들었다. -> 귀무가설이 참이더라도 기각하고 대립가설 채택
  6. 제 2종 오류(b) : 귀무가설이 거짓인데 불구하고, 귀무가설을 기각하지 않은 오류 = 제 2종 오류(b)
  7. 검정통계량(Test Statistic, M-) : 귀무가설이 참이다는 가정 하에, 표본을 이용하여 계산된 통계량, 실제 실험의 결과 => 검정 통계량 이하의 확률이 유의확률이 됨

가설검정의 예시

  1. H0 : A반 키의 평균은 160이다.에 대해 H1 : A반 키의 평균은 160이 아니다를 입증하고 싶다.
    1) 가설설정은 되었다. 2번째 검정통계량(H0 참이라는 가정하 실험해서 결과)을 구하기 위해, A반 학생 10명을 뽑아서 평균 키를 젠다
    2) 유의수준(0.05)하에서 검정통계량(10명의 평균 키)을 바탕으로 유의확률을 계산한다
    3) 기각역에 들어오면 H0 기각 = H1 채택한다.

  2. 휴지가 300m인지 확인해보고싶다. (보수적, 잘알려진 H0 : 휴지의 평균길이는 300m임)
    1) 가설설정 : H0 : 휴지의 평균길이 300m이다. H1 : 휴지의 평균길이가 300m이 아니다.
    2) H0하에 검정통계량을 계산하기 위해서 휴지 10개의 길이를 제고 유의수준하에 유의확률을 확인한다
    3) H0기각 = H1채택 하거나 H0를 그대로 기각하지않는다.

  3. 숟가락이 잘 구부려진다 -> 나는 초능력자다( 보수적, 잘알려진 H0 : 나는 초능력자x : 내 능력 = 다른사람의 능력)
    1) 가설설정 : H0 : 나의 능력 = 다른사람들의 능력(보통 숟가락30개 구부림) / H1 : 내 능력 != 다른사람들의 능력(30개보다 더 많이 구부릴 것이다)
    2) H0하에서 10명과 함께 숟가락 구부린 횟수의 평균과 내 구부린 횟수의 평균을 비교하여, 검정통계량을 계산한다
    3) 유의수준하에서 유의확률을 확인하여, H0 기각 ? 기각x ? 를 결정한다.

  4. 듀라셀 vs 에너자이져의 수명 차이
    1) 가설설정 : H0 : 듀라셀 수명 = 에너자이저 수명
    2) H0하에서 10개의 평균 수명을 비교 실험하여 검정통계량 계산
    3) 유의수준 하(H0실험의 t분포에서 0.05)에서 검정통계량에 의한 유의확률을 확인하여 H0 기각x or H1 채택 을 선택한다.

가설검정의 종류

  1. 양측검정 : H1 : Ma != Mb 으로서 실험결과분포의 양쪽(Ma > Mb & Ma < Mb)을 봐야한다.
  2. 단측검정 : H1 : Ma > Mb or Ma < Mb으로서 실험결과분포의 한쪽만 본다.

오늘날의 p-value

  1. 빅데이터 시대로서 n수가 늘어나서 -> 유의확률0.05 들어가는 검정통계량도 늘었다 -> 다 유의하다고 나옴
  2. p-value만 보기에는 문제가 생길 수 있다. -> 평균차이와 비율차이를 같이 제시
  3. 유의함을 넘어서 더 좁아진 유용함(MCID, The Minimal Clinically Important Change Score)구간도 생김
  4. JAMA에서 발표한 임상의 확증적 연구 : 0.005쓰자
  5. 미국통계학회(ASA) : 종합해서 판단하자.

문자열로 입력된 날짜를 임시로 만들고 년/월/일을 짤라놓은 뒤 || 을 이용해 년 월 일을 '-'로 연결한 뒤,다시 DATE타입으로 바꾸어 연산해보는 공부

  • 칼럼을 생성하고 싶다면 : SELECT절에서  값 AS 칼럼명
  • timestamp 1개로 년/월/일 3개로 나누기 : TOCHAR_( date or timestamp, ' date 기호 ')
  • || : SELECT절에서 문자열칼럼 or 생성한문자열 을 concat해준다.
  • 둘다 DATE타입일 경우, 마이너스 연산이 가능하다. 연산결과는 n days + 시간 -> int로 캐스팅해야 숫자로 사용( 나누기 365 등)할 수 있다.
  • PosgreSQL에서 birthday를 만나이로 계산하는 순서(년/월/일)로 나뉘어있다고 가정
    (1) || 을 통해 YYYY-MM-DD형태로 변환
    (2) DATE타입으로 캐스팅
    (3) NOW()현재날짜와 (-) 연산
    (4) int타입으로 캐스팅
    (5) /365해주기

  • MSSQL에서 birthday를 만나이로 계산하는 순서( YYYY-MM-DD)로 나뉘었다고 가정 :
    (1) DATEDIFF( day, 시작날짜, GETDATE()) / 365자동연산


전처리

  1. 기존 테이블
  1. SELECT에 < 한개의자료 AS 칼럼명 > 으로 새 칼럼을 생성한 뒤, INTO로 테이블 생성

    • '문자열' AS 칼럼명 (text형으로 칼럼생성)
    • NOW() AS 칼럼명 (timestamp형로 칼럼생성) 등등.. 다된다.
      SELECT *, NOW() AS time INTO students_with_birthday FROM students;
      
  2. time칼럼에 날짜 입력해주기

    UPDATE students_with_birthday SET time = '1987-01-30' WHERE name ='Moe';
    UPDATE students_with_birthday SET time = '1988-03-29' WHERE name ='Larry';
    UPDATE students_with_birthday SET time = '1980-02-28' WHERE name ='Rolf';
    UPDATE students_with_birthday SET time = '1987-06-27' WHERE name ='Anne';
    UPDATE students_with_birthday SET time = '1991-02-26' WHERE name ='Robert';
    UPDATE students_with_birthday SET time = '1958-01-26' WHERE name ='Jose';
    

  1. 날짜(YYYY-MM-DD)를 문자열 & 년/월/일 로 분리하여 칼럼 만들고, 확인되면 INTO 넣어서 필요한 칼럼들만으로 테이블로 만들기
    SELECT *,
     TO_CHAR(time, 'YYYY') as year_of_birth, 
     TO_CHAR(time, 'MM') as month_of_birth, 
     TO_CHAR(time, 'DD') as day_of_birth 
    FROM students_with_birthday
    
    SELECT name, current_mood,
     TO_CHAR(time, 'YYYY') as year_of_birth, 
     TO_CHAR(time, 'MM') as month_of_birth, 
     TO_CHAR(time, 'DD') as day_of_birth 
    INTO students_birthday
    FROM students_with_birthday
    SELECT * FROM students_birthday
    

문자열인 년/월/일 칼럼을 || 과 sep '-'로 붙히는 연습해보기

  1. 문자열칼럼 || '문자열' or 문자열 칼럼
    SELECT year_of_birth  FROM students_birthday                         -- 2019
    SELECT year_of_birth||'-'  FROM students_birthday                     -- 2019-
    SELECT year_of_birth||'-'||month_of_birth  FROM students_birthday            -- 2019-02 
    SELECT year_of_birth||'-'||month_of_birth||'-'||day_of_birth FROM students_birthday     -- 2019-02-01
    
  1. 문자열 YYYY || MM || DD 를 concat(||)으로 붙힌 것을 CAST( AS DATE)으로 타입 변환하기 ( YYYY-MM-DD 형태 아니면 안됨 )

    SELECT CAST( year_of_birth||'-'||month_of_birth||'-'||day_of_birth AS DATE),* FROM students_birthday 
    
  2. date타입으로 바꿨으면, 특정시간칼럼과 연산이 가능하다.

    SELECT NOW() - CAST( year_of_birth||'-'||month_of_birth||'-'||day_of_birth AS DATE) FROM students_birthday 
    

  3. 날짜(둘다 DATE여야함.) 차이결과는 days +시간으로 나온다. 이것을 int로 다시 형변환하면, day숫자만 나온다. * NOW()는 date가 아니라 timestamp로서 다른 타입이니 또 캐스팅

    SELECT CAST( CAST(NOW() AS DATE) - CAST( year_of_birth||'-'||month_of_birth||'-'||day_of_birth AS DATE)  AS INT) FROM students_birthday 
    
  4. day를 /365 로 나누면, 만나이(년수)가 나온다.

    SELECT CAST( CAST(NOW() AS DATE) - CAST( year_of_birth||'-'||month_of_birth||'-'||day_of_birth AS DATE)  AS INT) / 365 FROM students_birthday 
    


02. 01 첫 cdm 스터디

2019. 2. 1. 23:55

최* 선생님께서 postgesql 서버에 접속한 노트북으로 쿼리를 돌리는 경험을 하게 해주셨다.

다만 민감한 서버라 내가 직접 가지고 연습할수는 없는가보다.

SQL의 귀재 김**선생님 의 세미나를 3시간가량 들었는데 그것을 매일 1회씩 5번 정도 반복했다

처음에는 4시간 넘게 걸리던 것이 1시간 가량으로 줄어들어 익숙해졌다.

 

쿼리보는 눈이 생긴것 같다.

앞으로 남은 기간동안 잘 할 수 있으려나

 

쿼리결과만 받아서 어떻게 분석할지 고민해보기로 했다.

설날동안 그동안 pandas분석한 것을 복습해서 최대한 결과물을 만들어봐야겠다.

2-2 ggplot - 산점도, bar차트, boxplot / plotly

ggplot - 산점도1

  • 도화지 : g <- ggplot(data, aes( 칼럼명1, 칼럼명2 ))
  • 그림1) 산점도 : g(data,aes(숫자1,숫자2)) + geom_point(aes( ))
  • 그림의 aes안에 colour, shape = factor(범주칼럼)으로 범주별 확인가능
  • 그림의 aes안에 size, alpha = 숫자형칼럼으로 scale범주별 확인가능
  • 그림의 aes안에 colour가 숫자형칼럼(factor()도 x)인 경우, scale바의 옵션을 지정해줄 수 있다. scale_colour_gradient(low = “red”, high = “green” )도
# install.packages("ggplot2")
library(ggplot2)

#  데이터 준비
data(mtcars)
View(mtcars)

# 1. 도화지 그리기
ggplot(mtcars, aes(wt, mpg))

# 2. 도화지에 산점도 그리기 + 색/모양으로 범주별로 보기
ggplot(mtcars, aes(wt, mpg)) +
  geom_point( aes( colour = factor(vs),
                   shape  = factor(cyl)) )

#  3. 도화지에 산점도 그리기 + 색/모양으로 범주별 + *size로 숫자형 칼럼을 scale로 
ggplot(mtcars, aes(wt, mpg)) +
  geom_point(aes(colour = factor(vs),shape=factor(cyl),size=qsec)) 

# 4. 도화지 + 산점도 + 색/모양으로로 범주별 + size로 숫자형칼럼을 scale로 + alpha로 숫자형 칼럼을 scale로 
#+ alpha
ggplot(mtcars, aes(wt, mpg)) +
  geom_point(aes(colour = factor(vs),shape=factor(cyl),size=qsec,alpha=wt))

# 5. colour가 숫자형칼럼(factor()도 x)인 경우, scale바의 옵션을 지정해줄 수 있다.
ggplot(mtcars, aes(wt, mpg)) +
  geom_point(aes(colour = disp,shape=factor(cyl),size=qsec,alpha=wt)) +
  scale_colour_gradient(low = "red")

ggplot(mtcars, aes(wt, mpg)) +
  geom_point(aes(colour = disp,shape=factor(cyl),size=qsec,alpha=wt)) +
  scale_colour_gradient(low = "red",high="green")

산점도 2 - facet_grid( ~ )범주별로 그림쪼개서 보기

  • 도화지는 변수에 담아둘 수 있다.
  • 산점도가 겹치는 경우 alpha 를 통해 투명도를 준다.
  • 범주별로 그림쪼개서 볼때, 앞의 범주( 범주칼럼 ~ .)는 가로방향으로 쪼갠다.
  • 범주별로 그림쪼개서 볼때, 뒤의 범주( . ~ 범주칼럼)는 세로방향으로 쪼갠다.
  • facet_grid( ~ )로 나눠봤을 때, 빈 그림이 존재하면 facet_wrap으로
    앞범주 X 뒷범주의 조합별로 산점도를 그리자 + ncol 도 입력가능하다.
# mpg 데이터(내장)
View(mpg)

# 숫자칼럼2개 -> 산점도 도화지
g <- ggplot(mpg, aes(displ, hwy))

# 숫자들이 겹치느 경우, alpha를 주어 겹치는 부분을 나타낸다.
g + geom_point(alpha=1/3) 

# facet_grid( ~ )로 범주칼럼을 주면 범주별로 앞(가로방향으로) , 뒤(세로방향으로) 쪼개서 볼 수 있다.
g + geom_point(alpha=1/3) + facet_grid(cyl ~ . )

g + geom_point(alpha=1/3) + facet_grid(. ~  cyl)

g + geom_point(alpha=1/3) + facet_grid(cyl ~  class)

# 만약 산점도가 범주별로 빈값이 존재한다면 -> facet_wrap( ~ , ncol=)을 쓰자.
# 앞 범주 X 뒷 범주의 조합으로 쪼개준다.
g + geom_point(alpha=1/3) + facet_wrap(cyl ~  class)

g + geom_point(alpha=1/3) + facet_wrap(cyl ~  class, ncol = 3)

ggplot - bar chart

  • barchart는 범주별 빈도를 보는 것이다.
  • 도화지는 ggplot(df, aes( facotr(범주칼럼) )) * 범주칼럼이라도 factor()로 싸준다.
  • 그림안에 aes없이 바로 witdh = 로 너비를 조절한다 (높이는 빈도가 책임)
  • 그림안의 aes()에 colour = factor(범주칼럼)으로 테두리색으로 범주별 범주빈도를 본다.
  • 그림안의 aes()에 fill = factor(범주칼럼)으로 stacked된 막대색으로 범주별 범주빈도를 본다.

  • 추가적인 + coord_flip() 으로 가로세로반전해준다.

# 범주칼럼1개에 대한 빈도 : geom_bar()
c<-ggplot(mtcars, aes(factor(cyl)))
c+geom_bar()

# bar의 너비 조절하기 witdh옵션
c + geom_bar(width=0.5)

# aes(colour = factor(범주칼럼))으로 테두리색으로 범주별 barchart(범주별 빈도)를 본다.
# 만약 같은 범주를 준다면, x축의 범주와 테두리색의 범주가 같을 것이다.
c + geom_bar(width = 0.5, aes( colour = factor(cyl) ) )

# aes(fill = factor(범주칼럼))으로 stacked로 나눠진 막대 색으로   범주별  barchart(범주별 빈도)를 본다.
c + geom_bar(width = 0.5, aes( colour = factor(cyl),
                               fill =  factor(am)) )

c + geom_bar(width = 0.5, aes( colour = factor(cyl),
                               fill =  factor(am)) ) + coord_flip()

ggplot - boxplot

  • 기본 boxplot은 숫자형칼럼의 분포를 보는 것이나
  • geom_boxplot()은 기본적으로 범주별 숫자칼럼의 분포를 보는 도화지를 aes에 그린다. ggplot( df, aes(범주칼럼, 숫자칼럼))
  • 그림은 geom_boxplot()으로 그린다.

  • ** 더이상 범주추가가 없는 경우 - width, color(테두리색), fill(박스색), outlier의 색/모양을 준다.

  • ** 범주를 추가하고 싶은 경우 2가지 방법
  1. 그림안의 aes()안에 colour = factor() or fill = factor() 로 준다.
  2. facet_grid()로 범주별로 그림을 쪼개준다.
  • 선을 추가하고 싶을때는 geom_hline() 혹은 geom_vline()을 + 한다(abline대체)
# 데이터 준비
setwd("C:/Users/is2js/python_da/deep analysis(논문용 설문지 분석)")
sens = read.csv('sens_for_r.csv', header = T)

# 데이터 확인 - 범주2, 숫자1
dim(sens)
## [1] 387   3
head(sens)
##   Kind.of.medicinal.herbs Sensitivity              Group
## 1                      SA        0.95 Ph.D. of Herbology
## 2                      SA        0.95 Ph.D. of Herbology
## 3                      SA        1.00 Ph.D. of Herbology
## 4                      SA        0.95 Ph.D. of Herbology
## 5                      SA        0.90 Ph.D. of Herbology
## 6                      SA        0.95 Ph.D. of Herbology
summary(sens)
##  Kind.of.medicinal.herbs  Sensitivity                   Group    
##  AC :129                 Min.   :0.000   KMD               : 36  
##  AMC:129                 1st Qu.:0.650   Ph.D. of Herbology: 18  
##  SA :129                 Median :0.800   Undergraduates    :333  
##                          Mean   :0.757                           
##                          3rd Qu.:0.950                           
##                          Max.   :1.000
str(sens)
## 'data.frame':    387 obs. of  3 variables:
##  $ Kind.of.medicinal.herbs: Factor w/ 3 levels "AC","AMC","SA": 3 3 3 3 3 3 2 2 2 2 ...
##  $ Sensitivity            : num  0.95 0.95 1 0.95 0.9 0.95 1 1 0.95 0.95 ...
##  $ Group                  : Factor w/ 3 levels "KMD","Ph.D. of Herbology",..: 2 2 2 2 2 2 2 2 2 2 ...
apply(is.na(sens), MARGIN = 2, FUN='sum')
## Kind.of.medicinal.herbs             Sensitivity                   Group 
##                       0                       0                       0
# summarizeColumns(sens)

View(sens)

# 기본 범주-숫자형의    범주별 boxplot(숫자의 분포)
boxplot( Sensitivity ~ Group,
data= sens,
notch = TRUE,
col = "orange") 
## Warning in bxp(list(stats = structure(c(0.1, 0.55, 0.825, 0.9, 1, 0.75, :
## some notches went outside hinges ('box'): maybe set notch=FALSE

# ggplot - geom_boxplot(범주칼럼, 숫자칼럼, fill = 범주칼럼 or 새로운범주칼럼)
g <- ggplot(sens, aes(Group, Sensitivity))
g + geom_boxplot()

# (더이상의 추가 범주가 없을 때!)barplot처럼 그림의 aes 바깥에서 바로 width + fill ='박스색', color='테두리색'을 그린다.
g <- ggplot(sens, aes(Group, Sensitivity))
g + geom_boxplot(width = 0.3, color = 'darkslategrey', fill ='orange')

# 아웃라이어들에 대한 색과 모양도 지정해줄 수 있다.
g + geom_boxplot(width = 0.3, color = 'darkslategrey', fill ='orange',
                 outlier.color = 'black',outlier.shape = 1)

# (추가범주 방법1) 그림안의 aes()에서 colour/fill를 준다는 것은 테두리/박스색 별로 범주를 추가해주는 것이다.
# 이때는, aes()바깥의 color, fill을 삭제해야한다.
g + geom_boxplot(width = 0.3, aes(colour = factor(Kind.of.medicinal.herbs)))

g + geom_boxplot(width = 0.3, aes(fill = factor(Kind.of.medicinal.herbs)))

# (추가범주 방법2) 그림을 짤라서 범주를 주고 싶다면 facet_grid를 쓴다.
g + geom_boxplot(width = 0.3, color = 'darkslategrey', fill ='orange') +
  facet_grid(.~Kind.of.medicinal.herbs)

# ggplot에서 새로운 선을 주고 싶을 땐 geom_hline/geom_vline으로 추가해준다.
# 여기서는 facet_grid로 범주까지 나뉘어져있으니, 그 범주를 주어 3개다 그이게 한다.
# 라인의 방향에 따라 y/xintercept를 준다. 
g + geom_boxplot(width = 0.3, color = 'darkslategrey', fill ='orange') +
  facet_grid(.~Kind.of.medicinal.herbs)  +
  geom_hline(aes(yintercept = mean(Sensitivity), group = Kind.of.medicinal.herbs), colour = 'red')

g + geom_boxplot(width = 0.3, color = 'darkslategrey', fill ='orange') +
  facet_grid(.~Kind.of.medicinal.herbs)  +
  geom_vline(aes(xintercept = mean(Sensitivity), group = Kind.of.medicinal.herbs), colour = 'green')


데이터 준비 및 확인

library(moonBook)
data(acs) # 급성 심근경색

#View(acs)
dim(acs)
## [1] 857  17
head(acs)
##   age    sex cardiogenicShock   entry              Dx   EF height weight
## 1  62   Male               No Femoral           STEMI 18.0    168     72
## 2  78 Female               No Femoral           STEMI 18.4    148     48
## 3  76 Female              Yes Femoral           STEMI 20.0     NA     NA
## 4  89 Female               No Femoral           STEMI 21.8    165     50
## 5  56   Male               No  Radial          NSTEMI 21.8    162     64
## 6  73 Female               No  Radial Unstable Angina 22.0    153     59
##        BMI obesity  TC LDLC HDLC  TG  DM HBP smoking
## 1 25.51020     Yes 215  154   35 155 Yes  No  Smoker
## 2 21.91381      No  NA   NA   NA 166  No Yes   Never
## 3       NA      No  NA   NA   NA  NA  No Yes   Never
## 4 18.36547      No 121   73   20  89  No  No   Never
## 5 24.38653      No 195  151   36  63 Yes Yes  Smoker
## 6 25.20398     Yes 184  112   38 137 Yes Yes   Never
str(acs)
## 'data.frame':    857 obs. of  17 variables:
##  $ age             : int  62 78 76 89 56 73 58 62 59 71 ...
##  $ sex             : chr  "Male" "Female" "Female" "Female" ...
##  $ cardiogenicShock: chr  "No" "No" "Yes" "No" ...
##  $ entry           : chr  "Femoral" "Femoral" "Femoral" "Femoral" ...
##  $ Dx              : chr  "STEMI" "STEMI" "STEMI" "STEMI" ...
##  $ EF              : num  18 18.4 20 21.8 21.8 22 24.7 26.6 28.5 31.1 ...
##  $ height          : num  168 148 NA 165 162 153 167 160 152 168 ...
##  $ weight          : num  72 48 NA 50 64 59 78 50 67 60 ...
##  $ BMI             : num  25.5 21.9 NA 18.4 24.4 ...
##  $ obesity         : chr  "Yes" "No" "No" "No" ...
##  $ TC              : num  215 NA NA 121 195 184 161 136 239 169 ...
##  $ LDLC            : int  154 NA NA 73 151 112 91 88 161 88 ...
##  $ HDLC            : int  35 NA NA 20 36 38 34 33 34 54 ...
##  $ TG              : int  155 166 NA 89 63 137 196 30 118 141 ...
##  $ DM              : chr  "Yes" "No" "No" "No" ...
##  $ HBP             : chr  "No" "Yes" "Yes" "No" ...
##  $ smoking         : chr  "Smoker" "Never" "Never" "Never" ...
summary(acs)
##       age            sex            cardiogenicShock      entry          
##  Min.   :28.00   Length:857         Length:857         Length:857        
##  1st Qu.:55.00   Class :character   Class :character   Class :character  
##  Median :64.00   Mode  :character   Mode  :character   Mode  :character  
##  Mean   :63.31                                                           
##  3rd Qu.:72.00                                                           
##  Max.   :91.00                                                           
##                                                                          
##       Dx                  EF            height          weight      
##  Length:857         Min.   :18.00   Min.   :130.0   Min.   : 30.00  
##  Class :character   1st Qu.:50.45   1st Qu.:158.0   1st Qu.: 58.00  
##  Mode  :character   Median :58.10   Median :165.0   Median : 65.00  
##                     Mean   :55.83   Mean   :163.2   Mean   : 64.84  
##                     3rd Qu.:62.35   3rd Qu.:170.0   3rd Qu.: 72.00  
##                     Max.   :79.00   Max.   :185.0   Max.   :112.00  
##                     NA's   :134     NA's   :93      NA's   :91      
##       BMI          obesity                TC             LDLC      
##  Min.   :15.62   Length:857         Min.   : 25.0   Min.   : 15.0  
##  1st Qu.:22.13   Class :character   1st Qu.:154.0   1st Qu.: 88.0  
##  Median :24.16   Mode  :character   Median :183.0   Median :114.0  
##  Mean   :24.28                      Mean   :185.2   Mean   :116.6  
##  3rd Qu.:26.17                      3rd Qu.:213.0   3rd Qu.:141.0  
##  Max.   :41.42                      Max.   :493.0   Max.   :366.0  
##  NA's   :93                         NA's   :23      NA's   :24     
##       HDLC             TG             DM                HBP           
##  Min.   : 4.00   Min.   : 11.0   Length:857         Length:857        
##  1st Qu.:32.00   1st Qu.: 68.0   Class :character   Class :character  
##  Median :38.00   Median :105.5   Mode  :character   Mode  :character  
##  Mean   :38.24   Mean   :125.2                                        
##  3rd Qu.:45.00   3rd Qu.:154.0                                        
##  Max.   :89.00   Max.   :877.0                                        
##  NA's   :23      NA's   :15                                           
##    smoking         
##  Length:857        
##  Class :character  
##  Mode  :character  
##                    
##                    
##                    
## 
apply(is.na(acs), MARGIN = 2, FUN='sum')
##              age              sex cardiogenicShock            entry 
##                0                0                0                0 
##               Dx               EF           height           weight 
##                0              134               93               91 
##              BMI          obesity               TC             LDLC 
##               93                0               23               24 
##             HDLC               TG               DM              HBP 
##               23               15                0                0 
##          smoking 
##                0
library(mlr)
## Loading required package: ParamHelpers
summarizeColumns(acs)
##                name      type  na      mean        disp    median
## 1               age   integer   0  63.31155 11.69630382  64.00000
## 2               sex character   0        NA  0.33488915        NA
## 3  cardiogenicShock character   0        NA  0.06067678        NA
## 4             entry character   0        NA  0.36406068        NA
## 5                Dx character   0        NA  0.53325554        NA
## 6                EF   numeric 134  55.83444  9.62296368  58.10000
## 7            height   numeric  93 163.17539  9.07930672 165.00000
## 8            weight   numeric  91  64.84373 11.35519114  65.00000
## 9               BMI   numeric  93  24.28190  3.34669608  24.16104
## 10          obesity character   0        NA  0.33838973        NA
## 11               TC   numeric  23 185.20024 47.77292477 183.00000
## 12             LDLC   integer  24 116.58343 41.09484883 114.00000
## 13             HDLC   integer  23  38.23501 11.08668982  38.00000
## 14               TG   integer  15 125.23872 90.85259401 105.50000
## 15               DM character   0        NA  0.35472579        NA
## 16              HBP character   0        NA  0.41540257        NA
## 17          smoking character   0        NA  0.61260210        NA
##          mad      min       max nlevs
## 1  13.343400  28.0000  91.00000     0
## 2         NA 287.0000 570.00000     2
## 3         NA  52.0000 805.00000     2
## 4         NA 312.0000 545.00000     2
## 5         NA 153.0000 400.00000     3
## 6   7.857780  18.0000  79.00000     0
## 7   7.413000 130.0000 185.00000     0
## 8  10.378200  30.0000 112.00000     0
## 9   3.011608  15.6157  41.42012     0
## 10        NA 290.0000 567.00000     2
## 11 42.995400  25.0000 493.00000     0
## 12 40.030200  15.0000 366.00000     0
## 13 10.378200   4.0000  89.00000     0
## 14 60.045300  11.0000 877.00000     0
## 15        NA 304.0000 553.00000     2
## 16        NA 356.0000 501.00000     2
## 17        NA 204.0000 332.00000     3

VIM 패키지의 VIM 라이브러리를 통한 null값 확인

# install.packages("VIM")
library(VIM)
## Loading required package: colorspace
## Loading required package: grid
## Loading required package: data.table
## VIM is ready to use. 
##  Since version 4.0.0 the GUI is in its own package VIMGUI.
## 
##           Please use the package to use the new (and old) GUI.
## Suggestions and bug-reports can be submitted at: https://github.com/alexkowa/VIM/issues
## 
## Attaching package: 'VIM'
## The following object is masked from 'package:datasets':
## 
##     sleep
aggr(acs, prop = F, numbers = T)

범주형/숫자형 변수 나누어서 분석해주는 라이브러리 xda

# 범주형/숫자형 변수 확인을 위한 패키지 xda 설치 
#https://github.com/ujjwalkarn/xda
# library(devtools)
# install_github("ujjwalkarn/xda")
# 설치방법 : 1) *관리자 권환으로실행 -> 2) 깃허브인스톨 패키지설치 -> 3) 깃허브인스톨라이브러리로 xda 를 다운 -> 4) xda 라이브러리 사용
# install.packages("githubinstall")
# library(githubinstall)
# githubinstall("xda")
# 사용
# library(xda)
# 데이터셋 자체에 대해서 범주형과 숫자형 변수(칼럼)들 자동분석
# bivariate(iris,'Species','Sepal.Length',n.bins=2)
# bivariate(iris,'Sepal.Length','Species')

범주형 변수 분석 (범주 -> 빈도 / 범주-범주 -> 빈도로 이루어진 교차표 )

  • table()함수로 범주형 1개(단변수), 범주형 2개(이변수)를 빈도/교차표 생성
  • 카이제곱검정 chisq.test( table = 교차표 )로 범주형변수 2개의 연관성 확인
  • H0 : 서로 독립이다.
# 1. 범주형 단변수 분석
table(acs$Dx) #  범주별 빈도 확인
## 
##          NSTEMI           STEMI Unstable Angina 
##             153             304             400
prop.table( table(acs$Dx)) # 범주별 상대빈도 확인
## 
##          NSTEMI           STEMI Unstable Angina 
##       0.1785298       0.3547258       0.4667445
barplot( table(acs$Dx) ,
         col = c("orange", "blue", "red") ) # 범주별 빈도 bar chart

# install.packages("qualityTools")
library(qualityTools)
## Loading required package: Rsolnp
## 
## Attaching package: 'Rsolnp'
## The following object is masked from 'package:mlr':
## 
##     benchmark
## Loading required package: MASS
## 
## Attaching package: 'qualityTools'
## The following object is masked from 'package:data.table':
## 
##     cube
## The following object is masked from 'package:stats':
## 
##     sigma
paretoChart(table(acs$Dx))   # 범주별빈도 + 누적합계 bar chart + line 

##                                   
## Frequency         400   304    153
## Cum. Frequency    400   704    857
## Percentage      46.7% 35.5%  17.9%
## Cum. Percentage 46.7% 82.1% 100.0%
##                                              
## Frequency       400.00000 304.00000 153.00000
## Cum. Frequency  400.00000 704.00000 857.00000
## Percentage       46.67445  35.47258  17.85298
## Cum. Percentage  46.67445  82.14702 100.00000
paretoChart(table(acs$Dx),
            showTable = F)

##                                   
## Frequency         400   304    153
## Cum. Frequency    400   704    857
## Percentage      46.7% 35.5%  17.9%
## Cum. Percentage 46.7% 82.1% 100.0%
##                                              
## Frequency       400.00000 304.00000 153.00000
## Cum. Frequency  400.00000 704.00000 857.00000
## Percentage       46.67445  35.47258  17.85298
## Cum. Percentage  46.67445  82.14702 100.00000
# 2. 범주형 이변수 분석
table(acs$sex, acs$Dx) # 범주-범주 분석- 범주별 빈도 교차표
##         
##          NSTEMI STEMI Unstable Angina
##   Female     50    84             153
##   Male      103   220             247
tab <-table(acs$sex, acs$Dx)
addmargins(tab) # 범주2개의 교차표에 sum을 표함시킨다.
##         
##          NSTEMI STEMI Unstable Angina Sum
##   Female     50    84             153 287
##   Male      103   220             247 570
##   Sum       153   304             400 857
prop.table(tab) # 전체합에 대한 상대빈도 교차표
##         
##              NSTEMI      STEMI Unstable Angina
##   Female 0.05834306 0.09801634      0.17852975
##   Male   0.12018670 0.25670945      0.28821470
prop.table(tab, 1) # 1= 가로에 있는 범주(sex)에 대한(분모) 상대빈도 교차표
##         
##             NSTEMI     STEMI Unstable Angina
##   Female 0.1742160 0.2926829       0.5331010
##   Male   0.1807018 0.3859649       0.4333333
prop.table(tab, 2) # 2=0=세로에 있는 범주(Dx)에 대한 상대빈도 교차표
##         
##             NSTEMI     STEMI Unstable Angina
##   Female 0.3267974 0.2763158       0.3825000
##   Male   0.6732026 0.7236842       0.6175000
chisq.test( table(acs$sex, acs$Dx)) # 2개 범주의 독립성 확인(H0 : 독립)
## 
##  Pearson's Chi-squared test
## 
## data:  table(acs$sex, acs$Dx)
## X-squared = 8.7983, df = 2, p-value = 0.01229
mycol <- c("skyblue", "blue") # 범주 2개에 대한 색 미리 지정
barplot( table(acs$sex, acs$Dx),  # 앞에 넣은 범주가 범례가 된다.
         col = mycol , main ="Segmented Bar Plot of Gender")
legend("topleft", c("females "," males "), col= mycol, pch = 16)

mycol<-c("skyblue", "blue","darkblue")
barplot(table(acs$Dx, acs$sex), col = mycol, main = "Segmented Bar Plot of Diagnosis")
legend("topleft", c("NSTEMI", "STEMI", "Unstable Angina"), col= mycol, pch = 16, inset =0.05)

숫자형 변수 분석

  • 숫자형 변수는 분포를 확인해야하니 boxplot 이다.
  • 숫자형 변수의 분포 확인2 - 정규분포확인 hist
# 1. 숫자형 단변수
summary(acs$TG)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##    11.0    68.0   105.5   125.2   154.0   877.0      15
quantile(acs$TG, na.rm=T)
##    0%   25%   50%   75%  100% 
##  11.0  68.0 105.5 154.0 877.0
IQR(acs$TG, na.rm=T)
## [1] 86
var(acs$TG, na.rm=T)
## [1] 8254.194
sd(acs$TG, na.rm=T)
## [1] 90.85259
stem(acs$TG) # 줄기-잎 플롯
## 
##   The decimal point is 2 digit(s) to the right of the |
## 
##   0 | 12222222233333333333333333333333333333444444444444444444444444444444
##   0 | 55555555555555555555555555555555555555555555556666666666666666666666+220
##   1 | 00000000000000000000000000000000000000000000000111111111111111111111+151
##   1 | 55555555555555555555555555555555555666666666666666666666666666666777+55
##   2 | 000000001111111112222222222222222333344444444444
##   2 | 555556666666667778888899
##   3 | 000111223
##   3 | 5788899
##   4 | 1123
##   4 | 555669
##   5 | 4
##   5 | 58
##   6 | 2
##   6 | 58
##   7 | 
##   7 | 
##   8 | 0
##   8 | 8
#install.packages("mosaic")
library(mosaic)
## Loading required package: dplyr
## 
## Attaching package: 'dplyr'
## The following object is masked from 'package:MASS':
## 
##     select
## The following objects are masked from 'package:data.table':
## 
##     between, first, last
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
## Loading required package: lattice
## 
## Attaching package: 'lattice'
## The following object is masked from 'package:moonBook':
## 
##     densityplot
## Loading required package: ggformula
## Loading required package: ggplot2
## Loading required package: ggstance
## 
## Attaching package: 'ggstance'
## The following objects are masked from 'package:ggplot2':
## 
##     geom_errorbarh, GeomErrorbarh
## 
## New to ggformula?  Try the tutorials: 
##  learnr::run_tutorial("introduction", package = "ggformula")
##  learnr::run_tutorial("refining", package = "ggformula")
## Loading required package: mosaicData
## Loading required package: Matrix
## 
## The 'mosaic' package masks several functions from core packages in order to add 
## additional features.  The original behavior of these functions should not be affected by this.
## 
## Note: If you use the Matrix package, be sure to load it BEFORE loading mosaic.
## 
## Attaching package: 'mosaic'
## The following object is masked from 'package:Matrix':
## 
##     mean
## The following object is masked from 'package:ggplot2':
## 
##     stat
## The following objects are masked from 'package:dplyr':
## 
##     count, do, tally
## The following object is masked from 'package:qualityTools':
## 
##     dotPlot
## The following object is masked from 'package:mlr':
## 
##     resample
## The following objects are masked from 'package:stats':
## 
##     binom.test, cor, cor.test, cov, fivenum, IQR, median,
##     prop.test, quantile, sd, t.test, var
## The following objects are masked from 'package:base':
## 
##     max, mean, min, prod, range, sample, sum
dotPlot(acs$TG,pch=19,col=6)        # Dot plot

hist(acs$TG, pch=19, col="lightgray") # 히스토 그램 with 평균, 중앙값
abline(v= mean(acs$TG,na.rm=T), col = " blue")
abline(v= median (acs$TG,na.rm=T), col = "green")
legend("topright", c("Mean", "Median"), pch = c(16,15), col = c("blue", "green"))

par(mfrow=c(1,2))
boxplot(acs$TG, notch = T, col="orange", main="TG (in original scale)")
boxplot(log(acs$TG), notch = T, col="orange", main="TG (in log scale)")

par(mfrow=c(1,1))




# 2. 숫자형 이변수

# 데이터 준비
data("iris")
attach(iris)
# 데이터 확인
library(mlr)
summarizeColumns(iris) # 4가지 숫자형  +  1가지 범주형
##           name    type na     mean      disp median     mad  min  max
## 1 Sepal.Length numeric  0 5.843333 0.8280661   5.80 1.03782  4.3  7.9
## 2  Sepal.Width numeric  0 3.057333 0.4358663   3.00 0.44478  2.0  4.4
## 3 Petal.Length numeric  0 3.758000 1.7652982   4.35 1.85325  1.0  6.9
## 4  Petal.Width numeric  0 1.199333 0.7622377   1.30 1.03782  0.1  2.5
## 5      Species  factor  0       NA 0.6666667     NA      NA 50.0 50.0
##   nlevs
## 1     0
## 2     0
## 3     0
## 4     0
## 5     3
boxplot(Sepal.Length, notch = TRUE, col = "grey") # 숫자형 단변수의 분포 boxplot

boxplot(Sepal.Length~Species, data=iris, notch = TRUE, col = "orange") # 범주별 숫자의 분포boxplot 

# 숫자형 변수의 분포 확인2 - hist로 정규분폰지 보기
hist(Sepal.Length, 
     col = "grey", 
     main = "R default", 
     ylab = "Frequency", 
     freq = FALSE)

plot(iris[,-5]) # 산점도 행렬 기본

plot(iris,'Petal.Length') # 특정 숫자칼럼에 대한 산점도 행렬 

pairs(iris[,-5], gap = 0)  # 산점도행렬

pairs(iris[,-5], gap = 0, panel = panel.smooth) #산점도행렬 + 회귀라인

# 범주별 - 산점도(숫자-숫자) 행렬
# install.packages("mycor")
library(mycor)

plot(mycor(iris),type=1,groups=Species)

plot(mycor(iris),type=2,groups=Species) #hist추가

plot(mycor(iris),type=3,groups=Species) #상관계수와 fitting curve

abline의 2가지 이용기

    1. 숫자-숫자형의 산점도에서 나오는 회귀식 결과 or 절편(a)과 기울기(b)로 회귀선을 그림
    1. 수평(h) 혹은 수직(v)선을 추가로 그려줌
 #abline-회귀식에서 나온 계수로 회귀선 그리기
plot (cars , xlim =c(0, 25) )
abline (a=-5, b=3.5 , col ="red ")
abline(lm(dist~speed,data=cars),col="green")

# 추가 수평/수직선 그리기
plot (cars , xlim =c(0, 25) )
abline (a=-5, b=3.5 , col ="red ")
abline (h= mean ( cars $ dist ), lty =2, col =" blue ")
abline (v= mean ( cars $ speed ), lty =2, col =" green ")

블로그정리 1. np.random모듈

numpy 의 np.random. randint vs rand/randn

np.random.seed seed를 통한 난수 생성

np.random.randint 균일 분포의 정수 난수 1개 생성
np.random.rand 0부터 1사이의 균일 분포에서 난수 matrix array생성
np.random.randn 가우시안 표준 정규 분포에서 난수 matrix array생성

np.random.shuffle 기존의 데이터의 순서 바꾸기
np.random.choice 기존의 데이터에서 sampling
np.unique 데이터에서 중복된 값을 제거하고 중복되지 않는 값의 리스트를 출력
np.bincount 발생하지 않은 사건에 대해서도 카운트를 해준다

In [1]:
import numpy as np

random.randint 와 np.random.randint : 모두 (시작, n-1) 사이의 랜덤숫자 1개 뽑아내기

In [3]:
np.random.randint(6) # 0 or 1 or ~ or 5      0부터 5까지 랜덤한 숫자 1개 
Out[3]:
5
In [4]:
np.random.randint(1, 20) # 1부터 19까지 랜덤숫자 1개
Out[4]:
19

np.random.rand(m,n) : 0 ~ 1의 균일분포 표준정규분포 난수를 matrix array(m,n) 생성

In [5]:
np.random.rand(6)  
Out[5]:
array([0.82374834, 0.03504426, 0.19848749, 0.47607174, 0.98983665,
       0.63021609])
In [6]:
np.random.rand(3,2)
Out[6]:
array([[0.21023055, 0.46075628],
       [0.99993567, 0.29630209],
       [0.79509783, 0.05405658]])

np.random.randn(m,n) : 평균0, 표준편차1의 가우시안 표준정규분포 난수를 matrix array(m,n) 생성

In [7]:
np.random.randn(6)
Out[7]:
array([ 0.42240858,  0.39214236, -0.05216362, -0.31037385, -1.75930161,
        0.04749234])
In [8]:
np.random.randn(3, 2)
Out[8]:
array([[ 1.65238965, -0.75137173],
       [-1.59079976, -1.26309433],
       [ 0.20991563,  2.23786713]])

총 정리

  1. SEQUENCE와 달리, CREATE할 필요없이 그냥 테이블 생성시 칼럼 TYPE에 SERIAL 지정해주면 된다.
  2. 단, INSERT INTO 시, 테이블명옆에 table( serial칼럼을 제외한 칼럼명들)을 명시해주고 입력해야한다.
    만약, SERIAL 이외 칼럼 명시없이, 제외하고 입력하면 오류난다.

SERIAL

-- SERIAL type 
-- : postgreSQL에 없는 autoincrement를 SEQUENCE로 했었지만, 그럴 필요없이 쉽게할 수있는 type제공
-- (SEQUENCE START n -> SET -> OWNED BY 의 과정이 필요없어진다!)


-- (1) TABLE 생성시 type으로 SERIAL만 주면 된다. 1부터 시작한다.
CREATE TABLE test (
    id SERIAL PRIMARY KEY,
    name character varying(255)
)


-- (2) 입력시 id와 name 중 name만 주면 된다.
INSERT INTO test(name)
VALUES ('chojaeseong');


SELECT * FROM test


총 정리

ENUM

  1. ENUM 생성 : CREATE TYPE 이넘명 AS ENUM( '순서범주1', '순서범주2', '순서범주3' ) 형식으로 생성
  2. 테이블 생성시, datatype으로 지정해주기
  3. INSERT INTO 시 ENUM에 속한 범주만 입력가능
  4. WHERE 절에 특정범주에 부등호(< or >)를 통해 여러범주를 필터링할 수 있다.

NESTED SELECT

  1. 집계하는 테이블에서는, 집계결과를 따로 칼럼을 보기 위해서는, GROUP BY 에 넣어주고 그 칼럼별 집계밖에 못보지만,
    집계결과를 따로 NESTED SELECT(SubQuery)로 취급하면 -> SELECT문에서 연산에 사용 or WHERE의 조건으로 걸 수 있다.

    cf) 서브쿼리는 줄을 바꿔주고 tab하나 넣는게 보통양식
    cf) SELECT 집계연산 FROM 테이블 WHERE 조건 ---> 조건까지 만족하는 테이블 이후 집계가됨
    cf) alias 칼럼명은 "" 쌍따옴표로 만들어주자.

ENUM

-- 1) ENUM 

--     (1) 새로운 dataTYPE을 만드는데 ENUM으로서 만들자. ENUM()안에 limited된 String value들을 정의해주면,
--        ***넣은 순서대로 순서를 가지는*** 순서형 범주(Ordinal <-> Nomial명목형) 데이터 타입이 된다.
 CREATE TYPE mood AS ENUM('extremely unhappy', 'unhappy', 'ok', 'happy', 'extremely happy');

--    (2) 새로 생성한 ENUM type의 mood를 칼럼의 type으로 지정해서 테이블을 생성하자.
 CREATE TABLE students (
name character varying(255),
current_mood mood 
 );

 SELECT * FROM students --mood type의 칼럼이 생김

INSERT INTO students
VALUES ('Moe', 'hapy') -- ENUM 범주에 없는 값을 입력시키면 에러가 난다.

INSERT INTO students
VALUES ('Moe', 'happy')

INSERT INTO students
VALUES ('Larry', 'happy')

INSERT INTO students
VALUES ('Rolf', 'extremely unhappy')

INSERT INTO students
VALUES ('Anne', 'extremely happy')

INSERT INTO students
VALUES ('Robert', 'unhappy')

INSERT INTO students
VALUES ('Jose', 'happy')



SELECT * FROM students



 -- (3) ENUM은 순서형 범주(Ordinal)로서, WHERE 절에서 value로 필터링 할 수 있다.
 -- enum을 이용하면, string형으로 순서를 줄 수 있어서, 회원등급을 메길 수 있고
 -- 설문score도 적용시킬 수 있다. 등등 
 SELECT * FROM students WHERE current_mood > 'ok';

NESTED SELECTD

 -- NESTED SELECT : 집계결과를, 집게테이블에서 WHERE문 or SELECT에 다  쓸 수 있다.
 -- 매우 유용함. 쿼리 안에 쿼리가 들어가는 구조

 -- 예제1) 보다 높은 가격의 item들 목록

 -- 내가 한 것)
 SELECT AVG(price) FROM items        -- 73.16125
 SELECT * FROM items WHERE price > 73.16125

 -- 좋은 것으나 안되는 것)
 SELECT * FROM items WHERE price > AVG(price)

 -- 또다른 조건을 줄때는 HAVING을 쓰나, Group by 된 상태만 된다.

 -- 정답1) NESETED SELECT를 이용해야만, 집계테이블에서, 집계결과를, WHERE문에 사용할 수 있다.
--    (1) 평균(집계)결과값을 사용할 부분을 제외한 WHERE문 작성
SELECT * FROM items WHERE price > 
();
--    (2) 집계결과를 bracket()에 작성
SELECT * FROM items WHERE price > 
(SELECT AVG(items.price) FROM items);


--    my) 이전에 했었던 아이템 목록중 가장 비싼 아이템 골라오기
SELECT * FROM items WHERE price = 
(SELECT MAX(price) FROM items);





-- 예제2 ) 아이템들의 가격이 <아이템 평균가격>보다 얼마나 비싸거나 쌀까?

-- 내가 한 것 )
SELECT * FROM items;
SELECT AVG(price) FROM items;
SELECT items.name, items.price, () FROM itesm;
SELECT items.name, items.price, (SELECT AVG(price) FROM items) FROM items;
SELECT items.name, items.price - (SELECT AVG(price) FROM items) FROM items;


 -- 정답 1)
--    (1) 평균집계값을 () 으로 비워두고, 집계테이블에서 필요한 칼럼들을 SELECT
--        가격의 비교를 위해서는 빼야한다. numeric은 SELECT 란에서 바로 (-)된다.
SELECT items.name, items.price - () FROM items;

--    (2) 서브쿼리의 기본 형식은 라인을 바꿔주고, tab을 넣는 것이다.
SELECT items.name, items.price - (
    SELECT AVG(price) FROM items
) FROM items;





-- 예제3 )  expensive_items아이템들의 가격이
--       과 얼마나 차이나는가?
--        를 VIEW로 만들기

-- 내가 한 것)
SELECT * FROM items;
SELECT * FROM items WHERE price > 80;

SELECT AVG(price) FROM items WHERE price > 80; -- 155.163 : FROM WHERE이후 --> WHERE절로 필터링
SELECT AVG(price) FROM items;    --73.16

SELECT items.name, items.price - () FROM items;

SELECT items.name, items.price - (
    SELECT AVG(price) FROM items WHERE price > 80
) FROM items;

-- 내가 catch못한 것 : 80달러보다 비싼 아이템의 평균값만 구하고, 
-- 그 이후 바깥의테이블에서는 80달러보다 비싸다는 WHERE를 안달아줘서, 모든 items들이 다 나왔음.
-- 문제를 잘못받아적음. 기본적으로 expensive 아이템가격들의 expensive아이템의 평균과의 차이 
-- ***FROM WHERE 필터링 이후--> SELECT의 집계연산이 이루어진다.


-- 정답3 )
--    (1) 80달러보다 비싼 아이템의 평균  = WHERE 절까지 거친 테이블에 집계함수가 일어남
SELECT AVG(items.price) FROM items WHERE price > 80;

--    (2) 다른칼럼들과 함께 + price - (1) 의 차이 SELECT하기 + WHERE 절 안빠트리기
SELECT *, items.price - (

) 
FROM items WHERE price > 80;

--    (3) NESTED SELECT 넣어주기
SELECT *, items.price - (
SELECT AVG(items.price) FROM items WHERE price > 80
) 
FROM items WHERE price > 80;

--    (4) VIEW로 만들기
CREATE VIEW expensive_items_diff AS 
SELECT *, items.price - (
SELECT AVG(items.price) FROM items WHERE price > 80
) 
FROM items WHERE price > 80;

--    (5) VIEW SELECT해보기
SELECT * FROM expensive_items_diff

--    (6) 칼럼이름이 없어서, 다시 DROP후 서브쿼리 ()끝나고 AS로 칼럼명 " "로 alias 정의해주기
DROP VIEW expensive_items_diff;

CREATE VIEW expensive_items_diff AS 
SELECT *, items.price - (
SELECT AVG(items.price) FROM items WHERE price > 80
) AS "average_diff"
FROM items WHERE price > 80;

SELECT * FROM expensive_items_diff;


DATE - timestamp

2019. 1. 31. 09:18

총 정리

# TO_CHAR() : timestamp -> 문자열 or 영어로표현된 문자열로

  1. 현재시간  : MSSQL : GETDATE() / PostgreSQL : NOW()
  2. 날짜-> 문자열로 변환 : MSSQL : CONVERT( datatype, 시간(시간칼럼), date type number) / PostgreSQL : TO_CHAR( 시간(시간칼럼) , 'date type' )
  3. TO_CHAR(  ,  )에 들어가는 datetype에서,  YYYY=yyyy, MM=mm, DD=dd, HH=hh 대소문자 구분안하고 년/월/일/시이다.
  4. 분(minute)는 MI = mi

    --위는 TIMESTAMP 형식 : [ YYYY-MM-DD HH:MI:SS ]   

    --아래에서는 MM과 DD 대신 --> [  요일(FMDAy) - 일의 th형식   + 월의 영어표현(FMMonth) ] -----
       [ FMDay FMDDth FMMonth, YYYY HH:MI:SS]

  5. 요일은 Day = day = DAY 가 있다. 그러나 공백을 제거하기 위해서는 FM을 붙힌 FMDay = FMDAY = FMday 
  6. 일의 th 표현DDth = ddth이다.
  7. 월의 영어표현 FMMonth = FMMONTH = FMmonth 이다.


# TO_TIMESTAMP() : 문자열 -> timestamp 로 

  1. TO_TIMESTAMP( '문자열로 표현된 시간' , ' 그 문자열의 DATE TYPE 형식' )을 넣어주면, timestamp로 변환

TIMESTAMP

-- DATE
-- date는 많은 문제가 있다. 
-- 1) datetime의 형태가 너무 많다. ex> 2019-01-30  or  01-30-2019 or 01. 30. 2019
-- 2) timezone개념이 붙어있다. ex> GMT, RUSSIAN, UTC 등등 장소에 따라 current time이 달라진다.

-- >> 추천하는 방법 :  SQL에는 TIMEZONE을 입력하지 않고, python 등 어플리케이션 단에서 TIMEZONE을 적용한다.

-- PostgreSQL의 메인 date 타입 4가지 : timestamp, date, time, interval

-- 1) timestamp
--    ISO 8601 standard date 타입 in DB.  YYYY-MM-DD HH:mm:ss 
--    pgSQL의 default 형식으로 많은 문제를 예방할 수 있음.
SELECT timestamp '2019-01-30 13:30:45';



-- 2) timestamp to string
--    (1) NOW()로 현재시간을 가져온다 <--> MSSQL의 GETDATE() or DATEDIFF( 간격, datetime, GETDATE())
--    (2) TO_CHAR( timestamp, 'DD' 등 날짜포맷 ); 으로 문자열로 전환 <--> MSSQL의 CONVERT( type, datetime, style)

SELECT NOW(); --MSSQL에서는 GETDATE() 였다! 

SELECT TO_CHAR( NOW(),'DD' ); -- MSSQL의 CONVERT( type, datetime, style) or DATEDIFF( 간격, datetime, GETDATE())
SELECT TO_CHAR( NOW(),'dd' );
-- text 30

SELECT TO_CHAR( NOW(),'MM' );
SELECT TO_CHAR( NOW(),'mm' );
-- text 01 

SELECT TO_CHAR( NOW(),'YYYY' );
SELECT TO_CHAR( NOW(),'yyyy' );
-- text 2019 

SELECT TO_CHAR( NOW(), 'DD-MM-YYYY')
-- text 30-01-2019  
SELECT TO_CHAR( NOW(), 'DD-MM-YY')
-- text 30-01-19   ==> confusing

SELECT TO_CHAR( NOW(), 'DD-MM-YYYY HH')
-- text 30-01-2019  02 

SELECT TO_CHAR( NOW(), 'DD-MM-YYYY HH:MI'); -- pgSQL에서는 MM,mm month / MI :minute
-- text 30-01-2019  02:28 

SELECT TO_CHAR( NOW(), 'DD-MM-YYYY HH:MI:SS');
-- text 30-01-2019  02:28:08

SELECT TO_CHAR( NOW(), 'Day, DD-MM-YYYY HH:MI:SS');
-- text Wednesday, 30-01-2019  02:28:08
-- 만약 토요일이면, Saturday , 형식으로 빈칸이 하나 발생한다 그걸 제고 하고 싶다면 Day앞에 FM을 쓴다.

SELECT TO_CHAR( NOW(), 'FMDay, DD-MM-YYYY HH:MI:SS');
-- text Wednesday, 30-01-2019  02:28:08


SELECT TO_CHAR( NOW(), 'FMDay Month, DD-MM-YYYY HH:MI:SS');
-- text Wednesday January  , 30-01-2019  02:28:08

SELECT TO_CHAR( NOW(), 'FMDay FMMonth, DD-MM-YYYY HH:MI:SS');
-- text Wednesday January, 30-01-2019  02:28:08


--날짜를 3th 4th 형식으로도 줄 수 있다.
SELECT TO_CHAR( NOW(), 'FMDay DDth FMMonth, DD-MM-YYYY HH:MI:SS');
-- text Wednesday 30th January  , 30-01-2019  02:28:08


-- 이제 DD-MM 을 제거해서 보자.
SELECT TO_CHAR( NOW(), 'FMDay DDth FMMonth, YYYY HH:MI:SS');
-- text Wednesday 30th January, 2019 02:28:08



-- 3) 이제 TO_TIMESTAMP( '마지막에 작성한 양식의 string' , 'string과 똑같은 양식의 날짜포맷' );
--    를 통해 sting을 timestamp로 바꿀 수 있다.

SELECT TO_TIMESTAMP('Wednesday 30th January, 2019 02:28:08', 'FMDay DDth FMMonth, YYYY HH:MI:SS');

-- 4) string양식이 ISO8601 이었으면, 그 string에 맞게 양식을 적어주면, timestamp로 변한다
SELECT TO_TIMESTAMP('2019-01-30 02:28:08', 'YYYY-DD-MM HH:MI:SS');


list, set comprehension 정리

2019. 1. 30. 21:33

리스트 속 요소들을 변환한 리스트를 반환받는 법

  1. 변환될 빈 리스트 생성 -> for문 돌면서 처리 -> 각각 append
  2. list comprehension
  3. list ( map ( lambda x: , list ) )

리스트 속 요소들을 필터링한 리스트 반환받는 법

  1. list comprehenshion + if
    1) [ x if 조건1 else 조건 2 for in ]
    2) [ x for in if 조건1 ]
  2. list ( filter(lambda x: , list ) )

리스트 vs set

  1. list() :  [1, 2, 3] or []  로 생성하면 append한다
  2. set() :  {1, 2, 3} 로 생성하면 add한다.
  3. { } 로 생성은 빈 딕셔너리다 ! set이 아니다! set의 양쪽 구성만 { }

리스트 속 요소들을 변환한 set을 반환받는 방법

  1. { set comprehension for in 리스트 }


총정리

  1. HAVING은 GROUP BY 끝난 쿼리문에서 조건을 추가적으로 달 수 있다.
  2. HAVING은 1차 처리된 칼럼(COUNT()등)을 조건을 걸고 싶을 땐, alias사용이 안된다.



HAVING

-- 내장함수 & HAVING 

-- 내장함수의 종류 : 
-- COUNT(), SUM()
-- 예제 ) 고객별 구매횟수 ( 횟수, 건수 : 해당 테이블의 id를 카운트 해버리면 된다)
SELECT customers.first_name, customers.last_name, COUNT(purchases.id) FROM customers
JOIN purchases ON customers.id = purchases.customer_id
GROUP BY customers.id

-- AVG()
-- 예제 ) 아이템의 가격 평균
SELECT AVG(items.price) FROM items;

-- 예제 ) 구매된(join으로 교집합) 아이템  가격의 평균
SELECT * FROM items
INNER JOIN purchases ON items.id = purchases.item_id;

SELECT AVG(items.price) FROM items
INNER JOIN purchases ON items.id = purchases.item_id;



-- 1) 구매된 아이템 중 가격 TOP 1 을 구하는 첫번째 : JOIN 구매된 아이템  -> ORDER BY -> LIMIT
SELECT * FROM items
INNER JOIN purchases ON items.id = purchases.item_id;

SELECT items.name, items.price FROM items
INNER JOIN purchases ON items.id = purchases.item_id

SELECT items.name, items.price FROM items
INNER JOIN purchases ON items.id = purchases.item_id
ORDER BY items.price DESC
LIMIT 1;


-- 2) 구매된 아이템 가격 TOP 1을 구하는 두번째 : JOIN 구매된 아이템 -> MAX() ? 
--    : 집계테이블이 되어, 같은 테이블 내 칼럼선택(items.name)을 못함

--    만약, 다른컬럼(items.name)도 같이보려고, 집계함수 앞에 SELECT 시
--        ~별 기준이 되어버려서, 각 items.name 별 최대값들이 여러 row로 나옴.

SELECT MAX(items.price) FROM items
INNER JOIN purchases ON items.id = purchases.item_id;    -- 집계테이블로서 1개 칼럼밖에 못봄

SELECT items.name, MAX(items.price) FROM items
INNER JOIN purchases ON items.id = purchases.item_id    
GROUP BY items.name;-- 하나더보려고 select + groupby에 넣으면 기준테이블이 되어 각 아이템별로 최대값이 되어버림


-- 3) HAVING = GROUP BY + 집계함수 결과에 filter링 (집계함수의 WHERE절)
--    (1) 고객별 구매횟수를 위에서 작성했떤 것 복사해오자.
SELECT customers.first_name, customers.last_name, COUNT(purchases.id) FROM customers
JOIN purchases ON customers.id = purchases.customer_id
GROUP BY customers.id

--    (2) GROUPBY  + 집계함수의 결과에서 HAVING을 이용하여 다시 3개이상의 구매자만 걸러내보자.
--        *** 이때, 집계한칼럼의  alias로는 HAVING에서 조건을 걸 수 없다.
--        *** 1차 작업물이 SELECT 문에 있을시 -> 이후 사용은 그 문장 그대로!
SELECT customers.first_name, customers.last_name, COUNT(purchases.id) AS purchase_count FROM customers
JOIN purchases ON customers.id = purchases.customer_id
GROUP BY customers.id
HAVING purchase_count > 2;     -- alias 사용시 오류

SELECT customers.first_name, customers.last_name, COUNT(purchases.id) AS purchase_count FROM customers
JOIN purchases ON customers.id = purchases.customer_id
GROUP BY customers.id
HAVING COUNT(purchases.id) > 2;

-- 4) 다음에 같은작업(고객별 구매횟수가 가장 많은 사람 뽑기)할일이 있다면, VIEW로 만들어보자.


총 정리

DROP 정리
  1. DROP TABLE IF EXIST 테이블명
  2. 종속관계가 있는 테이블은, 참조하고 있는 테이블부터 지워야 삭제 가능하나, 종속관계를 끊고 삭제하려면 DROP TABLE 테이블명 CASCADE;


VIEW정리

  1. VIEW 생성 : CREATE VIEW 뷰명 AS  groupby복잡한 쿼리문     을 통해 updatable한 테이블을 생성할 수 있음.
  2. VIEW 조회 : SELECT * FROM 뷰명  으로 테이블조회하듯이 조회한다.
  3. VIEW 삭제 : DROP VIEW 뷰명
  4. VIEW를 조회할 때 WHERE / ORDER BY달고 조회가능 / 
  5. VIEW + WHERE 절 달아서 새로운 VIEW 생성가능

  6. VIEW생성시 AS쿼리문에 1) * 모든칼럼 선택한상태 + 2) * group by 등 집계안한 상태 + 3)WHERE문 정도 라면 : INSERT INTO VIEW 로 값 대입 가능, 특별한 옵션이 없다면 WHERE조건 만족못해도 원본테이블에 들어감
  7. WITH LOCAL CHECK OPTION으로 VIEW생성시, 해당WHERE절 조건만족못하는 것은 INSERT INTO VIEW로 대입 불가능
  8. view1를 이용해 view2를 만든 상태에서
    (1) view1에 local옵션이 없고 & view2 local옵션만 있다면 ==> view2옵션만 체크한다.
    (2) view1에 local옵션이 있고 & view2 local옵션있다면     ==> view1, view2 둘다 체크한다. ( 각 view가 local있다면, 다 체크 )
    (3) view1에 local옵션이 없고 & view2 CASCADED 옵션이 있다면 ==> view1, view2 둘다체크한다. (마지막 view가 cascaded옵션있다면, 다 체크)


DROP TABLE 옵션 :  CASCADE vs CASCADED : VIEW를 포함한 VIEW에서 2개 모두 WHERE절 조건 체크 옵션



DROP

-- DROP TABLE

-- 1) videos테이블에서 users를 참조하고 있는 상황이므로 바로 삭제가 안된다.
DROP TABLE public.users;

-- 2) 강제로 삭제하는 방법은 뒤에 CASCADE를 붙혀 -> 다른테이블과의 관계를 끊는 것이다.
DROP TABLE public.users CASCADE;

-- 3) 관계는 끊어져도 기존에 연결된 값들은 유지되어있다.
--    videos 테이블의 정보를 보면, CONSTRAINT fkey정보가 업어져있다.
SELECT * FROM videos;


-- 4) 만약, videos테이블을 먼저 DROP한다면, uses테이블도 바로 DROP 될 것이다.
DROP TABLE public.videos;

-- 5) 없는 테이블을 삭제하는 오류를 줄이고 싶다면 DROP TABLE <--> 테이블명 사이에 IF EXISTS 를 준다.
--     테이블을 생성하기 전에 적어줘도 좋은 팁이 된다.
DROP TABLE IF EXISTS public.videos;

-- 6) DROP TABLE/DATABASE/SEQUENCE/VIEW 등이 다 DROP이 가능하다.

VIEW

-- VIEW & INSERT VIEW without groupby & WITH LOCAL/CASCADE CHECK OPTION

-- 1) view없이 고객들이 소비한 전체 돈 보기 by GROUP BY 
--     customers별 목록이 1 row로 나타나도록 id로 groupby + 기준테이블 칼럼들은 막 select +  집계테이블은 칼럼없이 sum()으로 1 row로 집계했다.
SELECT customers.first_name, customers.last_name, SUM(items.price) FROM customers
INNER JOIN purchases ON customers.id = purchases.customer_id
INNER JOIN items ON purchases.item_id = items.id
GROUP BY customers.id

-- 2) 매번 groupby를 할게 아니라, VIEW를 이용해 해당 결과를 저장하자.
--    CREATE VIEW + view이름 + AS + 저장할 쿼리문 형태이다.
CREATE VIEW total_revenue_per_customer AS
SELECT customers.first_name, customers.last_name, SUM(items.price) FROM customers
INNER JOIN purchases ON customers.id = purchases.customer_id
INNER JOIN items ON purchases.item_id = items.id
GROUP BY customers.id

-- 3) 마치 테이블처럼 SELECT할 수 있다.
SELECT * FROM total_revenue_per_customer

-- 4) DROP VIEW view이름으로 삭제할 수 있다.
DROP VIEW total_revenue_per_customer

-- 5) 다시 view를 만드는데, groupby 의 기준이된 customers.id도 같이 SELECT한 view를 만들자
--    새로운 테이블로 보는데, id칼럼이 없기 때문...?
--    이 아니라,, 새로운 구매정보를 늘릴 때 view는 어떻게 변하는지 보기 위해서다.
CREATE VIEW total_revenue_per_customer AS
SELECT customers.id, customers.first_name, customers.last_name, SUM(items.price) FROM customers
INNER JOIN purchases ON customers.id = purchases.customer_id
INNER JOIN items ON purchases.item_id = items.id
GROUP BY customers.id

SELECT * FROM total_revenue_per_customer;

-- 6) VIEW의 결과에 없는 customers 중에 한명이 item을 구매하도록 해보자.
--    구매정보를 늘이려면 purchases테이블에 추가해야한다.
SELECT * FROM total_revenue_per_customer;    --1,3,4,5는 구매했었음.
SELECT * FROM customers;            --남은건 2번 고객
SELECT * FROM items;                --아이템은 아무거나.. 여기서 6번 아이템


SELECT * FROM purchases;             -- 구매번호는 10번까지 차있구나.

INSERT INTO purchases                -- 구매번호 11, item_id 6번, customer_id 2번
VALUES (11, 6, 2);


-- 7) 기존에 저장한 VIEW를 다시 보자.
--    customers.id 2번이 추가되었다! / 만약 기존고객이 구매정보를 늘렸으면, sum이 증가할 것이다!
--    VIEW는 결과가 아니라 쿼리문 자체를 저장해서 updatable한 테이블을 생성해주는 것
SELECT * FROM total_revenue_per_customer;


-- 8) VIEW로 저장한 쿼리문에는 WHERE문도 달 수 있다.
SELECT * FROM total_revenue_per_customer WHERE sum > 150;

-- 9) VIEW에 WHERE문을 단 것 역시, VIEW로 만들 수 있다.
CREATE VIEW awesome_customer AS
SELECT * FROM total_revenue_per_customer WHERE sum > 150;

SELECT * FROM awesome_customer;

-- 10) VIEW를 table로 취급하여, ORDER BY 한 것도 가능하다.
SELECT * FROM total_revenue_per_customer WHERE sum > 100
ORDER BY sum DESC;



-- INSERT INTO VIEW를 통해 VIEW에 값 대입하기는 group by 절이 포함된 VIEW는 사용하지 못한다.
-- 위에서 만든  total_revenue_per_customer 같은 것들은 INSERT INTO에 view를 사용하지 못한다.

-- 1) group by가 없는 VIEW를 만들자.
CREATE VIEW expensive_items AS
SELECT * FROM items WHERE price > 100;

SELECT * FROM expensive_items

-- 2) 여기에 row를 대입해보자. ( 만약, SEQUENCE를 사용한 테이블이라면 해당값 입력x)
--    VIEW를 만든 테이블(items)에 값이 자동으로 삽입된다!
--     만약 일부칼럼만 가져온 VIEW라면?;; 
INSERT INTO expensive_items(id, name, price)
VALUES (9, 'DSLR',400.00);

SELECT * FROM expensive_items
SELECT * FROM items;            -- items테이블에도 추가되어있다.

-- 3) 만약, VIEW에 포함된 WHERE 조건절을 만족하지 않는 값을 추가한다면?
--     VIEW의 WHERE에는 100달러 이상 아이템만 가져오도록했는데, 5달러짜리를 추가해보자.
INSERT INTO expensive_items(id, name, price)
VALUES (10, 'DSLR', 5.00);

SELECT * FROM expensive_items;    --결과적으로, items테이블에는 추가되지만, VIEW에서는 안보이게 된다. 왜냐 살아있는 쿼리문이 저장되어있는 테이블이므로
SELECT * FROM items;    

-- 4) 그렇다면, INSERT INTO VIEW 를 이용해서, VIEW에 달린 WHERE 조건을 만족하는 값만 입력되도록 하려면?
--    PostgreSQL에만 있는 코드인 WITH LOCAL CHECK OPTION을 VIEW에 추가해줘야한다.
--    WITH LOCAL CHECK OPTION은 데이터를 insert나 update시 where조건절을 항상 체크한다.
DROP VIEW IF EXISTS expensive_items;

CREATE VIEW expensive_items AS
SELECT * FROM items WHERE price > 100
WITH LOCAL CHECK OPTION ;

INSERT INTO expensive_items(id, name, price)
VALUES (11, 'low camera', 5.00);        -- view의 조건을 위반했다고 뜬다.

INSERT INTO expensive_items(id, name, price)
VALUES (11, 'low camera', 101.00);

-- 5) 이번에는 WITH LOCAL CHECK OPTION 없이 만든 expensive_view(100달러이상)에 
--    FROM expensive_view  + WITH LOCAL CHECK OPTION 으로
--    10000달러는 이하인 non_luxury_items VIEW를 
DROP VIEW IF EXISTS expensive_items;

CREATE VIEW expensive_items AS
SELECT * FROM items WHERE price >= 100;

CREATE VIEW non_luxury_items AS
SELECT * FROM expensive_items WHERE price < 10000
WITH LOCAL CHECK OPTION;

-- 6) non_luxury_items VIEW 에다가 2달러짜리를 INSERT INTO 를 해보자.
--    LOCAL CHECK OPTION은 현재하고 있는 WHERE에 대해서만 체크를 한다고한다.

INSERT INTO non_luxury_items(id, name, price)
VALUES (12, 'Pencil', 2.00);
--    결과적으로 2달러도 입력이 된다.
--    직접적으로 달리지 않은 expensive_items에 대한 체크를 하지 않는다.
--    즉, non_luxury_items에 대한 check만 시행한다.


-- 7) LOCAL 이 아니라 종속관계까지 다 체크해주는 CASCADED CHECK 를 맨 마지막 VIEW에 달면,
--    내부의 VIEW(테이블)의 WHERE절까지 체크해준다.
DROP VIEW IF EXISTS non_luxury_items;

CREATE VIEW non_luxury_items AS
SELECT * FROM expensive_items WHERE price < 10000
WITH CASCADED CHECK OPTION;

INSERT INTO non_luxury_items(id, name, price)
VALUES (13, 'PencilTwo', 90.00); -- 내부에 있는 VIEW인 expensive_item에 대한 WHERE절에 위반되고 있다고 경고창이 뜬다.


+ Recent posts