데이터과학 입문
서울대학교 통계학과
April 2024
다음의 패키지가 설치되어 있지 않으면 설치한다.
비영리단체인 Gapminder에서 정리한 국가별, 연도별 15–49세 사이 인구의 HIV 유병률에 대한 데이터를 이용하여 미국, 프랑스, 남아프리카 공화국의 1979년부터 2009년까지 매 10년마다의 HIV 유병률을 살펴보고자 한다.
데이터는 구글 스프레드시트로 클라우드 상에 저장되어 있고, 접근을 위한 인증이 필요하다. 이를 위해 googlesheets4
패키지를 사용한다.
hiv_key <- "1kWH_xdJDM4SMfT_Kzpkk-1yuxWChfurZuWYjfmv51EA"
hiv <- googlesheets4::read_sheet(hiv_key) %>%
rename(Country = 1) %>%
filter(
Country %in% c("United States", "France", "South Africa")
) %>%
select(Country, `1979`, `1989`, `1999`, `2009`) %>%
unnest(cols = c(`2009`)) %>% ### more on unnest() later
mutate(across(matches("[0-9]"), as.double))
hiv
# A tibble: 3 × 5
Country `1979` `1989` `1999` `2009`
<chr> <dbl> <dbl> <dbl> <dbl>
1 France NA NA 0.3 0.4
2 South Africa NA NA 14.8 17.2
3 United States 0.0318 NA 0.5 0.6
NA
)을 매우 쉽게 추정할 수 있다.# A tibble: 12 × 3
Country Year hiv_rate
<chr> <chr> <dbl>
1 France 1979 NA
2 France 1989 NA
3 France 1999 0.3
4 France 2009 0.4
5 South Africa 1979 NA
6 South Africa 1989 NA
7 South Africa 1999 14.8
8 South Africa 2009 17.2
9 United States 1979 0.0318
10 United States 1989 NA
11 United States 1999 0.5
12 United States 2009 0.6
단점: 스프레드시트형보다 유병률의 변화 등을 시각적으로 알아차리기 어려움
장점
Gapminder HIV 데이터의 긴 자료표 형식을 깔끔한 데이터(tidy data)라고 부른다.
복잡한 데이터를 다루기 위해서는 간단한 일을 하는 표준적인 도구들을 순차적으로 적용하는 것이 효과적
깔끔한 데이터: 표준 도구가 적용될 수 있도록 단순하지만 정확하게 정의된 패턴으로 정렬된 데이터
babynames
데이터year | sex | name | n |
---|---|---|---|
1999 | M | Kavon | 104 |
1984 | F | Somaly | 6 |
2017 | F | Dnylah | 8 |
1918 | F | Eron | 6 |
1992 | F | Arleene | 5 |
1977 | F | Alissia | 5 |
1919 | F | Bular | 10 |
행: 사례 혹은 관측치. 특정하고 고유하며 유사한 유형의 것을 가리킴 (예: 이름이 Somaly인 1984년생 여자아이)
열: 변수. 각 행마다 동일한 종류의 값을 가짐 (예: n
– 신생아 수, sex
– 성별)
데이터가 깔끔한 형태로 정리되어 있으면 흥미로운 질문에 답하는 데 더 유용한 배열로 데이터를 비교적 간단하게 변환할 수 있음.
연도를 통틀어 가장 인기있는 이름?
sex | name | total_births |
---|---|---|
M | James | 5150472 |
M | John | 5115466 |
M | Robert | 4814815 |
M | Michael | 4350824 |
F | Mary | 4123200 |
M | William | 4102604 |
M | David | 3611329 |
M | Joseph | 2603445 |
M | Richard | 2563082 |
M | Charles | 2386048 |
자료표에 내재된 정보를 명시적으로 드러내는 다른 자료표로 변환하는 과정
깔끔한 데이터에 “data verb”를 적용하여 다른 형태의 깔끔한 데이터로 변환함으로써 실행 (4, 5장)
정돈된 데이터에 대한 규칙을 준수하면 데이터의 요약, 분석이 간단해짐.
babynames
자료표(깔끔)에서 컴퓨터로 총 아기 수를 찾으려면 변수 n
에 있는 모든 숫자를 더하면 됨.
표본 크기는 행을 세기만 하면 됨.
Minneapolis 선거 데이터(안 깔끔)에서는 총 투표자 수?
Figure 1 의 I열(“Total Ballots Cast”)에 있는 숫자를 더하면 일부 행에 사례가 아닌 요약이 포함되어 있으므로 결과는 실제 투표자 수의 3배
ward | precinct | registered | voters | absentee | total_turnout |
---|---|---|---|---|---|
1 | 1 | 28 | 492 | 27 | 0.2723 |
1 | 4 | 29 | 768 | 26 | 0.3662 |
1 | 7 | 47 | 291 | 8 | 0.1579 |
2 | 1 | 63 | 1011 | 39 | 0.3642 |
2 | 4 | 53 | 117 | 3 | 0.0734 |
2 | 7 | 39 | 138 | 7 | 0.1378 |
2 | 10 | 87 | 196 | 5 | 0.0691 |
3 | 3 | 71 | 893 | 101 | 0.3735 |
3 | 6 | 102 | 927 | 71 | 0.3531 |
깔끔한 자료표의 한 행은 하나의 사례를 나타낸다.
현실 세계에서 사례가 무엇을 의미하는가?
Precinct | First | Second | Third | Ward |
---|---|---|---|---|
P-04 | undervote | undervote | undervote | W-6 |
P-06 | BOB FINE | MARK ANDREW | undervote | W-10 |
P-02D | NEAL BAXTER | BETSY HODGES | DON SAMUELS | W-7 |
P-01 | DON SAMUELS | undervote | undervote | W-5 |
P-03 | CAM WINTON | DON SAMUELS | OLE SAVIOR | W-1 |
어떤 설명이 모든 사례를 고유하게 만드는가?
투표 요약 자료표에서 투표소는 사례를 고유하게 식별하지 않음
같은 투표소는 여러 행에 걸쳐 나타남
투표소-선거구 조합은 단 한 번만 나타남
마찬가지로 Table 1 에서 이름과 성별은 사례를 유일하게 지정하지 않음
이름-성별-생년의 조합이 있어야 행을 유일하게 식별
name.yob | sex | age | year | gun |
---|---|---|---|---|
jane polanek 1974 | F | 32 | 2006 | 114.50000 |
jane poole 1948 | F | 55 | 2003 | 92.71667 |
jane poole 1948 | F | 56 | 2004 | 87.28333 |
jane poole 1948 | F | 57 | 2005 | 85.05000 |
jane poole 1948 | F | 58 | 2006 | 80.75000 |
jane poole 1948 | F | 59 | 2007 | 78.53333 |
jane schultz 1964 | F | 35 | 1999 | 91.36667 |
jane schultz 1964 | F | 37 | 2001 | 79.13333 |
jane schultz 1964 | F | 38 | 2002 | 76.83333 |
jane schultz 1964 | F | 39 | 2003 | 82.70000 |
jane schultz 1964 | F | 40 | 2004 | 87.91667 |
jane schultz 1964 | F | 41 | 2005 | 91.46667 |
jane schultz 1964 | F | 42 | 2006 | 88.43333 |
jane smith 1952 | F | 47 | 1999 | 90.60000 |
jane smith 1952 | F | 49 | 2001 | 97.86667 |
gun
변수는 무엇을 뜻하는가?inner_join()
및 left_join()
함수를 사용하여 결합, 새로운 깔끔한 자료표를 만들 수 있음# A tibble: 3 × 3
subject before after
<chr> <dbl> <dbl>
1 BHO 160 115
2 GWB 120 135
3 WJC 105 145
# A tibble: 6 × 3
subject when sbp
<chr> <chr> <dbl>
1 BHO before 160
2 GWB before 120
3 WJC before 105
4 BHO after 115
5 GWB after 135
6 WJC after 145
# A tibble: 9 × 5
...1 subject when sbp dbp
<dbl> <chr> <chr> <dbl> <dbl>
1 1 BHO before 160 69
2 2 GWB before 120 54
3 3 BHO before 155 65
4 4 WJC after 145 75
5 5 WJC after NA 65
6 6 WJC after 130 50
7 7 GWB after 135 NA
8 8 WJC before 105 60
9 9 BHO after 115 78
WJC
에 대한 여러 개의 “after” 측정값 – 반복측정자료pivot_wider()
# A tibble: 3 × 3
subject before after
<chr> <dbl> <dbl>
1 BHO 160 115
2 GWB 120 135
3 WJC 105 145
names_from
: 넓은 자료표에서 변수 이름으로 사용할 좁은 자료표의 변수
values_from
: 넓은 자료표에서 변수의 값으로 사용할 좁은 자료표의 변수
BP_narrow
에서 ‘when’ 열의 값(before
/after
)을 변수로, ‘sbp’ 열의 값을 새 변수의 값으로 사용
다음 자료가 세 변수 (city
, large
, small
)를 가지는 깔끔한 자료라면 어떻게 생겼을까?
pivot_longer()
# A tibble: 6 × 3
subject when sbp
<chr> <chr> <dbl>
1 BHO before 160
2 BHO after 115
3 GWB before 120
4 GWB after 135
5 WJC before 105
6 WJC after 145
before
/after
)은 수집되어(gather) 좁은 자료표의 범주형 변수 레벨이 됨names_to
: 이 범주형 변수의 이름을 지정해 주어야 하는데, when
으로 지정values_to
: 수집되는 변수의 값을 저장할 변수의 이름도 지정해야 하는데, sbp
로 지정subject
는 제외다음 자료가 세 변수 (country
, year
, n
)를 가지는 깔끔한 자료라면 어떻게 생겼을까?
# A tibble: 6 × 3
# Groups: subject [3]
subject when mean_sbp
<chr> <chr> <dbl>
1 BHO after 115
2 BHO before 158.
3 GWB after 135
4 GWB before 120
5 WJC after 138.
6 WJC before 105
모든 관측값을 포함하는 데이터 요약을 만들 수 있을까?
BP_summary <- BP_full %>%
group_by(subject, when) %>%
summarize(
sbps = paste(sbp, collapse = ", "),
dbps = paste(dbp, collapse = ", ")
)
BP_summary
# A tibble: 6 × 4
# Groups: subject [3]
subject when sbps dbps
<chr> <chr> <chr> <chr>
1 BHO after 115 78
2 BHO before 160, 155 69, 65
3 GWB after 135 NA
4 GWB before 120 54
5 WJC after 145, NA, 130 75, 65, 50
6 WJC before 105 60
# A tibble: 6 × 5
# Groups: subject [3]
subject when sbps dbps mean_sbp
<chr> <chr> <chr> <chr> <dbl>
1 BHO after 115 78 138.
2 BHO before 160, 155 69, 65 138.
3 GWB after 135 NA 128.
4 GWB before 120 54 128.
5 WJC after 145, NA, 130 75, 65, 50 125
6 WJC before 105 60 125
sbps
, dbps
항목이 문자열이기 때문에 평균이 제대로 계산되지 않음tidyr::nest()
# A tibble: 6 × 3
# Groups: subject, when [6]
subject when data
<chr> <chr> <list>
1 BHO before <tibble [2 × 3]>
2 GWB before <tibble [1 × 3]>
3 WJC after <tibble [3 × 3]>
4 GWB after <tibble [1 × 3]>
5 WJC before <tibble [1 × 3]>
6 BHO after <tibble [1 × 3]>
tibble
로 축소list
, 이름의 기본값은 data
list
형 변수를 list-column이라고 부름list
일 뿐이고, 그 벡터의 변수형은 임의data
열은 tibble
로 구성된 list
형 벡터tibble
의 크기(data
열의 항목)는 다를 수 있음dplyr::pull()
tibble
의 특정 열을 가져올 수 있다.Error in `mutate()`:
ℹ In argument: `sbp_list = pull(data, sbp)`.
ℹ In group 1: `subject = "BHO"` and `when = "after"`.
Caused by error in `UseMethod()`:
! no applicable method for 'pull' applied to an object of class "list"
purrr::map()
data
는 tibble
로 이루어진 list
. tibble
이 아님!map()
을 사용하면 data
의 각 원소에 pull()
을 적용할 수 있음 (7장)# A tibble: 6 × 4
# Groups: subject, when [6]
subject when data sbp_list
<chr> <chr> <list> <list>
1 BHO before <tibble [2 × 3]> <dbl [2]>
2 GWB before <tibble [1 × 3]> <dbl [1]>
3 WJC after <tibble [3 × 3]> <dbl [3]>
4 GWB after <tibble [1 × 3]> <dbl [1]>
5 WJC before <tibble [1 × 3]> <dbl [1]>
6 BHO after <tibble [1 × 3]> <dbl [1]>
sbp_list
는 double
로 이루어진 list
map()
을 한 번 더 사용하여 SBP의 평균을 구할 수 있다.# A tibble: 6 × 5
# Groups: subject, when [6]
subject when data sbp_list sbp_mean
<chr> <chr> <list> <list> <list>
1 BHO before <tibble [2 × 3]> <dbl [2]> <dbl [1]>
2 GWB before <tibble [1 × 3]> <dbl [1]> <dbl [1]>
3 WJC after <tibble [3 × 3]> <dbl [3]> <dbl [1]>
4 GWB after <tibble [1 × 3]> <dbl [1]> <dbl [1]>
5 WJC before <tibble [1 × 3]> <dbl [1]> <dbl [1]>
6 BHO after <tibble [1 × 3]> <dbl [1]> <dbl [1]>
tidyr::unnest()
sbp_mean
이 길이 1의 double
형 리스트이므로, unnest()
를 사용하여 list-column의 중첩 구조를 풀어줌# A tibble: 6 × 5
# Groups: subject, when [6]
subject when data sbp_list sbp_mean
<chr> <chr> <list> <list> <dbl>
1 BHO before <tibble [2 × 3]> <dbl [2]> 158.
2 GWB before <tibble [1 × 3]> <dbl [1]> 120
3 WJC after <tibble [3 × 3]> <dbl [3]> 138.
4 GWB after <tibble [1 × 3]> <dbl [1]> 135
5 WJC before <tibble [1 × 3]> <dbl [1]> 105
6 BHO after <tibble [1 × 3]> <dbl [1]> 115
# A tibble: 2 × 3
# Groups: name [1]
name sex total
<chr> <chr> <int>
1 Sue F 144465
2 Sue M 519
pivot_wider()
로 대조를 쉽게NA
로 기록되는 것을 방지하기 위해 values_fill = 0
으로 지정baby_wide %>%
filter(M > 50000, F > 50000) %>%
mutate(ratio = pmin(M / F, F / M) ) %>%
arrange(desc(ratio)) %>% # ratio를 기준으로 정렬
head(3)
# A tibble: 3 × 4
name F M ratio
<chr> <int> <int> <dbl>
1 Riley 100881 92789 0.920
2 Jackie 90604 78405 0.865
3 Casey 76020 110165 0.690
변수 또는 함수명을 정할 때 고려해야할 사항
100NCHS
(X), NCHS100
(O).
또는 _
를 제외한 다른 특수기호들은 사용할 수 없다. ?NCHS
(X), N*Hanes
(X).
을 사용하지 않는 것이 좋다. (_
을 사용하라.)NCHS
, ncHs
, nChs
등은 모두 다른 이름._
)을 사용한다. 함수 이름에 마침표(.
)를 사용하는 것은 S3 메소드로 제한한다.styler
패키지를 사용하여 코드를 tidyverse 스타일 가이드를 구현하는 형식으로 다시 포맷할 수 있다.쉬운 데이터 형식은 모두 비슷하다. 모든 어려운 데이터 형식은 각자의 방식으로 어렵다.
인터넷상의 데이터를 (구조화된) 텍스트로 처리하여 데이터로 변환하는 데이터 수집 형태
데이터 입력의 실수나 저장 또는 코딩 방식의 결함으로 인해 발생하는 오류가 있는 경우가 많음.
데이터 정제(data cleaning) – 이러한 오류를 수정하는 작업
.rda
또는 .RData
확장자로 구분Note
saveRDS()
사용)하여 두 번째 마크다운 파일에서 읽을 수 있는(readRDS()
사용) 하나의 마크다운 파일 또는 노트북을 만들면 편리..mat
): 공학 및 물리학에서 널리 사용.dta
): 경제 연구에 주로 사용.sav
): 사회과학 연구에 주로 사용.mtw
): 비즈니스 애플리케이션에 자주 사용됨.sas7bdat
): 대규모 데이터 세트에 자주 사용.xlsx
): 비즈니스에서 많이 사용되는 독점 스프레드시트 형식.
<table>
형식"year","sex","name","n","prop"
1880,"F","Mary",7065,0.07238359
1880,"F","Anna",2604,0.02667896
1880,"F","Emma",2003,0.02052149
1880,"F","Elizabeth",1939,0.01986579
1880,"F","Minnie",1746,0.01788843
1880,"F","Margaret",1578,0.0161672
.csv
, .txt
, .dat
\t
, .tsv
), |
등base::read.csv()
, readr::read_csv()
(큰 파일에서 더 빠름)
CSV 파일은 로컬 하드 드라이브에 존재하지 않아도 됨:
mdsr_url <- "https://raw.githubusercontent.com/beanumber/mdsr/master/data-raw/"
houses <- mdsr_url %>%
paste0("houses-for-sale.csv") %>%
read_csv()
head(houses, 3)
# A tibble: 3 × 16
price lot_size waterfront age land_value construction air_cond fuel heat
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 132500 0.09 0 42 50000 0 0 3 4
2 181115 0.92 0 0 22300 0 0 2 3
3 109000 0.19 0 133 7300 0 0 2 3
# ℹ 7 more variables: sewer <dbl>, living_area <dbl>, pct_college <dbl>,
# bedrooms <dbl>, fireplaces <dbl>, bathrooms <dbl>, rooms <dbl>
http://en.wikipedia.org/wiki/Mile_run_world_record_progression 에 있는 다음의 표를 불러오고 싶다.
url <- "http://en.wikipedia.org/wiki/Mile_run_world_record_progression"
tables <- url %>%
rvest::read_html() %>%
html_nodes("table")
length(tables)
[1] 13
tables
에는 해당 페이지 내에 있는 총 13개의 표가 포함되어있다.purrr
의 pluck()
함수를 사용하여 원하는 표를 꺼낼 수 있다.응용 프로그램 인터페이스(application programming interface, API): 사용자가 제어할 수 없는 컴퓨터 프로그램과 상호 작용하기 위한 프로토콜
텔레비전의 리모컨 설명서와 달리 “블랙박스”를 사용하기 위해 합의된 일련의 지침
다양한 소스에서 웹 상의 거대한 공개 데이터에 접근할 수 있게 해줌.
모든 API가 동일하지는 않지만, 이를 사용하는 방법을 익힘으로써 데이터를 수동으로 스크래핑하지 않고도 데이터를 효과적으로 끌어올 수 있다.
Table 6 의 표에서 Time
과 Date
변수는 문자열로 저장됨
이 정보를 사용하려면 먼저 날짜 및 시간을 컴퓨터가 처리할 수 있는 형식으로 변환해야 함
데이터 정제란 변수에 포함된 정보를 가져와서 해당 정보를 사용할 수 있는 형태로 변환하는 것을 말함
houses-for-sale.csv
는 미국 뉴욕주 사라토가 지역의 1728개 주택 매물에 대한 데이터이다 (mdsr::saratoga_houses
).houses %>%
select(fuel, heat, sewer, construction) %>%
head(5) %>%
mdsr_table(caption = "Four of the variables from the tables giving features of the Saratoga houses stored as integer codes. Each case is a different house.")
fuel | heat | sewer | construction |
---|---|---|---|
3 | 4 | 2 | 0 |
2 | 3 | 2 | 0 |
2 | 3 | 3 | 0 |
2 | 2 | 2 | 0 |
2 | 2 | 3 | 1 |
sewer
, heat
등의 경우, 범주형임에도 불구하고 수치형로 표기됨
codes <- translations %>%
pivot_wider(
names_from = system_type,
values_from = meaning,
values_fill = "invalid"
)
codes %>%
mdsr_table(caption = "The Translations data table rendered in a wide format.")
code | new_const | sewer_type | central_air | fuel_type | heat_type |
---|---|---|---|---|---|
0 | no | invalid | no | invalid | invalid |
1 | yes | none | yes | invalid | invalid |
2 | invalid | private | invalid | gas | hot air |
3 | invalid | public | invalid | electric | hot water |
4 | invalid | invalid | invalid | oil | electric |
fuel_type | heat_type | sewer_type |
---|---|---|
electric | electric | private |
gas | hot water | private |
gas | hot water | public |
gas | hot air | private |
gas | hot air | public |
gas | hot air | private |
ordway_birds
데이터Rows: 15,829
Columns: 4
$ Timestamp <chr> "4/14/2010 13:20:56", "", "5/13/2010 16:00:30", "5/13/2010 1…
$ Year <chr> "1972", "", "1972", "1972", "1972", "1972", "1972", "1972", …
$ Month <chr> "7", "", "7", "7", "7", "7", "7", "7", "7", "7", "7", "7", "…
$ Day <chr> "16", "", "16", "16", "16", "16", "16", "16", "16", "16", "1…
Year
, Month
, Day
가 문자열parse_number()
ordway_birds <- ordway_birds %>%
mutate(
Month = parse_number(Month),
Year = parse_number(Year),
Day = parse_number(Day)
)
ordway_birds %>%
select(Timestamp, Year, Month, Day) %>%
glimpse()
Rows: 15,829
Columns: 4
$ Timestamp <chr> "4/14/2010 13:20:56", "", "5/13/2010 16:00:30", "5/13/2010 1…
$ Year <dbl> 1972, NA, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 19…
$ Month <dbl> 7, NA, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,…
$ Day <dbl> 16, NA, 16, 16, 16, 16, 16, 16, 16, 16, 17, 18, 18, 18, 18, …
""
)이 자동으로 NA
로 변환되는 방식에 유의날짜는 문자열로 기록되는 경우가 많음(예: 29 October 2014
).
ordway_birds
데이터에서 Timestamp
변수: 데이터가 원래의 실험 노트에서 컴퓨터 파일로 기록된 시간날짜의 중요한 속성: 자연스러운 순서. 16 December 2015
< 29 October 2016
문자열로 저장된 날짜가 주어지면 일반적으로 날짜를 위해 특별히 고안된 데이터 유형으로 변환해야 함 (lubridate
패키지)
lubridate::mdy_hms()
birds <- ordway_birds %>%
mutate(When = mdy_hms(Timestamp)) %>%
select(Timestamp, Year, Month, Day, When, DataEntryPerson)
birds %>%
glimpse()
Rows: 15,829
Columns: 6
$ Timestamp <chr> "4/14/2010 13:20:56", "", "5/13/2010 16:00:30", "5/13/…
$ Year <dbl> 1972, NA, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 19…
$ Month <dbl> 7, NA, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,…
$ Day <dbl> 16, NA, 16, 16, 16, 16, 16, 16, 16, 16, 17, 18, 18, 18…
$ When <dttm> 2010-04-14 13:20:56, NA, 2010-05-13 16:00:30, 2010-05…
$ DataEntryPerson <chr> "Jerald Dosch", "Caitlin Baker", "Caitlin Baker", "Cai…
month/day/year hour:minute:second
형태의 문자열을 날짜-시간형(dttm
)으로 변환first()
, last()
, interval()
lubridate
함수들: ymd()
, dmy()
, hour()
, yday()
DataEntryPerson | start | finish | duration |
---|---|---|---|
NA | NA | NA | |
Abby Colehour | 2011-04-23 15:50:24 | 2011-04-23 15:50:24 | 0.0000000 |
Brennan Panzarella | 2010-09-13 10:48:12 | 2011-04-10 21:58:56 | 209.4657870 |
Caitlin Baker | NA | 2010-05-28 19:41:52 | NA |
Emily Merrill | 2010-06-08 09:10:01 | 2010-06-08 14:47:21 | 0.2342593 |
Jerald Dosch | 2010-04-14 13:20:56 | 2010-04-14 13:20:56 | 0.0000000 |
Jolani Daney | 2010-06-08 09:03:00 | 2011-05-03 10:12:59 | 329.0485995 |
Keith Bradley-Hewitt | 2010-09-21 11:31:02 | 2011-05-06 17:36:38 | 227.2538889 |
Mary Catherine Muñiz | 2012-02-02 08:57:37 | 2012-04-30 14:06:27 | 88.2144676 |
내부적으로 R은 날짜-시간을 나타내기 위해 POSIXct
및 POSIXlt
두 클래스를 사용
대부분의 경우 이 두 클래스는 동일한 것으로 취급할 수 있지만 내부적으로는 서로 다르게 저장됨
POSIXct
객체는 UNIX 시대 (1970-01-01) 이후 경과한 시간을 초 단위로 저장POSIXlt
객체는 연도, 월, 일 등의 문자열 목록으로 저장List of 11
$ sec : num 33.7
$ min : int 37
$ hour : int 13
$ mday : int 9
$ mon : int 3
$ year : int 124
$ wday : int 2
$ yday : int 99
$ isdst : int 0
$ zone : chr "KST"
$ gmtoff: int 32400
- attr(*, "tzone")= chr [1:3] "" "KST" "KDT"
- attr(*, "balanced")= logi TRUE
Date
형 chr [1:2] "2021-04-29 06:00:00" "2021-12-31 12:00:00"
POSIXct[1:2], format: "2021-04-29 06:00:00" "2021-12-31 12:00:00"
요인형(factor
)는 범주형 데이터를 나타내는 데 사용되는 특별한 데이터형
부작용: 문자열로 잘못 인식하기 쉬움. 수치형이나 날짜 형식으로 변환할 때 다르게 동작
readr::read_csv()
는 문자열을 요인형이 아닌 문자열형(chr
)으로 해석
base::read.csv()
(R 4.0 이전)은 문자열을 기본적으로 요인형으로 변환
이러한 데이터를 정제하려면 종종 parse_character()
를 사용하여 다시 문자 형식으로 변환해야 함
부작용을 피하기 위해 교재에서 사용된 자료표는 범주형 또는 텍스트 데이터를 모두 문자열형으로 저장
Note
# 데이터 받아오기
tables <- "https://en.wikipedia.org/wiki/List_of_commercial_nuclear_reactors" %>%
read_html() %>%
html_nodes(css = "table")
# 'Fukushima Daiichi'가 포함된 표의 번호
idx <- tables %>%
html_text() %>%
str_detect("Fukushima Daiichi") %>%
which()
# 해당 표를 가져온 후 열 이름을 변경
reactors <- tables %>%
purrr::pluck(idx) %>%
html_table(fill = TRUE) %>%
janitor::clean_names() %>%
rename(
name = plantname,
reactor_type = type,
reactor_model = model,
capacity_net = capacity_mw,
construction_start = beginbuilding,
commercial_operation = commercialoperation,
closure = closed
) %>%
tail(-1)
Rows: 61
Columns: 9
$ name <chr> "Fukushima Daiichi", "Fukushima Daiichi", "Fukush…
$ unit_no <int> 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3…
$ reactor_type <chr> "BWR", "BWR", "BWR", "BWR", "BWR", "BWR", "BWR", …
$ reactor_model <chr> "BWR-3", "BWR-4", "BWR-4", "BWR-4", "BWR-4", "BWR…
$ status <chr> "Inoperable", "Inoperable", "Inoperable", "Inoper…
$ capacity_net <int> 439, 760, 760, 760, 760, 1067, 1067, 1067, 1067, …
$ construction_start <chr> "25 Jul 1967", "9 Jun 1969", "28 Dec 1970", "12 F…
$ commercial_operation <chr> "26 Mar 1971", "18 Jul 1974", "27 Mar 1976", "12 …
$ closure <chr> "19 May 2011", "19 May 2011", "19 May 2011", "19 …
mutate()
와 lubridate::dmy()
를 사용하여 랭글링reactors <- reactors %>%
mutate(
plant_status = ifelse(
str_detect(status, "Shut down"),
"Shut down", "Not formally shut down"
),
construct_date = dmy(construction_start),
operation_date = dmy(commercial_operation),
closure_date = dmy(closure)
)
glimpse(reactors)
Rows: 61
Columns: 13
$ name <chr> "Fukushima Daiichi", "Fukushima Daiichi", "Fukush…
$ unit_no <int> 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3…
$ reactor_type <chr> "BWR", "BWR", "BWR", "BWR", "BWR", "BWR", "BWR", …
$ reactor_model <chr> "BWR-3", "BWR-4", "BWR-4", "BWR-4", "BWR-4", "BWR…
$ status <chr> "Inoperable", "Inoperable", "Inoperable", "Inoper…
$ capacity_net <int> 439, 760, 760, 760, 760, 1067, 1067, 1067, 1067, …
$ construction_start <chr> "25 Jul 1967", "9 Jun 1969", "28 Dec 1970", "12 F…
$ commercial_operation <chr> "26 Mar 1971", "18 Jul 1974", "27 Mar 1976", "12 …
$ closure <chr> "19 May 2011", "19 May 2011", "19 May 2011", "19 …
$ plant_status <chr> "Not formally shut down", "Not formally shut down…
$ construct_date <date> 1967-07-25, 1969-06-09, 1970-12-28, 1973-02-12, …
$ operation_date <date> 1971-03-26, 1974-07-18, 1976-03-27, 1978-10-12, …
$ closure_date <date> 2011-05-19, 2011-05-19, 2011-05-19, 2011-05-19, …
실제로 원자로 용량은 시간이 지남에 따라 증가하는 경향이 있는 반면, 오래된 원자로는 공식적으로 폐쇄되었을 가능성이 더 높음
이 데이터는 수작업으로 코딩하는 것이 간단했지만, 더 크고 복잡한 테이블에 대해서는 데이터 수집을 자동화하는 것이 더 효율적이고 오류 발생 가능성이 적음