scrapy

1. Scrapy 개요

Scrapy는 크롤링/스크레이핑을 위한 파이썬 프레임워크로서 풍부한 기능들이 존재합니다.

  • 웹 페이지에서 링크 추출하기
  • robots.txt를 기반으로 허가된 페이지와 금지된 페이지 구분하기
  • XML 사이트맵 추출과 링크 추출하기
  • 도메인과 IP 주소마다 크롤링 시간 간격 조정하기
  • 여러 개의 크롤링 대상을 병렬 처리하기
  • 중복된 URL 크롤링하지 않기
  • 오류가 발생했을 때 특정 횟수만큼 재시도하기
  • 크롤러를 데몬으로 만들기와 잡 관리하기


2. Scrapy 설치 (window 환경)

Scrapy는 1.1 버전부터 파이썬 3을 지원하고 있으며, 여러 파이썬 패키지들을 기반으로 만들어졌습니다.

  • lxml : libxml2와 libxslt를 사용한 C 확장 라이브러리로서 효율적인 XML과 HTML 파서 역할을 수행
  • twisted : 이벤트 구동(Event Drive) 네트워크 프로그래밍 엔진을 기반으로 만들어졌기 떄문에 웹사이트 다운로드 처리를 비동기적으로 실행하며 다운로드 중에도 스크레이핑 처리 등을 할 수 있습니다.

공식 문서에 따르면 Scrapy를 설치 시 pip말고, Anaconda 또는 miniconda를 설치하여 conda-forge 채널의 패키지를 활용하는 것이 많은 설치 이슈를 피할 수 있다고 추천하고 있습니다. (Scrapy install guide)

conda install -c conda-forge scrapy

약 2~3분 정도의 시간이 소요되며 설치가 완료된 후에 scrapy --version 해당 명령어를 실행시키면 아래와 같은 결과를 보실 수 있습니다.

C:\Users>scrapy --version
Scrapy 1.5.1 - no active project

Usage:
  scrapy <command> [options] [args]

Available commands:
  bench         Run quick benchmark test
  fetch         Fetch a URL using the Scrapy downloader
  genspider     Generate new spider using pre-defined templates
  runspider     Run a self-contained spider (without creating a project)
  settings      Get settings values
  shell         Interactive scraping console
  startproject  Create new project
  version       Print Scrapy version
  view          Open URL in browser, as seen by Scrapy

  [ more ]      More commands available when run from project directory

Use "scrapy <command> -h" to see more info about a command


3. 공식 예제

1.5 버전의 공식 예제는 http://quotes.toscrape.com 사이트의 링크를 순회하며 text와 authon를 스크레이핑하는 코드입니다.

quotes_spider.py

import scrapy

class QuotesSpider(scrapy.Spider):
    # spider의 이름(변경가능)
    name = "quotes"
    # 크롤링을 시작할 URL 리스트
    start_urls = [
        'http://quotes.toscrape.com/tag/humor/',
    ]

    def parse(self, response):
        '''
        링크를 순회하며 div.quote부분의 text와 author를 추출
        '''
        for quote in response.css('div.quote'):
            yield {
                'text': quote.css('span.text::text').extract_first(),
                'author': quote.xpath('span/small/text()').extract_first(),
            }

        '''
        최상위 페이지의 모든 링크를 추출
        '''    
        next_page = response.css('li.next a::attr("href")').extract_first()
        if next_page is not None:
            yield response.follow(next_page, self.parse)

scrapy의 runspider 명령어의 파라미터로 [실행 파일 경로, 출력 형태]를 지정하여 실행하면 로그와 함께 크롤링이 완료된 것을 볼 수 있습니다.

scrapy runspider quotes_spider.py -o quotes.json


실행 결과

C:\workspace\python\scrapy>scrapy runspider quotes_spider.py -o quotes.json
2018-07-22 12:23:17 [scrapy.utils.log] INFO: Scrapy 1.5.1 started (bot: scrapybot)
2018-07-22 12:23:17 [scrapy.utils.log] INFO: Versions: lxml 3.6.4.0, libxml2 2.9.4, cssselect 1.0.3, parsel 1.4.0, w3lib 1.19.0, Twisted 17.5.0, Python 3.5.2 |Anaconda custom (64-bit)| (default, Jul  5 2016, 11:41:13) [MSC v.1900 64 bit (AMD64)], pyOpenSSL 16.2.0 (OpenSSL 1.0.2o  27 Mar 2018), cryptography 1.5, Platform Windows-10-10.0.17134-SP0
2018-07-22 12:23:17 [scrapy.crawler] INFO: Overridden settings: {'FEED_FORMAT': 'json', 'SPIDER_LOADER_WARN_ONLY': True, 'FEED_URI': 'quotes.json'}
2018-07-22 12:23:17 [scrapy.middleware] INFO: Enabled extensions:
...
2018-07-22 12:23:19 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://quotes.toscrape.com/tag/humor/> (referer: None)
2018-07-22 12:23:20 [scrapy.core.scraper] DEBUG: Scraped from <200 http://quotes.toscrape.com/tag/humor/>
{'author': 'Jane Austen', 'text': '“The person, be it gentleman or lady, who has not pleasure in a good novel, must be intolerably stupid.”'}
...
'finish_time': datetime.datetime(2018, 7, 22, 3, 23, 20, 646076),
 'start_time': datetime.datetime(2018, 7, 22, 3, 23, 18, 724216)}
2018-07-22 12:23:20 [scrapy.core.engine] INFO: Spider closed (finished)


출력 결과 확인 

C:\workspace\python\scrapy>type quotes.json
[
{"text": "\u201cThe person, be it gentleman or lady, who has not pleasure in a good novel, must be intolerably stupid.\u201d", "author": "Jane Austen"},
...
{"text": "\u201cA lady's imagination is very rapid; it jumps from admiration to love, from love to matrimony in a moment.\u201d", "author": "Jane Austen"}
][
{"author": "Jane Austen", "text": "\u201cThe person, be it gentleman or lady, who has not pleasure in a good novel, must be intolerably stupid.\u201d"},
...
{"author": "Jane Austen", "text": "\u201cA lady's imagination is very rapid; it jumps from admiration to love, from love to matrimony in a moment.\u201d"}
]



Reference

  • Scrapy 1.5 documentation
  • 카토 코다, 『파이썬을 이용한 웹 크롤링과 스크레이핑』, 윤인성, 위키북스(2018-03-22), p267~270.


write

[Fastcampus] RDC 강의 내용 정리 - 이부일 강사님

상관분석(Correlation Analysis)

When?
두 양적 자료 간에 관련성(직선의 관계 = 선형의 관계)이 있는지를 통계적으로 검정하는 방법

예제 데이터 : cars(speed, dist), attitude


1. 산점도(Scatter Plot)

(1) 기본

plot(x - data$variable, y - data$variable)
plot(cars$speed, cars$dist)

# 한 화면에 여러개 plot 출력
par(mfrow = c(2, 3))
for(i in colnames(attitude)[2:7]){
    plot(attitude[ , i], attitude$rating,
         main = paste("rating vs ", i),
         xlab = i,
         ylab = "rating",
         col = "blue",
         pch = 12)
}
par(mfrow = c(1, 1))


(2) 산점행렬도(SMP : Scatter Matrix Plot)

plot(iris[ , 1:4])


(3) 3D 산점도 : rgl, car package

with(iris,
     plot3d(Sepal.Length,
            Sepal.Width,
            Petal.Length,
            type="s",
            col=as.numeric(Species)))


scatter3d(x = iris$Sepal.Length,
          y = iris$Petal.Length,
          z = iris$Sepal.Width,
          groups = iris$Species,
          surface=FALSE,
          grid = FALSE,
          ellipsoid = TRUE,
          axis.col = c("black", "black", "black"))

