ADsP NOTE검색

데이터 분석 / 6분 읽기

R mean 결과가 NA일 때, na.rm과 행 삭제는 어떻게 다른가요?

mean의 na.rm = TRUE는 이번 계산에서 결측값을 제외할 뿐 원본을 바꾸지 않습니다. 한 열만 제외하면 시간 평균이 약 43.33분, 두 열이 모두 채워진 행만 고르면 45분이 되는 예제로 계산 대상의 차이를 확인합니다.

평균에 NA가 나오면 빠진 값부터 확인합니다

R에서 숫자 벡터의 평균을 구했는데 NA가 나왔다면 결측값이 섞였는지 먼저 확인하세요. mean 함수의 기본값은 na.rm = FALSE라 결측값을 제외하지 않습니다. na.rm = TRUE를 지정하면 해당 계산에서 빠진 값을 제외하지만 원본의 결측값을 채우거나 데이터 전체의 행을 삭제하지는 않습니다.

score <- c(10, NA, 30)
mean(score)
mean(score, na.rm = TRUE)

첫 평균은 NA이며 두 번째는 10과 30의 평균인 20입니다. 두 번째 계산 후에도 score에는 10, NA, 30이 남습니다. 20을 결측값 자리에 넣었다는 해석은 틀립니다. 여기서는 숫자형 벡터와 기본 함수를 다루며 예제의 점수는 학습용으로 만든 값입니다.

is.na는 위치를 찾고 na.rm은 이번 계산에서 제외합니다

is.na는 각 값이 결측인지 검사해 TRUE 또는 FALSE를 돌려줍니다. 위 score에 적용하면 FALSE, TRUE, FALSE입니다. 결측값이 몇 개인지 알고 싶다면 이 결과의 TRUE 개수를 세면 됩니다.

is.na(score)
sum(is.na(score))
sum(!is.na(score))

결측값은 1개이고 관측된 값은 2개입니다. 느낌표는 논리값을 뒤집으므로 !is.na(score)는 빠지지 않은 위치를 표시합니다. 평균 20을 보고할 때 ‘3명 중 점수가 관측된 2명의 평균’이라고 적으면 실제 계산에 포함된 대상을 알 수 있습니다.

NA == NA로 결측값을 찾으려 하지 마세요. 알 수 없는 두 값이 같은지 비교한 결과 역시 NA입니다. 결측 여부를 묻는 is.na(NA)는 TRUE를 줍니다. 실제 숫자 0은 관측된 값이므로 is.na(0)는 FALSE입니다. 문자열 "NA"와 결측값 NA도 다르며 문자 두 개를 저장한 것인지 값이 빠진 것인지 구분해야 합니다.

함수 이름을 외울 때는 is.na는 어디가 비었나, na.rm은 이번 계산에서 뺄까로 역할을 나누세요. 둘 다 결측값을 다루지만 하나는 위치를 검사하고 다른 하나는 함수에 전달하는 처리 옵션입니다. 이름이 비슷하다고 같은 결과를 반환하는 함수로 묶지 않습니다.

행을 제외하면 평균을 구하는 대상도 달라집니다

가상의 학습 기록을 보겠습니다. 점수와 학습 시간이 서로 다른 행에서 빠져 있습니다. 학습 시간 평균만 필요한데 점수까지 채워진 행으로 제한하면 어떤 차이가 생길까요?

행점수 score학습 시간 minutes
A1030
BNA40
C30NA
D5060
d <- data.frame(
  score = c(10, NA, 30, 50),
  minutes = c(30, 40, NA, 60)
)
mean(d$minutes, na.rm = TRUE)
mean(d$minutes[complete.cases(d)])

첫 계산은 minutes 열에서만 빠진 값을 제외합니다. A, B, D의 30, 40, 60을 사용하므로 평균은 130/3, 약 43.33분입니다. 둘째 계산의 complete.cases(d)는 표의 두 열 모두 결측이 없는 행을 찾습니다. A와 D만 남아 30과 60의 평균인 45분을 구합니다.

