기술 통계 - 평균만 보면 틀리는 이유
이 장에서 배우는 것
엑셀에서 합계와 평균을 구해 본 사람은 많다. 그런데 평균 하나만 보고 "이 가게는 하루에 이만큼 판다"고 말하면 틀리는 경우가 있다. 이 장에서는 편의점 판매 기록으로 숫자 여러 개를 함께 보면서, 데이터의 중심과 흩어진 정도를 어떻게 읽는지 배운다. 범주별 개수를 세는 방법, 두 값이 함께 움직이는지 재는 방법, 데이터에서 일부를 무작위로 뽑고 그 결과를 다시 똑같이 재현하는 방법도 다룬다.
- 평균, 중앙값, 절사평균이 같은 데이터에서 왜 다르게 나오는지 설명하고 상황에 맞게 고른다.
- 분산, 표준편차, 분위수로 흩어진 정도를 재고, 사분위 범위로 이상값 후보를 찾는다.
table과prop.table로 범주별 개수와 비율을 구한다.cor로 상관계수를 구하고, 결측값이 있을 때의 옵션과 해석의 한계를 안다.sample과set.seed로 재현 가능한 표본을 뽑는다.
문제 상황
동네 편의점 사장님이 지난 열흘의 하루 매출을 엑셀에 적어 두었다. 대부분의 날은 4만 6천 원에서 5만 5천 원 사이였다. 그런데 열흘째에는 근처 모임에서 도시락과 음료를 한꺼번에 주문해 34만 9천 원이 찍혔다. 사장님이 열흘 평균을 내 보니 8만 원이었다. 이 숫자를 보고 "하루 평균 8만 원은 판다"고 생각해 알바 인력과 발주량을 잡으면, 열흘 중 아홉 날은 그 절반을 겨우 넘기는 매출에 맞춰 일하게 된다.
또 다른 고민도 있다. 영수증 품목이 수천 건 쌓였는데 전부 들여다볼 수는 없다. 음료가 전체 판매에서 얼마나 차지하는지 알고 싶고, 더운 날에 음료가 더 팔리는지도 확인하고 싶다. 일부만 뽑아서 살펴보려면 어떤 기준으로 뽑아야 하는지, 내일 다시 돌려도 같은 결과가 나오게 하려면 어떻게 해야 하는지도 정해야 한다. 이 장의 도구가 이 질문에 답한다.
중심을 재는 값: 평균과 중앙값
평균은 모든 값의 영향을 받는다
평균(mean)은 모든 값을 더해 개수로 나눈 값이다. 엑셀의 AVERAGE 와 같다. R 에서는 mean() 이다. 평균은 값 하나하나를 모두 반영하기 때문에, 유난히 큰 값 하나가 있으면 그쪽으로 끌려간다.
중앙값(median)은 값을 작은 것부터 세워 놓았을 때 한가운데 오는 값이다. 개수가 짝수이면 가운데 두 값의 평균을 쓴다. 열흘 매출에서는 다섯째 값 50과 여섯째 값 51의 평균인 50.5 가 중앙값이다. 349 가 400 이 되든 4000 이 되든 중앙값은 그대로다. 값의 크기가 아니라 순서만 쓰기 때문이다.
그림에서 평균선은 거의 모든 막대보다 위에 있다. 평균이 "전형적인 하루"를 가리키지 못하는 전형적인 모양이다. 중앙값선은 막대들의 윗면 근처를 지난다.
절사평균과 비교표
양 끝의 값 일부를 버리고 평균을 내는 방법도 있다. 이것이 절사평균이다. mean(daily, trim = 0.1) 은 작은 쪽 10% 와 큰 쪽 10% 를 버린다. 열 개 중 각 끝에서 하나씩, 곧 46 과 349 를 버리고 나머지 여덟 개로 평균을 낸다. 합이 405 이므로 50.625 다.
| 통계값 | 결과(천 원) | 349 의 영향 |
|---|---|---|
| 평균 | 80.0 | 크다 |
| 중앙값 | 50.5 | 없다 |
| 10% 절사평균 | 50.6 | 작다 |
| 표준편차 | 94.6 | 크다 |
| 사분위 범위 | 4.5 | 없다 |
어느 쪽이 옳다고 고르는 문제가 아니다. 모임 주문도 실제로 들어온 매출이므로, 한 달 총매출을 계산할 때는 평균이 맞는 도구다. 평소의 하루가 어떤지 말할 때는 중앙값이 낫다. 두 값이 크게 다르다는 사실 자체가 "값 몇 개가 튄다"는 신호이므로, 보고할 때는 둘을 나란히 쓰는 습관이 좋다.
흩어진 정도: 분산, 표준편차, 분위수
분산과 표준편차
중심이 같아도 값이 모여 있는지 퍼져 있는지는 다를 수 있다. 분산(variance)은 각 값이 평균에서 얼마나 떨어져 있는지를 제곱해서 모은 값이다. 제곱하는 이유는 평균보다 작은 값의 음수와 큰 값의 양수가 서로 지워지지 않게 하기 위해서다. 열흘 매출에서는 349 가 평균보다 269 큰데, 제곱하면 72,361 이 되어 나머지 아홉 값의 제곱 합 8,109 를 압도한다. 제곱합 80,470 을 9 로 나누면 분산 약 8,941.1 이다.
9 로 나누는 점에 주의한다. R 의 var() 와 sd() 는 개수 10 이 아니라 개수에서 1 을 뺀 값으로 나눈다. 가진 데이터가 더 큰 전체에서 뽑은 일부라고 보고, 일부의 평균을 기준으로 재면 퍼짐이 조금 작게 나오는 경향을 보정하는 방식이다. 엑셀의 VAR.S 와 같다.
분산의 단위는 원본 단위의 제곱이라 읽기가 불편하다. 천 원 단위 데이터의 분산은 "천 원의 제곱"이다. 제곱근을 씌운 표준편차(standard deviation)는 원래 단위로 돌아온다. 94.6 천 원은 "값들이 평균에서 대략 이 정도 떨어져 있다"는 뜻이다. 그런데 열흘 중 아홉 날은 평균 근처에 있지 않다. 표준편차도 평균처럼 튄 값 하나에 끌려가는 통계값이다.
분위수와 사분위 범위
분위수(quantile)는 값을 작은 것부터 줄 세웠을 때 앞에서 몇 퍼센트 지점에 오는 값이다. 50% 지점이 중앙값이고, 25% 와 75% 지점을 1사분위수와 3사분위수라고 부른다. 두 값 사이의 거리 IQR() 이 사분위 범위로, 가운데 절반의 데이터가 펼쳐진 폭이다.
R 의 quantile() 은 기본 설정에서 지점의 위치를 1 + (n - 1) × p 번째로 잡고, 위치가 정수가 아니면 이웃한 두 값을 비례해서 섞는다. 열 개 데이터의 25% 는 3.25 번째이므로 셋째 값 48 과 넷째 값 49 사이에서 4분의 1 만큼 간 지점인 48.25 다. 75% 는 7.75 번째이므로 52 와 53 사이의 52.75 다.
사분위 범위는 52.75 − 48.25 = 4.5 다. 흔히 쓰는 이상값(outlier) 후보 기준은 3사분위수에 사분위 범위의 1.5 배를 더한 값을 넘는지 보는 것이다. 52.75 + 6.75 = 59.5 를 넘는 값은 349 하나뿐이다. 기준은 관례일 뿐 정답이 아니다. 후보를 찾는 용도로만 쓰고, 지울지 남길지는 그날 무슨 일이 있었는지 확인해서 결정한다. 이 기준은 다음 장에서 그릴 상자 그림이 수염의 길이를 정할 때 쓰는 방식과 같다.
분위수 계산 방법은 여러 가지가 있고 type 인수로 고른다. 기본값은 위에서 설명한 방식이다. 자세한 정의는 quantile 도움말에서 확인할 수 있다.
세기, 관계 보기, 표본 뽑기
table 과 prop.table
숫자가 아니라 "음료", "과자" 같은 범주는 평균을 낼 수 없다. 대신 몇 건인지 센다. table() 은 값마다 개수를 세어 표로 돌려준다. 엑셀의 피벗 표에서 행에 분류를 놓고 개수를 센 결과와 같다. 세는 순서는 factor 의 수준(level) 순서를 따르므로, 원하는 순서가 있으면 factor 를 만들 때 levels 로 지정한다.
prop.table() 은 표의 각 칸을 전체 합으로 나눈다. 개수 8, 5, 4, 3 은 비율 0.40, 0.25, 0.20, 0.15 가 된다. 서로 다른 크기의 데이터를 비교할 때는 개수보다 비율이 낫다. 벡터 두 개를 table() 에 넣으면 교차표가 나오는데, 이때 prop.table(표, 1) 처럼 두 번째 인수를 주면 행 기준 비율이 된다.
개수만 보고 결론을 내리면 금액 구조를 놓친다. 그래서 분류별 금액은 tapply() 로 평균과 중앙값을 함께 구한다. 생활용품은 3건뿐이지만 9,000 원짜리 한 건 때문에 평균이 5,000 원으로 올라가고, 중앙값은 3,500 원이다. 여기서도 두 값의 차이가 튄 값을 알려 준다.
상관계수
상관계수(correlation coefficient)는 두 숫자가 함께 오르내리는 정도를 −1 에서 1 사이의 값으로 나타낸다. 1 에 가까우면 한쪽이 클 때 다른 쪽도 크고, −1 에 가까우면 한쪽이 클 때 다른 쪽이 작으며, 0 근처이면 직선에 가까운 관계가 없다. 기온과 음료 판매량처럼 짝지어진 숫자 두 벡터를 cor() 에 넣는다. 엑셀의 CORREL 과 같다.
기본값은 값 사이의 직선 관계를 재는 방식이다. method = "spearman" 을 주면 값 대신 순위를 써서, 곡선이라도 한 방향으로만 움직이면 1 이 나오고 튄 값의 영향도 줄어든다. 두 벡터 중 한쪽에 NA 가 있으면 결과가 NA 가 된다. use = "complete.obs" 를 주면 둘 다 값이 있는 쌍만 쓴다.
상관이 높다는 사실이 원인을 증명하지는 않는다. 기온이 오르면 음료가 더 팔리는 것으로 보이지만, 같은 데이터만으로는 행사나 요일 같은 제3의 요인을 배제할 수 없다. 상관계수는 "함께 움직인다"까지만 말해 준다.
표본 추출과 set.seed
전체 기록을 모집단(population), 그중에서 뽑은 일부를 표본(sample)이라고 한다. sample(nrow(sales), 5) 는 1 부터 행 수까지의 번호에서 중복 없이 다섯 개를 무작위로 뽑는다. 그 번호로 sales[번호, ] 처럼 행을 고르면 된다. 같은 항목이 다시 뽑힐 수 있게 하려면 replace = TRUE 를 준다.
컴퓨터의 난수는 실제로는 정해진 계산식이 이어 내는 수열이다. 이를 의사난수(pseudo-random number)라고 한다. 수열의 출발점이 시드(seed)이고, set.seed() 로 지정한다. 같은 시드에서 시작하면 같은 순서의 수가 나오므로, 분석 결과를 다른 사람이 같은 값으로 재현할 수 있다. 시드를 지정하지 않으면 실행할 때마다 다른 표본이 뽑힌다. 이 책은 출력이 결정적이어야 하므로 sample 을 부르기 전에 항상 시드를 정한다. 표본의 평균은 전체 평균과 같지 않고 뽑을 때마다 조금씩 다르다는 점도 기억해 둔다. 표본이 클수록 그 차이는 대체로 작아진다.
시드에서 나오는 수의 배열은 R 의 버전과 난수 설정에 따라 달라질 수 있다. 그래서 이 장의 코드는 뽑힌 번호 자체가 아니라 "같은 시드로 두 번 뽑으면 같은가", "개수와 중복 여부는 맞는가"만 출력해 확인한다. 자세한 동작은 sample 도움말에 있다.
tidyverse 대응표
이 책은 외부 패키지 없이 base R 만 쓴다. tidyverse 라는 패키지 모음을 쓰는 코드를 만나면 아래 표로 짝을 찾을 수 있다.
| 하는 일 | base R | tidyverse |
|---|---|---|
| 범주별 개수 | table(x) | count(df, x) |
| 범주별 평균 | tapply(v, g, mean) | group_by(g) 후 summarise(mean(v)) |
| 무작위 행 뽑기 | df[sample(nrow(df), 5), ] | slice_sample(df, n = 5) |
| 상관계수 | cor(x, y) | summarise(cor(x, y)) |
완성 코드
아래 코드를 main.R 한 파일로 저장한다. 데이터는 코드 안에서 만들고 외부 파일과 패키지는 쓰지 않는다.
# main.R - 편의점 판매 기록의 기술 통계
# 1. 데이터: 하루 매출(천 원) 10일치. 마지막 날은 행사 납품이 끼어 있다
daily <- c(52, 48, 50, 47, 55, 49, 51, 53, 46, 349)
# 영수증 품목 20건: 분류와 금액(원)
category <- factor(
rep(c("음료", "과자", "식품", "생활"), times = c(8, 5, 4, 3)),
levels = c("음료", "과자", "식품", "생활")
)
amount <- c(1500, 1800, 2000, 1500, 2500, 1800, 1500, 2000,
1500, 2000, 1800, 3000, 1700,
3500, 4500, 4000, 6000,
2500, 9000, 3500)
sales <- data.frame(category = category, amount = amount)
# 2. 중심을 재는 값
cat("== 중심 ==\n")
cat(sprintf("평균: %.1f\n", mean(daily)))
cat(sprintf("중앙값: %.1f\n", median(daily)))
cat(sprintf("10%% 절사평균: %.1f\n", mean(daily, trim = 0.1)))
# 3. 흩어진 정도
cat("== 흩어진 정도 ==\n")
cat(sprintf("범위: %.0f ~ %.0f\n", min(daily), max(daily)))
cat(sprintf("분산: %.1f\n", var(daily)))
cat(sprintf("표준편차: %.1f\n", sd(daily)))
q <- quantile(daily, probs = c(0.25, 0.5, 0.75))
cat(sprintf("사분위수: %.2f, %.2f, %.2f\n", q[1], q[2], q[3]))
upper <- q[3] + 1.5 * IQR(daily)
cat(sprintf("이상값 기준 상한: %.2f\n", upper))
cat(sprintf("상한을 넘는 값: %s\n", paste(daily[daily > upper], collapse = ", ")))
# 4. 개수와 비율
cat("== 개수와 비율 ==\n")
counts <- table(sales$category, dnn = "분류")
print(counts)
print(round(prop.table(counts), 2))
cat(sprintf("음료 비중: %.0f%%\n", 100 * as.numeric(prop.table(counts)["음료"])))
# 5. 분류별 금액
cat("== 금액 ==\n")
cat(sprintf("전체 평균 %.0f원, 중앙값 %.0f원\n", mean(amount), median(amount)))
means <- tapply(sales$amount, sales$category, mean)
meds <- tapply(sales$amount, sales$category, median)
cat(sprintf("%s: 평균 %.0f, 중앙값 %.0f\n", names(means), means, meds), sep = "")
# 6. 관계
cat("== 관계 ==\n")
temp <- c(10, 14, 18, 22, 26, 30)
cups <- c(12, 15, 21, 22, 30, 32)
cat(sprintf("기온-음료 판매량 상관계수: %.2f\n", cor(temp, cups)))
cat(sprintf("순위 상관계수: %.2f\n", cor(temp, cups, method = "spearman")))
cups_na <- c(12, 15, NA, 22, 30, 32)
cat(sprintf("NA 포함: %s\n", cor(temp, cups_na)))
cat(sprintf("결측 쌍 제외: %.2f\n", cor(temp, cups_na, use = "complete.obs")))
# 7. 표본 추출
cat("== 표본 추출 ==\n")
set.seed(2024)
pick1 <- sample(nrow(sales), 5)
set.seed(2024)
pick2 <- sample(nrow(sales), 5)
cat(sprintf("같은 시드의 결과가 같은가: %s\n", identical(pick1, pick2)))
cat(sprintf("표본 크기: %d, 중복 없음: %s\n", length(pick1), !anyDuplicated(pick1)))
cat(sprintf("뽑힌 행 수: %d\n", nrow(sales[pick1, ])))
줄별 해설
daily <- c(...): 열흘 매출을 천 원 단위로 담은 숫자 벡터다. 열 번째 값 349 가 행사 납품이 낀 날이다.category <- factor(rep(...), levels = ...):rep의times에 횟수를 벡터로 주면 음료 8번, 과자 5번, 식품 4번, 생활 3번을 이어 붙인다.levels를 직접 지정해 표에 나오는 순서를 고정한다. 지정하지 않으면 글자 정렬 순서가 되어 환경에 따라 달라질 수 있다.amount와data.frame: 분류 순서에 맞춰 금액 20개를 적고 두 열을 하나의 데이터 프레임으로 묶는다. 분류별 금액 개수가 8, 5, 4, 3 이 되도록 줄을 나눠 썼다.sprintf("평균: %.1f\n", ...):%.1f는 소수 첫째 자리까지 쓰라는 뜻이다.cat에 여러 값을 넘기면 사이에 공백이 들어가므로, 한 줄을sprintf로 완성한 뒤cat에 하나만 넘겼다.%%는 퍼센트 기호 자체를 뜻한다.mean(daily, trim = 0.1): 양 끝에서 10% 씩, 열 개 중 하나씩 버리고 평균을 낸다.var,sd: 제곱합을 개수에서 1 을 뺀 9 로 나눈다. 결과는 8941.1 과 94.6 이다.quantile(daily, probs = ...): 구하려는 지점을 0 과 1 사이의 비율로 준다. 결과는 이름이 붙은 벡터라서q[1],q[2],q[3]으로 꺼낸다.upper <- q[3] + 1.5 * IQR(daily): 3사분위수에 사분위 범위의 1.5 배를 더한 이상값 후보 기준이다.daily[daily > upper]는 기준을 넘는 값만 고르고,paste(..., collapse = ", ")는 그 값들을 문자열 하나로 잇는다.table(sales$category, dnn = "분류"): 분류별 개수를 센다.dnn은 표 위에 찍히는 변수 이름이다.prop.table은 각 칸을 합계 20 으로 나누고,round(..., 2)로 소수 둘째 자리까지 줄인다.as.numeric(prop.table(counts)["음료"]): 이름으로 음료 칸 하나를 꺼내 보통 숫자로 바꾼 뒤 100 을 곱해 퍼센트로 쓴다.tapply(sales$amount, sales$category, mean): 분류별로 금액을 나눠 함수를 적용한다. 평균과 중앙값을 각각 구하고,sprintf에 벡터를 그대로 넘겨 네 줄을 한 번에 만든다. 마지막의sep = ""는 줄 사이에 공백을 넣지 않도록 한다.cor(temp, cups): 기온과 음료 판매량의 상관계수다. 기온이 오를 때 판매량도 거의 일정하게 오르기 때문에 0.98 이 나온다.method = "spearman"은 순위의 상관이고, 두 벡터 모두 순서가 완전히 같아서 1.00 이 된다.cups_na: 셋째 날 판매량이 빠진 경우다. 그대로cor에 넣으면NA가 나오고,use = "complete.obs"를 주면 나머지 다섯 쌍으로 계산한다.set.seed(2024)와sample(nrow(sales), 5): 시드를 두 번 같은 값으로 맞추고 같은 방식으로 뽑아identical로 두 결과가 같은지 비교한다.anyDuplicated는 중복이 있으면 처음 중복된 위치를, 없으면 0 을 돌려주므로 앞에!를 붙이면 "중복 없음"이 된다.sales[pick1, ]는 뽑힌 번호의 행을 고른다.
실행 결과
터미널에서 main.R 이 있는 폴더로 이동해 다음 명령을 실행한다.
$ Rscript main.R
== 중심 ==
평균: 80.0
중앙값: 50.5
10% 절사평균: 50.6
== 흩어진 정도 ==
범위: 46 ~ 349
분산: 8941.1
표준편차: 94.6
사분위수: 48.25, 50.50, 52.75
이상값 기준 상한: 59.50
상한을 넘는 값: 349
== 개수와 비율 ==
분류
음료 과자 식품 생활
8 5 4 3
분류
음료 과자 식품 생활
0.40 0.25 0.20 0.15
음료 비중: 40%
== 금액 ==
전체 평균 2880원, 중앙값 2000원
음료: 평균 1825, 중앙값 1800
과자: 평균 2000, 중앙값 1800
식품: 평균 4500, 중앙값 4250
생활: 평균 5000, 중앙값 3500
== 관계 ==
기온-음료 판매량 상관계수: 0.98
순위 상관계수: 1.00
NA 포함: NA
결측 쌍 제외: 0.99
== 표본 추출 ==
같은 시드의 결과가 같은가: TRUE
표본 크기: 5, 중복 없음: TRUE
뽑힌 행 수: 5
표의 이름 줄과 숫자 줄 끝에는 공백이 하나씩 붙는다. R 이 이름 붙은 값을 찍는 방식이다. 전체 금액 평균 2,880 원과 중앙값 2,000 원의 차이도 생활용품의 9,000 원과 식품의 6,000 원 같은 비싼 품목이 평균을 끌어올린 결과다.
실무에서 자주 틀리는 것
평균 하나만 적어 보고한다
틀린 코드는 평균만 출력하고 "하루 평균 80(천 원)"이라고 기록한다.
daily <- c(52, 48, 50, 47, 55, 49, 51, 53, 46, 349)
cat("하루 평균 매출:", mean(daily), "\n")
평균 80 은 열흘 중 아홉 날보다 훨씬 크다. 중앙값을 곁들이고, 두 값이 크게 다르면 원인이 된 값을 확인한다.
cat(sprintf("평균 %.1f, 중앙값 %.1f\n", mean(daily), median(daily)))
cat("가장 큰 값:", max(daily), "\n")
NA 가 있는데 그대로 통계를 낸다
결측값이 하나라도 들어 있으면 mean, median, sd, quantile 대부분이 NA 를 돌려주거나 오류를 낸다. quantile 은 NA 가 있으면 오류를 낸다.
x <- c(52, 48, NA, 47)
mean(x)
quantile(x)
첫 줄은 NA 를 돌려주고, 둘째 줄은 오류로 멈춘다. na.rm = TRUE 로 빼고 계산하되, 몇 개를 뺐는지도 같이 확인한다. 결측이 많으면 통계값이 전체를 대표하지 못한다.
x <- c(52, 48, NA, 47)
mean(x, na.rm = TRUE)
quantile(x, na.rm = TRUE)
sum(is.na(x))
숫자 하나를 sample 에 넣는다
sample(x, 1) 에서 x 가 길이 1 인 숫자이면, R 은 그 숫자가 아니라 1 부터 그 숫자까지에서 뽑는다. 후보가 하나뿐이거나 후보가 줄어드는 상황에서 조용히 틀린 값이 나온다.
ids <- c(7)
sample(ids, 1)
위 코드는 7 이 아니라 1 에서 7 사이의 수 하나를 돌려준다. 번호가 아니라 위치를 뽑고 그 위치로 꺼내면 후보가 몇 개이든 안전하다.
ids <- c(7)
ids[sample(length(ids), 1)]
set.seed 를 뽑은 뒤에 부르거나 한 번만 부른다
시드는 그 뒤에 나오는 난수부터 영향을 준다. 뽑은 뒤에 시드를 지정하면 그 뽑기는 재현되지 않는다.
pick <- sample(20, 5)
set.seed(2024)
뽑기 직전에 시드를 지정한다. 스크립트에서 뽑기가 여러 번이면 첫 뽑기 앞에 한 번 지정하고, 그 스크립트 전체가 같은 순서로 실행된다는 점을 기억한다. 중간에 뽑기를 하나 추가하면 그 뒤의 결과가 모두 바뀐다. 뽑기마다 결과를 고정하고 싶으면 뽑기마다 시드를 지정한다.
set.seed(2024)
pick <- sample(20, 5)
한눈에 보기
| 함수 | 하는 일 | 튄 값의 영향 | 결측값이 있을 때 |
|---|---|---|---|
| mean | 평균 | 크다 | na.rm = TRUE |
| median | 중앙값 | 작다 | na.rm = TRUE |
| var, sd | 분산, 표준편차(n − 1 로 나눔) | 크다 | na.rm = TRUE |
| quantile, IQR | 분위수, 사분위 범위 | 작다 | na.rm = TRUE |
| table, prop.table | 개수, 비율 | 해당 없음 | 기본은 NA 를 세지 않음 |
| cor | 상관계수 | 기본 방식은 크다 | use = "complete.obs" |
| set.seed, sample | 재현 가능한 무작위 추출 | 해당 없음 | 해당 없음 |
연습 문제
daily의 마지막 값 349 를 60 으로 바꾼 벡터의 평균과 중앙값을 구하고, 원래 값과 비교하라.sales에서 금액이 3,000 원 이상인 품목이 분류별로 몇 건인지table로 구하라. 해당 품목이 없는 분류도 0 으로 나와야 한다.c(2, 4, 6, 8)의 분산과 표준편차를 손으로 계산하고,var와sd결과와 맞는지 확인하라.cups의 부호를 바꾼-cups와 기온의 상관계수, 그리고2 * cups + 5와 기온의 상관계수를 예상하고 확인하라.
정답과 해설
1번.
daily2 <- c(52, 48, 50, 47, 55, 49, 51, 53, 46, 60)
mean(daily2)
median(daily2)
합은 451 + 60 = 511 이므로 평균은 51.1 이고, 중앙값은 그대로 50.5 다. 튄 값이 사라지자 평균이 80 에서 51.1 로 크게 내려왔는데 중앙값은 움직이지 않았다. 이것이 중앙값이 튄 값에 강하다는 뜻이다.
2번.
table(sales$category[sales$amount >= 3000])
결과는 음료 0, 과자 1, 식품 4, 생활 2 이다. 음료의 최고 금액은 2,500 원이라 해당이 없다. category 가 factor 이고 수준이 네 개 모두 남아 있어서, 걸러낸 뒤에도 음료 칸이 0 으로 표시된다. 문자 벡터였다면 0 건인 분류는 표에서 사라진다.
3번. 평균은 5 이고 편차는 −3, −1, 1, 3 이다. 제곱하면 9, 1, 1, 9 이고 합은 20 이다. 개수 4 에서 1 을 뺀 3 으로 나누면 분산 6.67 이고, 제곱근인 표준편차는 2.58 이다.
v <- c(2, 4, 6, 8)
round(var(v), 2)
round(sd(v), 2)
4 로 나누면 분산 5 가 되는데, 이것은 var 가 내는 값이 아니다.
4번.
round(cor(temp, -cups), 2)
round(cor(temp, 2 * cups + 5), 2)
첫 결과는 −0.98 이고 둘째는 0.98 이다. 부호를 바꾸면 관계의 방향이 반대가 되어 부호만 뒤집힌다. 상수를 곱하고 더하는 것은 단위를 바꾸는 일이어서 상관계수는 변하지 않는다. 그래서 같은 관계를 천 원 단위로 재든 원 단위로 재든 상관계수는 같다.