(4) corrplot package

corrplot::corrplot(cor(iris[ , 1:4]), method = "circle")




2. 상관계수(Coefficient of Correlation)

두 양적 자료의 관련성(직선의 관계 = 선형의 관계) 정도를 수치로 알려줌
cor(datavariable,datavariable, datavariable, method = c("pearson", "spearman", "kendall"))

> cor(cars$speed, cars$dist, method = "pearson")
[1] 0.8068949

> cor(attitude, method = "pearson")
              rating complaints privileges  learning    raises  critical   advance
rating     1.0000000  0.8254176  0.4261169 0.6236782 0.5901390 0.1564392 0.1550863
complaints 0.8254176  1.0000000  0.5582882 0.5967358 0.6691975 0.1877143 0.2245796
privileges 0.4261169  0.5582882  1.0000000 0.4933310 0.4454779 0.1472331 0.3432934
learning   0.6236782  0.5967358  0.4933310 1.0000000 0.6403144 0.1159652 0.5316198
raises     0.5901390  0.6691975  0.4454779 0.6403144 1.0000000 0.3768830 0.5741862
critical   0.1564392  0.1877143  0.1472331 0.1159652 0.3768830 1.0000000 0.2833432
advance    0.1550863  0.2245796  0.3432934 0.5316198 0.5741862 0.2833432 1.0000000

> round(cor(attitude, method = "pearson") , digits = 3)
           rating complaints privileges learning raises critical advance
rating      1.000      0.825      0.426    0.624  0.590    0.156   0.155
complaints  0.825      1.000      0.558    0.597  0.669    0.188   0.225
privileges  0.426      0.558      1.000    0.493  0.445    0.147   0.343
learning    0.624      0.597      0.493    1.000  0.640    0.116   0.532
raises      0.590      0.669      0.445    0.640  1.000    0.377   0.574
critical    0.156      0.188      0.147    0.116  0.377    1.000   0.283
advance     0.155      0.225      0.343    0.532  0.574    0.283   1.000

> round(cor(iris[ , 1:4], method = "pearson") , digits = 3)
             Sepal.Length Sepal.Width Petal.Length Petal.Width