complete.cases는 행을 직접 삭제하는 함수가 아닙니다. 어떤 행이 완전한지 나타내는 논리 벡터를 반환하고 이 예제에서는 대괄호에 그 벡터를 넣어 해당 행의 시간만 선택했습니다. 결과 숫자뿐 아니라 계산에 포함된 사람이 달라졌다는 점을 봐야 합니다.

같은 원자료를 두 방식으로 계산한 결과를 나란히 놓아 보겠습니다.

구할 평균필요한 열에서만 결측 제외두 열 모두 완전한 행 선택
학습 시간A, B, D의 3개 값, 약 43.33분A, D의 2개 값, 45분
점수A, C, D의 3개 값, 30점A, D의 2개 값, 30점

점수 평균은 우연히 같지만 사용한 행 수는 3개와 2개로 다릅니다. 평균만 비교하면 계산 대상이 바뀐 사실을 놓칩니다.

필요한 열을 정한 뒤 complete.cases를 적용합니다

분석에서 함께 쓸 두 변수의 짝을 맞춰야 한다면 두 열 모두 관측된 행을 고르는 방법을 검토할 수 있습니다. 점수와 시간의 관계만 볼 때 다른 열의 결측까지 조건에 넣으면 쓸 수 있는 행을 더 버릴 수 있습니다. 어떤 열에 complete.cases를 적용하는지 먼저 읽으세요.

ok <- complete.cases(d[c("score", "minutes")])
d[ok, ]

이 코드는 점수와 학습 시간이 모두 있는 행을 선택합니다. 열 이름과 결측 확인의 범위를 코드에 남겨 두면 나중에 다른 열이 추가돼도 제외 기준을 설명하기 쉽습니다. 원본 d는 그대로 두고 결과를 별도 이름으로 저장하면 처리 전후 행 수를 비교하기도 좋습니다.

다만 행을 선택했다고 결측 문제를 통계적으로 해결한 것은 아닙니다. 학습 시간이 짧은 사람에게 점수가 더 자주 빠졌다면 남은 사람의 평균이 전체를 잘 대표하지 않을 수 있습니다. 제외할지 값을 채울지 결정하기 전에 누가 어떤 이유로 빠졌는지 살펴야 합니다. 원인과 처리 내용을 기록하는 방법은 데이터 품질 점검에서 이어서 확인할 수 있습니다.

모두 빠진 자료에는 평균을 만들 값이 없습니다

mean(c(NA_real_, NA_real_), na.rm = TRUE)

숫자형 결측값 둘을 모두 제외하면 계산할 값이 남지 않아 결과는 NaN입니다. 이 결과를 0으로 바꾸어 표시하면 실제로 0을 관측한 자료와 구분하지 못합니다. 결측값을 제외한 뒤 남은 개수가 0인지 먼저 확인하고 ‘계산할 관측값 없음’처럼 상황을 전달해야 합니다.

평균을 기록할 때는 사용한 열과 결측 제외 기준, 남은 관측값 개수도 함께 적으세요. NA를 없애는 코드가 실행됐다는 사실과 분석 결과가 타당하다는 판단은 별개입니다.

핵심 정리와 실수 점검

핵심 정리

  • mean의 na.rm = TRUE는 해당 평균 계산에서 결측값을 제외하며 원본 값을 바꾸지 않습니다.
  • is.na는 값별 결측 위치를, complete.cases는 지정한 자료에서 결측이 없는 행을 표시합니다.
  • 분석별 제외와 완전한 행 선택은 사용하는 관측값이 달라질 수 있으므로 남은 개수도 기록합니다.

놓치기 쉬운 지점

  • NA == NA로 결측을 검사하거나 NA를 실제 0으로 바꾸어 해석하지 마세요.
  • complete.cases에 관계없는 열까지 넣고도 같은 분석 대상을 유지했다고 생각하지 마세요.

