Menu

R에서 열 추가하고 변형하기 (mutate와 base R)

데이터 프레임의 열을 추가하고 변형하고 삭제하는 방법: df$new <- ..., 조건부 열을 만드는 ifelse(), transform(), 그리고 case_when()과 함께 쓰는 dplyr의 mutate().

이 페이지에는 실행 가능한 에디터가 있습니다 - 편집하고 실행하면 결과를 바로 볼 수 있습니다.

열 추가하기: df$new <- 값

base R에서 열을 추가하려면 아직 존재하지 않는 이름에 값을 할당합니다. 값은 보통 이미 있는 열로부터 계산합니다:

두 가지 일이 일어났습니다. orders$total은 벡터 전체를 받았습니다 - R의 산술 연산은 벡터화되어 있으므로 price * qty가 모든 행에 대해 한 번에 계산됩니다. orders$currency는 단일 값을 받았고, R이 이를 네 행 전체로 재활용(recycle) 했습니다. 그 밖의 길이는 오류가 됩니다(replacement has 3 rows, data has 4). 이는 R이 어긋난 열로부터 여러분을 보호하는 장치입니다.

이것이 가능한 이유는 데이터 프레임이 길이가 같은 열들의 리스트이기 때문입니다 - 이름 붙은 원소를 새로 할당하면 리스트가 커집니다. 데이터 프레임 문서에서 이 모델을 자세히 설명합니다.

조건부 열: ifelse()

조건에 따라 값이 달라지는 열에는 ifelse()가 적합합니다 - if벡터화된 사촌입니다. 조건 벡터 전체를 받아 행마다 하나의 값을 반환합니다:

평범한 if는 왜 안 될까요? if는 정확히 하나의 TRUE/FALSE만 평가하므로 열을 넘기면 오류(the condition has length > 1)가 납니다. ifelse(test, yes, no)는 각 원소를 검사해 행마다 값을 고릅니다. 결과가 셋 이상이면 중첩할 수 있습니다:

두 단계 중첩까지는 괜찮지만, 세 단계 이상이면 피라미드가 됩니다 - 바로 그 문제를 위해 아래의 dplyr case_when()이 만들어졌습니다. 단일 값을 다루는 if/else 자체는 if-else 문서에서 다룹니다.

여러 열을 한 번에: transform()

base R의 transform()은 열 이름을 그대로 써서 한 번의 호출로 여러 열을 추가합니다:

transform() 데이터 프레임을 반환한다는 점에 유의하세요 - 결과를 다시 할당해야 합니다. 반면 df$new <- ...는 제자리에서 수정합니다. 한 가지 제약이 있습니다: 한 번의 transform() 호출 안에 있는 표현식들은 서로를 볼 수 없으므로 discount = ifelse(total > 50, ...)는 실패합니다(total이 아직 존재하지 않습니다). 두 단계로 나누거나, 이를 허용하는 mutate()를 쓰세요.

수정 vs 생성

동일한 할당 문법이 기존 열을 덮어쓰기도 합니다 - 그 열이 이미 있었느냐가 유일한 차이입니다:

여기서 한 번 멈춰 생각할 만합니다: 열을 덮어써도 R은 아무 경고를 주지 않습니다. df$prise <- round(df$price, 2) 같은 오타는 기존 열을 고치는 대신 조용히 새 열을 생성 합니다. 변형 후에는 print()str()로 데이터 프레임을 확인해 의도한 대로 바뀌었는지 검증하세요.

열 삭제하기

NULL을 할당하면 삭제됩니다:

별다른 절차 없이 열이 사라집니다. 여러 개를 삭제할 때는 NULL을 반복하는 것보다 df[, c("a", "b")](남길 열을 나열)가 대개 더 명확합니다.

dplyr 방식: mutate()

위의 모든 작업에 해당하는 dplyr 동사는 mutate()입니다. 아래 예제는 정적입니다(샌드박스는 base R만 실행합니다). 하지만 같은 작업이 어떤 모습인지 보여 줍니다:

library(dplyr)

orders |>
    mutate(
        total    = price * qty,
        big      = total > 50        # can use total, defined one line up
    )

base R 대비 두 가지 장점이 있습니다. 첫째, 같은 mutate() 안에서 정의한 열들은 순서대로 서로를 참조할 수 있습니다 - transform()이 거부하는 바로 그 기능입니다. 둘째, 조건 처리 헬퍼가 중첩된 ifelse()보다 낫습니다:

students |>
    mutate(
        grade = if_else(score >= 60, "pass", "fail"),
        band  = case_when(
            score >= 85 ~ "high",
            score >= 60 ~ "mid",
            .default    = "low"
        )
    )

if_else()는 더 엄격한 ifelse()입니다(두 분기의 타입이 같아야 하므로 실제 버그를 잡아 줍니다). case_when()은 위에서 아래로 검사되는 조건 ~ 값 쌍의 평평한 목록처럼 읽히고 .default가 대비책이 됩니다 - ifelse() 피라미드를 대체하는 읽기 좋은 방법입니다. 모든 dplyr 동사가 그렇듯 mutate()도 새 데이터 프레임을 반환합니다. 나머지 동사와 어떻게 연결되는지는 dplyr 입문을 참고하세요.

핵심 정리

  • df$new <- 표현식으로 열을 추가하며, 표현식은 모든 행에 대해 한 번에 실행됩니다.
  • ifelse(test, yes, no)는 조건부 열을 만듭니다. 평범한 if로는 불가능합니다.
  • transform()은 한 번에 여러 열을 추가하지만 서로를 볼 수 없고, mutate()는 가능합니다.
  • 동일한 할당이 조용히 덮어쓰므로, 변형 후에는 철자를 확인하세요.
  • df$col <- NULL은 열을 삭제하고, case_when()은 중첩된 ifelse() 피라미드를 대체합니다.

다음 주제: 열 이름 바꾸기 - names(), 견고한 base R 관용구, 그리고 dplyr의 rename().

자주 묻는 질문

R에서 데이터 프레임에 새 열을 어떻게 추가하나요?

아직 존재하지 않는 열 이름에 값을 할당합니다: df$total <- df$price * df$qty. R이 그 자리에서 열을 만들어 줍니다. 값은 단일 값(모든 행으로 재활용됨)일 수도 있고 다른 열로부터 계산한 벡터일 수도 있는데, 길이가 행 수와 맞아야 합니다.

R에서 조건부 열은 어떻게 만드나요?

벡터화된 ifelse()를 사용합니다: df$grade <- ifelse(df$score >= 60, "pass", "fail")는 모든 행을 한 번에 검사합니다. 평범한 if는 쓰지 마세요 - 하나의 조건만 처리하며 열 전체를 다루지 못합니다. 결과가 셋 이상이면 ifelse()를 중첩하거나 dplyr의 case_when()을 쓰세요.

R에서 mutate()는 무엇을 하나요?

mutate()는 열을 추가하거나 변형하는 dplyr의 동사입니다: df |> mutate(total = price * qty)는 새 열이 추가된 데이터 프레임 사본을 반환합니다. 한 번의 호출로 여러 열을 정의할 수 있고, 뒤쪽 열은 같은 mutate 안에서 앞서 정의한 열을 사용할 수 있습니다.

R에서 데이터 프레임의 열을 어떻게 삭제하나요?

NULL을 할당합니다: df$notes <- NULL은 그 자리에서 열을 제거합니다. dplyr에서는 select(-notes)가 해당 열이 빠진 사본을 반환합니다.

Coddy programming languages illustration

Coddy로 코딩 배우기

시작하기