Sepal.Length        1.000      -0.118        0.872       0.818
Sepal.Width        -0.118       1.000       -0.428      -0.366
Petal.Length        0.872      -0.428        1.000       0.963
Petal.Width         0.818      -0.366        0.963       1.000```


3. 상관분석

  • 귀무가설 : speed와 dist 간에는 관련성이 없다.
  • 대립가설 : speed와 dist 간에는 관련성이 있다.
    cor.test(datavariable,datavariable, datavariable, method = "pearson")
> cor.test(cars$speed, cars$dist, method = "pearson")

	Pearson's product-moment correlation

data:  cars$speed and cars$dist
t = 9.464, df = 48, p-value = 1.49e-12
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.6816422 0.8862036
sample estimates:
      cor
0.8068949

유의확률이 0.000이므로 유의수준 0.05에서 speed와 dist 간에는 통계적으로 유의한 양의 상관관계가 있는 것으로 나타났다.
즉, speed가 증가하면 dist도 증가하는 경향을 보인다.



  • 귀무가설 : rating과 complaints 간에는 관련성이 없다.
  • 대립가설 : rating과 complaints 간에는 관련성이 있다.
> cor.test(attitude$complaints, attitude$rating, method = "pearson")

	Pearson's product-moment correlation

data:  attitude$complaints and attitude$rating
t = 7.737, df = 28, p-value = 1.988e-08
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.6620128 0.9139139
sample estimates:
      cor
0.8254176

유의확률이 0.000이므로 유의수준 0.05에서 complaints와 rating 간에는 통계적으로 유의한 매우 높은 양의 상관관계가 있는 것으로 나타났다.



Quiz.

rating과 나머지 6개 변수 간의 관련성 검정을 해 보세요.

# for문 활용
for(i in colnames(attitude)[2:7]){
    print(cor.test(attitude[ , i], attitude$rating, method = "pearson"))
}

# corr.test 패키지 활용
> psych::corr.test(attitude, method = "pearson")

Call:psych::corr.test(x = attitude, method = "pearson")
Correlation matrix
           rating complaints privileges learning raises critical advance
rating       1.00       0.83       0.43     0.62   0.59     0.16    0.16
complaints   0.83       1.00       0.56     0.60   0.67     0.19    0.22
privileges   0.43       0.56       1.00     0.49   0.45     0.15    0.34
learning     0.62       0.60       0.49     1.00   0.64     0.12    0.53
raises       0.59       0.67       0.45     0.64   1.00     0.38    0.57
critical     0.16       0.19       0.15     0.12   0.38     1.00    0.28
advance      0.16       0.22       0.34     0.53   0.57     0.28    1.00
Sample Size
[1] 30
Probability values (Entries above the diagonal are adjusted for multiple tests.)
           rating complaints privileges learning raises critical advance
rating       0.00       0.00       0.19     0.00   0.01     1.00    1.00
complaints   0.00       0.00       0.02     0.01   0.00     1.00    1.00
privileges   0.02       0.00       0.00     0.07   0.15     1.00    0.51
learning     0.00       0.00       0.01     0.00   0.00     1.00    0.03
raises       0.00       0.00       0.01     0.00   0.00     0.36    0.01
critical     0.41       0.32       0.44     0.54   0.04     0.00    0.90
advance      0.41       0.23       0.06     0.00   0.00     0.13    0.00

 To see confidence intervals of the correlations, print with the short=FALSE option


Quiz.

housing data : price와 관련성이 있는 상위 6개의 변수명, 상관계수, t값, p-value를 출력하시오.

houseDF <- readxl::read_excel(path      = "kc_house_data.xlsx",
                              sheet     = 1,
                              col_names = TRUE)
View(houseDF)
houseDF$year <- 2018 - houseDF$yr_built
remove.variables <- c("id", "date", "floors", "waterfront", "view",
                      "condition", "yr_built", "yr_renovated",
                      "zipcode", "lat", "long")

houseDF2 <- houseDF %>%
    select(-one_of(remove.variables))
    #psych::corr.test()

corr.result <- psych::corr.test(houseDF2)
str(corr.result)
corr.result$r[ , 1]
str(corr.result$r)
top6.r <- round(sort(corr.result$r[, 1], decreasing = TRUE)[2:7], digits = 3)
top6.t <- round(sort(corr.result$t[, 1], decreasing = TRUE)[2:7], digits = 3)
top6.pvalue <- round(sort(corr.result$p[, 1], decreasing = TRUE)[2:7], digits = 3)
top6.variables <- names(round(sort(corr.result$r[, 1], decreasing = TRUE)[2:7], digits = 3))
plot(houseDF2[ , c(top6.variables, "price")])

corrDF <- data.frame(Variables = top6.variables,
                     r = top6.r,
                     t = top6.t,
                     pvalue = top6.pvalue)
writexl::write_xlsx(corrDF, path = "correlationResult.xlsx")


01Basic

[Fastcampus] RDC 강의 내용 정리 - 이부일 강사님

분산분석(ANOVA : Analysis of Variance)

When?
세 개 이상의 집단 간에 양적 자료에 차이가 있는지를 통계적으로 검정하는 방법

Use library & data

require("readxl")
require("nortest")
require("nparcomp")
require("PMCMR")
require("PMCMRplus")
require("writexl")

houseDF <- readxl::read_excel(path      = "kc_house_data.xlsx",
                              sheet     = 1,
                              col_names = TRUE)
View(houseDF)
str(houseDF)
table(houseDF$condition)
houseDF$condition <- as.factor(houseDF$condition)


1. 질적 자료 1개, 양적 자료 1개

질적 자료는 3개 이상의 유한 집단으로 구성되어 있어야 함.

  • 귀무가설 : condition에 따라 price에 차이가 없다.
  • 대립가설 : condition에 따라 price에 차이가 있다.

1단계 : 정규성 검정

  • 귀무가설 : 정규분포를 따른다.
  • 대립가설 : 정규분포를 따르지 않는다.
by(houseDF$price, houseDF$condition, ad.test)

2단계 : 정규성 가정이 만족이 되었다면

분산분석 : ANOVA
분산분석 결과 <- aov(양적자료 ~ 질적자료, data = dataname)

anova.result <- aov(price ~ condition, data = houseDF)
summary(anova.result)

<결과>
               Df           Sum Sq       Mean Sq F value       Pr(>F)    
condition       1    3851399435634 3851399435634   28.61 0.0000000894 ***
Residuals   21611 2909065362485666  134610400374                         
---
Signif. codes:  0***0.001**0.01*0.05 ‘.’ 0.1 ‘ ’ 1

유의확률이 0.000이므로 유의수준 0.05에서 condition에 따라 price에 통계적으로 유의한 차이가 있는 것으로 나타났다.


3단계 : 2단계의 결론이 대립가설이면

다중비교(Multiple Comparison) = 사후분석(Post-Adhoc)
Duncan, Tukey, Scheffee, Bonferroni, Dunnett

TukeyHSD(anova.result)

2단계 : 정규성 가정을 만족하지 않으면

Kruskal - Wallis Test
kruskal.test(양적자료 ~ 질적자료, data = dataname)

> kruskal.test(price ~ condition, data = houseDF)

	Kruskal-Wallis rank sum test

data:  price by condition
Kruskal-Wallis chi-squared = 260.85, df = 4, p-value < 2.2e-16

유의확률이 0.000이므로 유의수준 0.05에서 condition에 따라 price에 통계적으로 유의한 차이가 있는 것으로 나타났다.


3단계 : Kruskal-Wallis Test의 결론이 대립가설이면

다중비교 = 사후분석을 실시함
nparcomp::nparcomp(양적자료 ~ 질적자료, data = dataname)

> PMCMR::posthoc.kruskal.nemenyi.test(price ~ condition, data = houseDF,
+                                     dist="Tukey")

	Pairwise comparisons using Tukey and Kramer (Nemenyi) test
                   with Tukey-Dist approximation for independent samples

data:  price by condition

  1                 2                 3                 4                
2 0.99908           -                 -                 -                
3 0.000025063186814 < 2e-16           -                 -                
4 0.00023           0.000000000000046 0.000000544539610 -                
5 0.000000126256704 < 2e-16           0.000000000002318 0.000000000000046

P value adjustment method: none


Quiz

condition에 따라 price, bedrooms, bathrooms, sqft_living, sqft_lot, sqft_above, sqft_basement, year(2018 - yr_built)에 통계적으로 유의한 차이가 있는지를 검정하시오.

Variable Normaility Method F/Chisqaure pvalue
price No Krukal-Wallis 260.850 0.000
bathrooms Yes ANOVA 100.000 0.012
houseDF$year <- 2018 - houseDF$yr_built
analysis.variable <- c("price", "bedrooms", "bathrooms",
                       "sqft_living", "sqft_lot", "sqft_above",
                       "sqft_basement", "year")
Normality  <- c()
Method     <- c()
FChiSquare <- c()
PValue     <- c()
for(i in analysis.variable){
    normality.result <- by(unlist(houseDF[ , i]), houseDF$condition, ad.test)
    if(normality.result$`1`$p.value < 0.05 |
       normality.result$`2`$p.value < 0.05 |
       normality.result$`3`$p.value < 0.05 |
       normality.result$`4`$p.value < 0.05 |
       normality.result$`5`$p.value < 0.05){
        kruskal.result <- kruskal.test(unlist(houseDF[ , i]) ~ houseDF$condition)
        Normality  <- c(Normality, "No")
        Method     <- c(Method, "kruskal-Wallis")
        FChiSquare <- c(FChiSquare, kruskal.result$statistic)
        PValue     <- c(PValue, kruskal.result$p.value)
    }else{
        aov.result <- aov(unlist(houseDF[ , i]) ~ houseDF$condition)
        aov.result <- summary(aov.result)
        Normality  <- c(Normality, "Yes")
        Method     <- c(Method, "ANOVA")
        FChiSquare <- c(FChiSquare, unlist(aov.result)[7])
        PValue     <- c(PValue, unlist(aov.result)[9])
    }
}

anovaDF <- data.frame(Variables = analysis.variable,
                      Normality,
                      Method,
                      FChiSquare,
                      PValue)
writexl::write_xlsx(anovaDF, path = "anovaResult.xlsx")
01Basic

Git의 기초

출처 : Scott Chacon, Ben Straub, 『Pro Git 2/E』, 박창우, 이성환, 최용재 옮김, 인사이트 출판사(1800), p22~34.


1. 깃 저장소 만들기

  1. 기존 프로젝트나 디텍터리를 Git 저장소로 만드는 방법
  2. 다른 서버에 있는 저장소를 Clone하는 방법

기존 프로젝트를 Git으로 관리하고 싶을때?

$ git init

명령을 실행하여 .git 이라는 하위 디렉터리 생성
저장소에 필요한 뼈대 파일(sekeleton)이 들어 있음
Git이 파일을 관리하게 하려면 저장소에 파일을 추가하고 커밋해야 한다.

git add *.c             # 파일을 추가하고
git add LICENSE  
git commit -m 'message' # 커밋 

기존 저장소를 Clone하기

git clone [url] [dir_name]

다른 버전관리 툴과 차이점은 서버에 있는 거의 모든 데이터를 복사한다는 점





2. 수정하고 저장소에 저장하기

워킹 디렉터리의 모든 파일은 크게 Tracked(관리대상임)와 Untracked(관리대상이 아님)로 나눈다.

Tracked

이미 스냅샷에 포함돼 있던 파일로서 3가지 상태 중 하나이다.

  • Unmodified(수정하지 않음)
  • Modified(수정함)
  • Staged(커밋으로 저장소에 기록할)

나머지 파일은 모두 Untracked 파일이다.

Untracked

워킹 디렉터리에 있는 파일 중 스냅샷에도 Staging Area에도 포함되지 않은 파일

처음 저장소에 Clone하면 모든 파일은 Tracked이면서 Unmodified상태이다. 파일을 Checkout하고 나서 아무것도 수정하지 않았기 때문에 그렇다.

마지막 커밋 이후 아직 아무것도 수정하지 않은 상태에서 어떤 파일을 수정하면 Git은 그 파일을 Modified 상태로 인식한다. 실제로 커밋을 하기 위해서는 이 수정한 파일을 Staged 상태로 만들고, Staged 상태의 파일을 커밋한다.

<사진 공간 - P19 파일의 라이프사이클>

파일의 상태 확인하기

$ git status
> On branch master
> nothing to commit, working directory clean

파일을 하나도 수정하지 않았다는 의미 => Tracked나 modified 상태인 파일이 없다는 의미
기본 브랜치가 master이기 때문에 현재 브랜치 이름이 'master'로 나온다.

README 파일 만들기

$ echo 'My Project' > README
$ git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)
 
  README
 
nothing added to commit but untracked files present (use "git add" to track)

README 파일은 Untracked 상태
=> Git은 untracked 파일을 아직 스냅샷(커밋)에 넣어지지 않은 파일이라고 본다.
Git 파일이 Tracked 상태가 되기 전까지는 해당 파일을 커밋하지 않는다.

파일을 새로 추적하기

$ git add README
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
 
  new file: README

Modified 상태의 파일을 Stage 하기

이미 Tracked 상태인 파일을 수정하는 법
"CONTRIBUTING.md"라는 파일을 수정하고 나서 git status 명령을 다시 실행

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
 
  new file: README
 
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
 
  modified: CONTRIBUTING.md

파일 상태를 짤막하게 확인하기

$ git status -s 
또는
$ git status --short
  • ?? : 아직 추적하지 않는 새 파일
  • A : Staged 상태로 추가한 파일 중 새로 생성한 파일
  • M : 수정한 파일
  • MM : 변경하고 Staged 상태로 추가한 후 또 내용을 변경한 경우


01Basic

[Fastcampus] RDC 강의 내용 정리 - 이부일 강사님

Two Sample t-test

When?
두 개의 독립적인 집단의 평균이 같은지 다른지를 달라졌는지를 통계적으로 검정하는 방법
질적 자료(1개) : 두 집단
양적 자료(1개) :


  • 귀무가설 : 비졸업과 졸업 간에 용돈에 차이가 없다(mu1 = mu2).
  • 대립가설 : 비졸업과 졸업 간에 용돈에 차이가 있다(mu1 is not equal to mu2).

1단계 : 정규성 검정(Normality Test)

by(twosampleDF$money, twosampleDF$group, shapiro.test)

<결과>
twosampleDF$group: 비

	Shapiro-Wilk normality test

data:  dd[x, ]
W = 0.83701, p-value = 0.02885

----------------------------------------------------------------------------------
twosampleDF$group: 졸

	Shapiro-Wilk normality test

data:  dd[x, ]
W = 0.57538, p-value = 6.737e-05

두 집단 모두 정규성 가정이 깨짐 => 2단계로 Wilcoxon's rank sum test를 실시


2단계 : 정규성 가정이 만족이 되면

등분산성 검정(Equality of Variance Test)

  • 귀무가설 : 등분산이다.
  • 대립가설 : 이분산이다.

var.test(datavariable datavariable ~ datavariable)
var.test(양적 자료 ~ 질적 자료)

> var.test(twosampleDF$money ~ twosampleDF$group)

	F test to compare two variances

data:  twosampleDF$money by twosampleDF$group
F = 0.22298, num df = 10, denom df = 11, p-value = 0.02499
alternative hypothesis: true ratio of variances is not equal to 1
95 percent confidence interval:
 0.06324512 0.81720812
sample estimates:
ratio of variances
         0.2229815

> by(twosampleDF$money, twosampleDF$group, var)
twosampleDF$group:[1] 1280.455
----------------------------------------------------------------------------------
twosampleDF$group:[1] 5742.424

결론 : 유의확률이 0.025이므로 유의수준 0.05에서 이분산이다.


3단계 : 이분산이 가정된 독립 2표본 t검정

t.test(data$variable ~ data$vairable,alternative = c("greater", "less", "two.sided"), var.equal = FALSE)

> t.test(twosampleDF$money ~ twosampleDF$group,
         alternative = "two.sided",
         var.equal   = FALSE)

<결과>
Welch Two Sample t-test

data:  twosampleDF$money by twosampleDF$group
t = -0.21741, df = 15.963, p-value = 0.8306
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -57.02012  46.41406
sample estimates:
mean in group 비 mean in group 졸
        76.36364         81.66667

유의확률이 0.831이므로 유의수준 0.05에서 비졸업자과 졸업자의 용돈에는 통계적으로 유의한 차이는 없는 것으로 나타났다.


3단계 : 등분산이 가정된 독립 2표본 t검정

t.test(data$variable ~ data$vairable, alternative = c("greater", "less", "two.sided"), var.equal = TRUE)

> t.test(twosampleDF$money ~ twosampleDF$group,
         alternative = "two.sided",
         var.equal   = TRUE)

	Two Sample t-test

data:  twosampleDF$money by twosampleDF$group
t = -0.21122, df = 21, p-value = 0.8348
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -57.51554  46.90948
sample estimates:
mean in group 비 mean in group 졸
        76.36364         81.66667

유의확률이 0.835이므로 유의수준 0.05에서 비졸업자과 졸업자의 용돈에는 통계적으로 유의한 차이는 없는 것으로 나타났다.


2단계 : 윌콕슨의 순위합 검정(Wilcoxon's rank sum test)

wilcox.test(data$variable ~ data$variable, alternative = c("two.sided", "greater", "less"))

> wilcox.test(twosampleDF$money ~ twosampleDF$group,
              alternative = "two.sided")

	Wilcoxon rank sum test with continuity correction

data:  twosampleDF$money by twosampleDF$group
W = 78, p-value = 0.4594
alternative hypothesis: true location shift is not equal to 0

유의확률이 0.459이므로 유의수준 0.05에서 비졸업자과 졸업자의 용돈에는 통계적으로 유의한 차이는 없는 것으로 나타났다.



Quiz 1.

  • yr_built : 1900이상 ~ 2000미만 : group = "old"
  • yr_built : 2000이상 : group = "new"
  • 귀무가설 : old와 new 간에 price에 차이가 없다.
  • 대립가설 : old가 new보다 price가 작다.
# 사용 데이터
houseDF <- readxl::read_excel(path      = "kc_house_data.xlsx",
                              sheet     = 1,
                              col_names = TRUE)

houseDF$group <-  cut(houseDF$yr_built,
                      breaks = c(1900, 2000, 2020),
                      right  = FALSE)
levels(houseDF$group) <- c("old", "new")
by(houseDF$price, houseDF$group, ad.test)
wilcox.test(houseDF$price ~ houseDF$group,
            alternative = "less")

result <- var.test(houseDF$price ~ houseDF$group)
result$p.value


Quiz 02.

id, date, yr_built를 제외한 모든 변수에 대해서 아래 가설검정을 실시

  • 귀무가설 : old와 new는 같다.
  • 대립가설 : new와 old는 같지 않다.

최종 결과 형태

variableName Normaility Method Equality TW pvalue
price yes t.test yes 1.234 0.123
bedrooms no wilcox.test non 1.234 0.123
houseDF <- read_excel(path      = "path/kc_house_data.xlsx",
                      sheet     = 1,
                      col_names = TRUE)
houseDF <- data.frame(houseDF)
# 분석에 사용하지 않는 변수 제거
# 1) dplyr (전처리에 많이 활용되는 라이브러리입니다.)
# select(변수 벡터)          => 지정벡터(열)만 추출
# select(-one_of(변수 벡터)) => 지정벡터(열)   제거
exceptVariable <- c("id", "date", "yr_built")
analysis.variable <- houseDF %>%
  select(-one_of(exceptVariable))

# 2) grep(base library)
analysis.variable <- colnames(houseDF)[-grep("^id|^date|^yr_built|^group",
                                             colnames(houseDF))]


# group 변수 생성
# ifelse를 활용하여 group - new/old 변수 생성 => 가설검정에서는 old를 기준으로 분석해야한다.
# default는 factor에서 new, old순서이기 때문에
# factor 형태 변환시 levels와 labels를 사용해서 old, new순서로 변경
# *주의) 데이터 형 변환시 as.factor와 factor는 동일한 기능을 수행하지만
#        levels와 lables를 argument로 가질 수 있는 것은 factor
houseDF$group <- ifelse(houseDF$yr_built >= 2000, "new","old")
str(houseDF$group)
table(houseDF$group)
houseDF$group <- factor(houseDF$group,
                        levels = c("old", "new"),
                        labels = c("old", "new"))
table(houseDF$group)

# 최종 결과를 저장하기 위한 빈 벡터 생성
Normality <- c()
Method    <- c()
Equality  <- c()
TW        <- c()
PValue    <- c() 

# for문(반복 횟수는 in (조건)에 들어간 벡터의 길이로 결정됩니다.)
# analysis.variable에서 변수를 한개씩 가져와서 실행
# for문의 반복 횟수는 19번(length(anaylsis.variable))
# 19번 동안 i에는 각각의 char타입의 열이름이 들어가서 실행
for(i in analysis.variable){

  # 1) 정규성 검정
  # 왜 unlist를 사용해야 하는가?
  # 우측 houseDF의 타입 : data.frame vs tbl_df
  # data.frame의 경우 houseDF[,i] 결과가 numveric vector입니다.
  # tbl_df의 경우 houseDF[,i] 결과가 tbl_df, data.frame형태의 class입니다.
  # 따라서, tbl_df형태를 가지는 houseDF는
  # 1) unlist를 통해서 벡터형태로 변환해주거나
  # 2) houseDF[, i]$변수명 형태로 한번더 슬라이싱을 해줘야 하는 문제가 있습니다.
    result.normality <- by(unlist(houseDF[ , i]), houseDF$group, ad.test)
    # result.normality <- by((houseDF[ , i]$i), houseDF$group, ad.test)
    # houseDF를 data.frame으로 변경하고 사용할 시
    # result.normality <- by(houseDF[ , i], houseDF$group, ad.test)

  # 2) 정규성 검정 결과를 통해 모수적 방법과 비모수적 방법 구분
  # 2-1) old와 new의 p-value가 둘중 하나라도 0.05 미만일 경우 정규성이 깨진다.
  #      => 비모수적행 방법 : wilcox.test
    if( (result.normality$old$p.value < 0.05) | (result.normality$new$p.value < 0.05)){
      # wilcox.test 결과 저장
        Normality <- c(Normality, "No")
        Method    <- c(Method, "wilcox.test")
        Equality  <- c(Equality, "Non")

        # 위의 unlist 설명과 동일
        # 대립 가설이 `old와 new가 같지 않다`이기 때문에 양측검정(two.sided)
        result.wilcox <- wilcox.test(unlist(houseDF[ , i])~ houseDF$group,
                                     alternative = "two.sided")

        # 필요한 부분만 추출하여 저장
        # str(result.wilcox)를 통해 wilcox 결과가 가지는 데이터의 구조를 확인 할 수 있습니다.
        TW     <- c(TW, result.wilcox$statistic)
        PValue <- c(PValue, result.wilcox$p.value)


  # 2-2) old와 newd의 p-value가 둘 모두 0.05 이상일 경우 정규성을 따른다.
  #     => 모수적 방법 : t-test
    }else{
        Normality <- c(Normality, "Yes")
        Method    <- c(Method, "t.test")

        # 3) 등분산성 검정
        # 귀무가설 : 등분산이다.
        # 대립가설 : 이분산이다.
        result.equality <- var.test(unlist(houseDF[ , i])~ houseDF$group)

        # p-value가 0.05 미만일 경우 이분산 (var.equal = FALSE)
        if(result.equality$p.value < 0.05){
            Equality  <- c(Equality, "No")
            result.ttest <- t.test(unlist(houseDF[ , i])~ houseDF$group,
                                   alternative = "two.sided",
                                   var.equal   = FALSE)
            TW     <- c(TW, result.ttest$statistic)
            PValue <- c(PValue, result.ttest$p.value)

        # p-value가 0.05 이상일 경우 등분산 (var.equal = TRUE)
        }else{
            Equality  <- c(Equality, "Yes")
            result.ttest <- t.test(unlist(houseDF[ , i])~ houseDF$group,
                                   alternative = "two.sided",
                                   var.equal   = TRUE)
            TW     <- c(TW, result.ttest$statistic)
            PValue <- c(PValue, result.ttest$p.value)
        }
    }
}

# for문을 6개의 벡터가 생성되었습니다.
# 하나의 결과로 저장
outputTest <- data.frame(Variable = analysis.variable,
                         Normality,
                         Method,
                         Equality,
                         TW,
                         PValue)
# 결과 내보내기
writexl::write_xlsx(outputTest, path = "outputTest.xlsx")
02Data

[Fastcampus] RDC 강의 내용 정리 - 이부일 강사님

One Sample t-test

When?
하나의 모집단의 양적 자료의 평균이 기존에 알고 있던 것보다
커졌는지, 작아졌는지, 달라졌는지를 통계적으로 검정하는 방법



1. 일표본 검정

  • 귀무가설 : 성인들의 평균 키는 170cm이다.
  • 대립가설 : 성인들의 평균 키는 170cm보다 크다.

1단계 : 정규성 검정(Normality Test)

  • 귀무가설 : 정규분포를 따른다.
  • 대립가설 : 정규분포를 따르지 않는다.

Shapiro-Wilk test : shapiro.test(data$variable)

height <- c(180, 175, 170, 170, 165, 184, 164, 159, 181, 167, 182, 186)
shapiro.test(height)

<결과>
Shapiro-Wilk normality test
data:  height
W = 0.93844, p-value = 0.4781

유의확률이 0.478이므로 유의수준 0.05에서 height는 정규분포를 따른다고 가정할 수 있다.


2단계 : 일표본 T검정(One sample t-test)

t.test(data$variable, mu = , alternative = )
mu : 귀무가설의 모평균
alternative : 대립가설, "greater", "less", "two.sided"

height.test <- t.test(height, mu = 170, alternative = "greater")
height.test

<결과>
One Sample t-test

data:  height
t = 1.3887, df = 11, p-value = 0.0962
alternative hypothesis: true mean is greater than 170
95 percent confidence interval:
 168.9492      Inf
sample estimates:
mean of x
 173.5833

유의확률이 0.096이므로 유의수준 0.05에서 성인들의 키는 통계적으로 유의하게 커지지 않았다. 성인들의 키는 변화가 없다.

str(height.test)
height.test$statistic    # t
height.test$parameter    # df
height.test$p.value      # p-value
height.test$conf.int     # 95% Confidence Interval, 신뢰구간
height.test$estimate     # 추정치, x bar, 표본의 평균
height.test$null.value   # 귀무가설의 모평균
height.test$alternative  # 대립가설
height.test$method       # One sample t-test
height.test$data.name    # height

2단계 : 윌콕슨의 부호 순위 검정(Wilcoxon's signed rank test)

wilcox.test(data$variable, mu = , alernative = )

wilcox.test(height, mu = 170, alternative = "greater")


Quiz 1.가설검정

귀무가설 : 성인들의 평균 용돈은 200만원이다.
대립가설 : 성인들의 평균 용돈은 200만원보다 작다.
유의수준 : 0.05

money <- c(45, 40, 40, 50, 50, 50, 40, 100, 50)

# 1단계 : 정규성 검정(Normality Test)
options(scipen = 100) # 지수 표현식 사용하지 않음
shapiro.test(money)
# 결론 : 유의확률이 0.000이므로 유의수준 0.05에서
# money는 정규분포를 따르지 않는다. 즉 정규성 가정을 만족하지 않음

# 2단계 : Wilcoxon's signed rank test
wilcox.test(money, mu = 200, alternative = "less")
# 결론 : 유의확률이 0.004이므로 유의수준 0.05에서
# 성인들의 용돈은 200만원보다 작다라는 대립가설을 채택
# 통계적으로 유의하게 성인들의 용돈이 줄어 들었다.


Quiz 2. 가설검정 결과 자동화 코드 작성

bedrooms", "bathrooms", "floors", "waterfront", "view", "condition", "grade" 변수에 대한 가설검정 결과를 엑셀 파일에 저장하시오.
귀무가설은 평균은 5이다로 함.

houseDF <- readxl::read_excel(path = "d:/da/kc_house_data.xlsx",
                              sheet = 1,
                              col_names = TRUE)

analysis.varibles <- c("bedrooms", "bathrooms", "floors", "waterfront", "view", "condition", "grade")


tv        <- c()
pvalue    <- c()
test.type <- c()

for(i in analysis.varibles){
    print(i)
    norm.test <- ad.test(unlist(houseDF[ , i]))
    if(norm.test$p.value > 0.05){
        result.t <- t.test(unlist(houseDF[ , i]), mu = 5, alternative = "two.sided")
        tv       <- c(tv, result.t$statistic)
        pvalue   <- c(pvalue, result.t$p.value)
        test.type <- c(test.type, "ttest")

    }else{
        result.wilcox <- wilcox.test(unlist(houseDF[ , i]), mu = 5, alternative = "two.sided")
        tv            <- c(tv, result.wilcox$statistic)
        pvalue        <- c(pvalue, result.wilcox$p.value)
        test.type <- c(test.type, "wilcox")
    }
}

resultDF <- data.frame(analysis.varibles, tv, pvalue, test.type)
writexl::write_xlsx(resultDF, path = "d:/da/resultDF.xlsx")

2017 하반기 회고


'역량' ?


모두가 저 단어가 중요하다고 얘기한다. 너의 역량을 충분히 어필했는가? 내가 보유한 역량이 뭔지, 회사에서 필요로 하는 역량이 뭔지...

내가 보유하고 있는, 나만이 가지고 있는 역량은...?


회사에 입사하면 누구보다 일을 잘 할 자신이 있고, 상사가 시키는 일은 눈치껏 빠르게 잘 해내고, 조직을 이끌어나가는?

회사는 그런 날 몰라주네 라고만 생각했다.


이것은 나의 착각일 뿐이었다.


내가 지금 회사에서 신입사원으로 들어간다면 할 수 있는 일은 무엇이 있는가? 

회사가 요구하는 역량은 그 일을 배우고 시작하면서 경험을 통해 쌓이는 것 일뿐,

그 일을 하기 전까지는 단지 역량을 위한 '가능성' 만 있을 뿐이다.


회사가 요구하는 역량? 인재? 

회사의 취향일 뿐이다. 


나는 회사의 취향을 분석하고 이에 맞춰서 준비해야 한다.


지피지기면 백전백승 


2018년까지 힘내자.



'Daily life' 카테고리의 다른 글

넋두리 02  (0) 2017.08.22
넋두리 01  (0) 2017.08.02

[ R studio project ] 

  • File - New project
  • 별도의 Directory 설정이 필요없다. ( R project 파일이 위치한 곳으로 자동으로 경로 지정됨)
  • Encoding 1회 설정
  • History 관리
  • Packrat : Package 관리 (과거 프로젝트 및 코드 관리 용이)

  packrat::init() -> 프로젝트 내 packrat 폴더 생성 및 해당 패키지 설치

  packrat::snapshot() -> 현재 사용 중인 패키지 버전을 확인

  remove.packages("패키지명") -> 설치된 패키지 삭제



[ Code Pipeline 제작 ]

 - 현재 진행중인 프로젝트 데이터 수집부터 최종 결과까지 진행되는 프로세스


텍스트 마이닝 프로젝트 예시

1) Web Review Data

2) Data Filtering

3) train / test data

4) Document Term Matrix

5) Delete Sparse terms

6) Delete Neutral terms

7) Modeling

8) ANOVA

[ Fastcampus Mini-project ] House Sales in King County, USA (Predict house price using Linear regression) - kaggle 


통계를 배운지 3주 정도 지나고, 배운 내용을 바탕으로 하여 미니 프로젝트를 진행

집값에 영향을 미치는 요인을 찾고 가격을 예측

데이터 셋 : kaggle에서 제공하는 House Sales in King County 

프로젝트 기간 : 08.03 ~ 08.09




rm(list=ls())

#' @title parallel coding

#' @author Jungchul HA

#' @version 1.0, 2017.08.21

#' @description 집값 예측 미니 프로젝


# 01. setting -------------------------------------------------------------


# import use library

library(dplyr)

library(readxl)

library(ggplot2)

library(ggmap)

library(DT)

library(maps)

library(corrplot)

library(caret)

library(car)


## King County

## 원본 데이터

House = readxl::read_excel("fs/house/kc_house.xlsx",

                           sheet = "data",

                           col_names = TRUE)

House$date<-(substr(House$date, 1, 8))

House$date = as.numeric(House$date)

House$per.price = House$price / House$sqft_living


# train / test 데이터 셋 분류

set.seed(1708)

ratio = sample(1:nrow(House), size = 0.30*nrow(House))

test = House[ratio,] #Test dataset 30% of total

train = House[-ratio,] #Train dataset 70% of total


# 분류된 모델을 바탕으로 분석 진행

# write.csv(train,

#           file      = "fs/house/train.csv",

#           row.names = FALSE)

# write.csv(train,

#           file      = "fs/house/test.csv",

#           row.names = FALSE)

# train = read.csv("fs/house/train.csv",

#                  header = TRUE)

# test = read.csv("fs/house/test.csv",

#                 header = TRUE)




# 02. EDA ----------------------------------------------------------------


summary(House)

View(House)

str(House)

colSums(is.na(House)) # 결측값은 0



# 02-1. 상관관계 분석 ---------------------------------------------------


train.num <- cor(train) ## House_num 생성 후 상관관계 분석치 할당

round(train.num, 3) ## 소수점 셋째자리까지 출력 

corrplot::corrplot(train.num, 

                   method = "number", 

                   shade.col = NA, 

                   tl.col = "black", 

                   tl.srt = 50,

                   order = "hclust",

                   diag = FALSE)


pairs(train, 

      pch = 19, 

      bg = c("red", "green", "blue"))  # 행렬모양 산점도


# 상관원계수가 클수록 크기가 크고 색깔이 진하다

# 양수면 파란색, 음수면 붉은색

corrplot(train)

corrplot(train, method = "number")    # 수와 색깔로 표현

corrplot(train, 

         method = "color",      # 색깔로 표현

         col    = col(200),     # 색상 200개 선정

         type   = "lower",      # 왼쪽 아래 행렬만 표기

         order  = "hclust",     # 유사한 상관계수끼리 군집화

         addCoef.col = "black", # 상관계수 색깔

         tl.col = "black",      # 변수명 색깔  

         tl.srt = 45,           # 변수명 45도 기울임

         diag   = F)


round(cor(test),3)

test = train %>% 

  select(price, sqft_living, grade ,sqft_above, sqft_living15,

         bathrooms, view, sqft_basement, bedrooms)


# 결과 : 5개의 변수가 price와 높은 상관관계를 보여줌

# price ~ sqft_living   0.702

# price ~ grade         0.667

# price ~ sqft_above    0.606

# price ~ sqft_living15 0.585

# price ~ bathrooms     0.525

# ------------------------- #

# price ~ view          0.397

# price ~ sqft_basement 0.324

# price ~ bedrooms      0.308

# price ~ waterfront    0.266

# price ~ floors        0.257

# price ~ yr_renovate   0.126

# price ~ sqft_lot      0.0897

# price ~ sqft_lot15    0.0824

# price ~ yr_built      0.054

# price ~ condition     0.0364


# per.price 와 나머지는 선형관계를 보이지는 않는다.


train1 = train


analysis = train %>% 

  select(price, zipcode, sqft_living, grade, bathrooms, long, lat, bedrooms)


# 양의 상관관계를 보이는 변수들 중 집을 보러 왔을때 신경쓰는 요소들을 

# 변수로 사용

# waterfront의 경우 163개의 데이터가 있지만 전역에 퍼져있으며 

# 집값의 경우도 비싼편은 아니라서 제외




# 02-2. zipcode를 바탕으로 파생변수 생성 ---------------------------------


## zipcode는 5자리로 구성되어 있으며,

## 주, 카운티, 도시 단위의 광범위한 지역을 나타내준다.

## 미국인들에게는 zipcode 자체가 '부와 명예'를 상징한다.(출처:LA중앙일보)

## zipcode(각 도시)마다 단위면적 당 가격(per.price)이

## 비슷한 가격대를 형성하고 있지 않을까?

## zipcode grouping 하여 파생변수로 활용해보자.

## 단위면적 당 price 계산 => per.price

analysis$per.price = analysis$price / analysis$sqft_living

# 최소 87.59 / 최대 810.14


## zipcode 별로 grouping

zipcode.price = analysis %>% 

  select(zipcode, per.price) %>% 

  group_by(zipcode) %>%  

  summarise(mean.price = mean(per.price),

            max.price = max(per.price),

            min.price = min(per.price),

            median.price = median(per.price),

            n = n())


head(zipcode.price, n=10)

## zipcode = 70개

## zipcode 별로 데이터의 개수 최소 34개 ~ 430개



# zipcode별 평균 - 중위수 차이 

zipcode.price$dif.price = round(zipcode.price$mean.price - zipcode.price$median.price, digits = 2)

zipcode.price = zipcode.price[order(zipcode.price$dif.price),]

## 평균과 중위수의 차이가 많이 나면 

## 해당 zipcode내에는 outlier로 예상되는 값이 존재 할 수도 있다 라고 생각.

## 단위 면적 가격 mean - median를 구해보니 

## 나머지 -0.6 ~ 21

## 결론) zipcode별로 평당 가격이 큰 차이를 보이는 집은 별로 없다.

DT::datatable(zipcode.price)


## 100단위로 dummy변수 대입

zipcode.price$median.price = round(zipcode.price$median.price)

zipcode.price$dummy.median = as.numeric(substr(zipcode.price$median.price,1,1))*100

## zipcode.price에서 zipcode, dummy.median를 추출하여 mg데이터 생성

mg = zipcode.price %>% 

  select(zipcode, dummy.median)


## train과 mg를 zipcode 기준으로 left outer join 

analysis1 = merge(analysis, mg, by = "zipcode", all.x=TRUE)

analysis1$dummy.median = as.factor(analysis1$dummy.median)


KingCounty = ggmap::get_map(location = c(lon=-122.3, lat=47.4),

                            zoom = 9)

KingCounty1 = ggmap(KingCounty) + geom_point(data = analysis1, aes(x = long, y = lat, colour = dummy.median))

KingCounty1


## 단위 면적 가격이 200이지만 상대적으로 가격대가 높은 지역과

## 붙어있는 3곳을 추가로 확인


## median.price 가 200~300 사이인 데이터만 추출

zipcode.price1 = zipcode.price %>%

  filter(median.price >= 200 & median.price <300) %>% 

  select(zipcode, median.price)


## 나머지를 구해서 10의자리 계산

zipcode.price1$dummy.median = as.numeric(substr(zipcode.price1$median.price,1,2))%%10

DT::datatable(zipcode.price1)


analysis2 = merge(analysis, zipcode.price1, by = "zipcode", all.x = TRUE)

analysis2$dummy.median = as.factor(analysis2$dummy.median) 

KingCounty2 = ggmap(KingCounty) + geom_point(data = analysis2, aes(x = long, y = lat, colour = dummy.median))

KingCounty2


## per.price 가 200대인 집들을 시각화 시켜봄.

## 200초반대 까지는 100에 

## 200 중반은 200으로

## 200 후반 값을 가지는 8,9는 300에 같이 그룹핑

zipcode.price2 = zipcode.price

zipcode.price2 = within(zipcode.price2,{

  dummy.median = numeric(0)

  dummy.median [ median.price < 220] = 1

  dummy.median [ median.price >= 221 & median.price <= 279 ] = 2

  dummy.median [ median.price >= 280 & median.price <= 399 ] = 3

  dummy.median [ median.price >= 400 & median.price <= 499 ] = 4

  dummy.median [ median.price >= 500 & median.price <= 599 ] = 5

  dummy.median [ median.price >= 600] = 6

})

DT::datatable(zipcode.price2)


mg1 = zipcode.price2 %>% 

  select(zipcode, dummy.median)

analysis3 = merge(analysis, mg1, by = "zipcode", all.x=TRUE)

analysis3$dummy.median = as.factor(analysis3$dummy.median)


KingCounty3 = ggmap(KingCounty) + geom_point(data = analysis3, aes(x = long, y = lat, colour = dummy.median))

KingCounty3


# 비싼 지역만 확대

KingCounty.zoom = ggmap::get_map(location = c(lon=-122.3, lat=47.6), zoom=11)

KingCounty4 = ggmap(KingCounty.zoom) + geom_point(data = analysis3, aes(x = long,  y = lat, colour = dummy.median))

KingCounty4

## 가격을 재분류 하여 찍어주었고

## 가격대가 높은 지역만 확대!




# 03. Modeling(회귀분석) -------------------------------------


## 다중선형회귀분석

## 집값에 영향이 있다고 생각되는 변수 추출

result = analysis3 %>% 

  select(price, bedrooms, bathrooms, grade, sqft_living, dummy.median)


## price와 sqft_living 은 숫자 단위가 크므로 log를 취함

model.1 = lm(log(price) ~ bedrooms + bathrooms + grade + log(sqft_living) + factor(dummy.median), data = result)

summary(model.1)

## bathrooms 은 0.063로 변수 제외


model.2 = lm(log(price) ~ bedrooms + grade + log(sqft_living) + factor(dummy.median), data = result)

summary(model.2)

vif(model.2)

lm.beta::lm.beta(model.2)

## bedrooms  : -0.040로 변수 제외


model.3 = lm(log(price) ~ grade + log(sqft_living) + factor(dummy.median), data = result)

summary(model.3)

vif(model.3)

lm.beta::lm.beta(model.3)

## 각 변수들간에 영향도도 있고

## Adjusted R-squared : 0.800

## waterfront는 163개 뿐인데 굳이 넣어야 될 필요성이 있을까?

## waterfront를 보유한 집들은 지도 전역에 골고루 퍼져있음




# 04. 모델 검증 ----------------------------------------------


vif(model.3)

## 다중공선성 문제도 없고

## 해당 모델을 최종 모델로 결정해서 test셋을 예측해보자


## 기존에 zipcode별로 그룹핑한 mg1 과 test 데이터를 merge 해서

## test 데이터에 dummy.median을 만들어주고

test = merge(test, mg1, by = "zipcode", all.x=TRUE)

test$log.price = log(test$price)


## test를 예측해보자

pred = predict(model.3, 

               newdata = test,

               interval = "predict")

pred

## 예측된 값고 test데이터의 log.price와 비교해보면

## 둘 사이의 관계가 0.89 정도

pred = as.data.table(pred)

pred$fit.price = 10^(pred$fit)

### log n복구

colnames(pred)

pred.final = pred 

test.final = test %>% 

  select(price, log.price)


final = cbind(test.final, pred.final)

final$result = ifelse( final$lwr<=final$log.price & final$log.price<=final$upr ,1,0)

table(final$result)



# 05. 기타(잔치 그래프) 확인 ----------------------------------------


analysis3[hatvalues(model.3) >= 0.025 , ]

# zipcode가 98039인 곳 가장 비쌈

out = train[c(11745,11812),]

DT::datatable(out)

out$per.price = out$price / out$sqft_living

## 다른 조건들에 비해 grade가 높아서 이상치로 판별됨


rm(list=ls())


train.out1 = train %>% 

  filter(zipcode == 98070)

mean(train.out1$per.price)


train.out2 = train %>% 

  filter(zipcode == 98146)


mean(train.out2$per.price)

zipcode.price3 = zipcode.price

zipcode.price = within(zipcode.price,{

  dummy.mean = numeric(0)

  dummy.mean [ mean.price < 220] = 1

  dummy.mean [ mean.price >= 221 & mean.price <= 279 ] = 2

  dummy.mean [ mean.price >= 280 & mean.price <= 399 ] = 3

  dummy.mean [ mean.price >= 400 & mean.price <= 499 ] = 4

  dummy.mean [ mean.price >= 500 & mean.price <= 599 ] = 5

  dummy.mean [ mean.price >= 600] = 6

})

DT::datatable(zipcode.price3)


mg1 = zipcode.price3 %>% 

  select(zipcode, dummy.mean)

train3 = merge(train, mg1, by = "zipcode", all.x=TRUE)

train3$dummy.mean = as.factor(train3$dummy.mean)


model4 = lm(log(price) ~ grade + log(sqft_living) + factor(dummy.mean), data = train)

summary(model4)

lm.beta::lm.beta(model4)

vif(model4)

plot(model4)

names(model4)

out = train3[c(4827,6475,13894,14499),]


water = train3 %>% 

  filter(waterfront == 0)


model5 = lm(log(price) ~ grade + log(sqft_living) + factor(dummy.mean), data = water)

summary(model5)

lm.beta::lm.beta(model5)

vif(model5)

par(mfrow = c(2,2))

plot(model5)



여행을 다니는 돈이 아깝다고 말한 친구가 갑자기 생각이 났고, 가만히 생각해보니 매년 해외로 떠난 것 같다. 야밤에 공부하다 말고 뭐하는 건지는 모르겠지만 그냥 작성해보고 싶었다.

2013년. 호기심의 시작
무료한 군 상병시절. TV로 우연히 보게 된 헝가리 부다페스트 야경이 시작이었던 것 같다. 이국적인 건물과 화려한 조명들... 무작정 저곳에 가봐야겠다는 생각이 들었다. (이때만 해도 주변에 해외여행을 다녀온 사람은 없었던 것 같은데...?) 유럽여행에 필요한 경비를 모으기 위해 전문하사 6개월 연장과 전역 후에는 투잡을 뛰며 돈을 모았고, 1년 동안 천만원이라는 자금을 마련했다. 돈을 모으기 위해 군대 연장이라는 독특한 선택?을 했지만 동기들과 다 함께 전문하사를 지원해서(사실 내가 꼬드꼇지만) 나름 즐거운 생활이었던 것 같다.

2014년. 나 홀로 유럽
모든 여행의 시작은 비행기 발권 아니겠는가? 되돌릴 수 없는 상황을 만들어두고 계획을 세우기 시작했다. 3개월 전부터 유랑 카페, 여행사 패키지 등을 비교 분석해보며 나만의 여행 일정을 계획했고, 6월 한 달간 유럽여행을 다녀왔다. 현지의 다양한 문화 속에서 그들만의 세계를 느끼고, 여행에서 처음 만난 사람들과 밤새도록 이야기를 나누며 마시는 맥주 등 모든 것이 새로웠고 신기한 경험이었다. 즐거운 시간은 어찌 이리도 빨리 가는지, 꿈에서 빠져나올 때 쯤 나는 복학생 신분으로 학교로 돌아갔다. 돈이 없어서 시작한 카페 아르바이트. 파스쿠찌와의 인연도 이때부터 인 것 같다.

2015년. 꿩 대신 닭
현실 속에 바삐 살면서도 해외여행에 대한 그리움은 계속 남아있었던 것 같다. 때마침 학교에서는 전공심화 해외체험 프로그램이 진행되고 있었고 동아리 사람들과 미국을 목표로 준비했다. 하지만 결론은 처참하게 떨어졌지(이래서 미국은 별로 가고 싶지 않는건가?) 꿩 대신 닭이라고 프로그램을 같이 준비하던 사람들과 카페에서 무작정 결정한 보라카이 여행. 
3대 해변이라 불리는 화이트 비치, 커플들이 가득한 휴양지 보라카이를 남자 4명이서 다녀왔다. 저희 왜 보라카이로 간거죠...? 유럽도 그렇고 보라카이도 그렇고 여행에서 하는 액티비티는 항상 꿀잼이다.

2016년. 재도전
작년의 아쉬움 때문인가? 또 다시 전공심화 해외체험 프로그램에 도전하게 된다. 교수님의 노련한 첨삭 덕분인지 프로그램에 당첨되었고 우리는 인도로 떠나게 되었다. (이때 인도 누가 가자고 했니-_-?) 프로그램에 대해 간단히 설명하자면 사전에 관련 전공 회사, 대학 컨텍을 통해 탐방을 기획해야하고, 교수님 동행, 동기들, 후배들과 함께 팀으로 진행된다. 연인, 친구들과 여행을 가도 싸우기 마련인데 저 조합으로 그것도 인도를 떠날 생각을 했다니 두 번은 못할 것 같다. 팀장 역할을 맡게 되어서 걱정도 많았는데 교수님도 그렇고, 아이들도 모두 잘 따라 와줘서 별탈없이 즐겁게 다녀온 것 같다. 그러고 친구들과 세부도 다녀왔지(얘들아 싸움은 부질없는거야... 흑흑)

2017년. 여전히 꿈을 꾸고 삽니다.
대학교 졸업하기 전에 갑작스럽게 인턴생활을 시작했다. 인턴 얘기가 나오고 다음 주에 바로 출근이라니. 하지만 졸업과 동시에 인턴생활도 끝이 났고, 대학교 4학년 말에 고생한 나에 대한 위로인지, 회사 생활에 대한 해방감인지 홧김에 남미로 떠나는 비행기표를 질렀다. 역시 여행은 즉흥이지. 인턴이 끝나고 3주도 지나지 않아 아무런 준비도 없이 남미로 떠났고 걱정과는 다르게 좋은 사람들과 가장 행복한 여행을 다녀온 것 같다. 여행에서 이렇게까지 친해질 수 있구나 라는 걸 깨달은 여행이라고 해야 되나? 즐거운 시간은 항상 순식간에 사라지고 나는 다시 취업준비를 하는 현실에 사는 중이다. 아마 한동안은 이런 장기여행은 힘들 것 같다. 또 다시 이런 여행을 계획한다면 여자친구가 나의 목숨을 노리겠지ㅎㅎㅎ 그렇지만 언젠가는 다시 떠날 테고 다음 여행지는 아프리카가 되지 않을까 하는 생각을 한다.

나는 그 친구와는 다르게 물건보다 경험에 돈을 투자한 것 뿐이고 이것을 후회하지 않는다. 대자연이 주는 힐링(저는 도시보다 자연이 좋습니다. 스위스가 가고싶군요), 숨 막히는 현실에서 벗어난 온전한 나만의 시간, 추억을 공유하는 소중한 사람들. 그 어떤 것과도 바꿀 수 없는 경험이라고 생각한다.

두서없이 그냥 막 쓴 거라 내용만 보시길

에휴 다시 공부해야지


'Daily life' 카테고리의 다른 글

2017 하반기 회고  (0) 2017.11.30
넋두리 01  (0) 2017.08.02

+ Recent posts