확인할 내용

  • is.na로 결측 위치와 개수를 확인한다.
  • 이번 분석에 필요한 열과 제외 기준을 먼저 정한다.
  • 처리 후 평균과 함께 남은 관측값 수, 제외한 이유를 기록한다.

이해한 내용 확인하기

직접 작성한 학습용 문항입니다. 공식 기출문제가 아닙니다.

Q1. x가 c(4, NA, 8)일 때 mean(x)와 mean(x, na.rm = TRUE)는 각각 무엇인가요?

생각한 뒤 해설 보기

각각 NA와 6입니다. 두 번째는 4와 8만 사용한 평균이며 x 자체를 바꾸지는 않습니다.

Q2. NA == NA와 is.na(NA)의 결과가 왜 다른가요?

생각한 뒤 해설 보기

NA == NA는 알 수 없는 두 값의 동일 여부를 비교하므로 NA입니다. is.na(NA)는 값이 결측으로 표시됐는지를 검사하므로 TRUE입니다.

Q3. 본문 표에서 minutes만 결측 제외한 평균을 구할 때 B행을 남기는 이유는 무엇인가요?

생각한 뒤 해설 보기

B행의 score는 빠져 있지만 이번 계산에 쓰는 minutes는 40으로 관측돼 있기 때문입니다. 남은 시간은 30, 40, 60이며 평균은 130/3입니다.

Q4. 본문 표에서 complete.cases(d)로 두 열이 모두 관측된 행만 고르면 어느 행과 몇 개의 값으로 시간 평균을 구하나요?

생각한 뒤 해설 보기

A와 D 두 행을 고릅니다. 시간 30과 60 두 개의 평균은 45입니다. complete.cases 자체는 행을 지우지 않고 TRUE/FALSE 벡터를 반환합니다.

Q5. mean(c(NA_real_, NA_real_), na.rm = TRUE)의 결과를 0점으로 기록해도 되나요?

생각한 뒤 해설 보기

안 됩니다. 결측값을 모두 제외하면 남은 값이 없어 평균은 NaN입니다. 실제 0점이 관측된 자료가 아니므로 계산할 관측값이 없다는 상태와 남은 개수 0을 기록합니다.

객관식 유형 연습 →

더 궁금한 점

na.rm = TRUE를 사용하면 결측값이 평균으로 채워지나요?

아닙니다. mean에서는 결측값을 빼고 평균을 계산할 뿐 원본 값을 변경하지 않습니다. 결측값을 어떤 값으로 채우는 대치와는 다른 처리입니다.

NaN도 is.na로 찾을 수 있나요?

숫자형 자료에서 is.na는 NA와 NaN 모두 TRUE로 표시합니다. NaN만 구분하려면 is.nan을 사용합니다. NA는 빠진 값 표시이고 NaN은 수치 계산 결과가 정의되지 않는 경우를 나타낼 수 있으므로 발생한 계산도 확인하세요.

참고 자료와 확인 범위
  • R 공식 문서: mean ↗

    na.rm 기본값 FALSE와 계산 전 결측값을 제외하는 옵션의 동작을 확인했습니다. 점수와 학습 시간 예제는 자체 제작했습니다.

  • R 공식 문서: NA와 is.na ↗

    결측값과 문자열 NA의 구분, is.na의 논리값 반환, 숫자형 NaN도 결측으로 표시하는 동작을 확인했습니다.

  • R 공식 문서: complete.cases ↗

    전달한 자료에서 결측이 없는 관측 행을 논리 벡터로 반환하는 동작을 확인했습니다. 원본 전체 삭제와 특정 변수의 계산별 제외는 구분했습니다.

함께 다듬는 노트

이해되지 않는 설명이 있었나요?

틀린 계산, 어려운 표현, 다음에 읽고 싶은 주제를 알려주세요.

의견 보내기