먼저 이름을 확인하기: names()와 colnames()
열 이름은 다른 벡터처럼 읽을 수 있는 속성에 들어 있습니다. 데이터 프레임에서는 names()와 colnames() 둘 다 이를 반환합니다:
결과가 같습니다. 진짜 차이는 하나뿐입니다: colnames()는 행렬에서도 동작하지만 names()는 그렇지 않습니다 - 그래서 범용 코드는 colnames()를 씁니다. 일상적인 데이터 프레임에서는 서로 바꿔 써도 됩니다.
아래 내용 전체를 관통하는 핵심은 이것입니다: 열 이름을 바꾸는 것은 이 벡터에 값을 할당하는 것입니다. 전용 base 함수는 없습니다. 다른 벡터와 똑같은 인덱싱으로 names(df)를 수정하면 됩니다.
위치로 이름 바꾸기 (동작하지만 취약함)
이름 벡터를 위치로 인덱싱해 할당합니다:
이제 2번 열이 price입니다. 대화형으로 빠르게 고칠 때는 괜찮지만 스크립트에서는 함정입니다. 위치는 열 순서에 대한 약속이고, 열 순서는 임포트한 데이터에서 가장 불안정한 요소입니다. 읽어 들인 CSV에 열이 하나 더 생기는 날 names(df)[2]는 조용히 엉뚱한 열의 이름을 바꿉니다. 스크립트가 위치로 이름을 바꾸면 스키마 변경이 오류 메시지 없이 그것을 망가뜨립니다.
이름으로 이름 바꾸기 (견고한 base R 관용구)
위치를 믿는 대신 옛 이름을 매칭합니다:
안쪽부터 읽으세요: names(sales) == "prc"는 FALSE TRUE FALSE를 만듭니다 - 이름 벡터 위의 논리 마스크입니다. 이것으로 인덱싱하면 일치하는 항목이 선택되고, 할당이 그 값을 교체합니다. "prc"가 없다면 아무것도 매칭되지 않고 아무것도 바뀌지 않습니다(오류도 없으니 결과를 확인하세요). 이 관용구는 열 순서 변경, 열 추가, 다른 스크립트로의 복사·붙여넣기에도 견딥니다. 외워 두세요. "열 이름 바꾸기"에 대한 base R의 답입니다.
여러 개를 한 번에
모든 열의 이름을 붙일 때는(임포트 직후에 흔합니다) 벡터 전체를 교체하고, 일부만 바꿀 때는 match()를 사용합니다:
match(old, names(df))는 옛 이름들의 위치를 반환하므로 새 이름이 정확히 옛 이름 자리에 놓입니다 - 단일 이름 관용구처럼 순서에 안전합니다. 옛 이름 중 하나라도 없으면 match()가 NA를 반환하고 할당이 오류를 냅니다. 바로 우리가 원하는 실패 방식입니다: 시끄러운 실패죠.
파이프라인을 위한 setNames()
위의 관용구들은 모두 두 단계로 변수를 변형합니다. setNames(object, names)는 이름이 바뀐 사본 을 한 표현식으로 반환하는데, 파이프라인에 필요한 형태입니다:
어떤 함수가 이름이 없거나 잘못 붙은 데이터 프레임을 반환할 때 임시 변수 없이 그 자리에서 고치고 싶다면 유용합니다 - read_something() |> setNames(c("id", "value"))처럼요.
dplyr 방식: rename()과 rename_with()
dplyr의 rename()은 new = old 쌍을 받습니다 - 새 이름이 먼저 이며, 누구나 한 번쯤 거꾸로 쓰게 됩니다(정적 예제입니다. 샌드박스는 base R만 실행합니다):
library(dplyr)
sales |> rename(price = prc, qty = q)
호출에서 언급하지 않은 이름은 그대로 유지되며, 옛 이름의 철자가 틀리면 조용히 무시되는 대신 명백한 오류가 납니다 - base R 관용구 대비 진짜 개선점입니다. 쌍이 아니라 규칙 으로 이름을 바꾸려면 rename_with()가 이름들에 함수를 적용합니다:
sales |> rename_with(toupper) # every column
sales |> rename_with(toupper, starts_with("p")) # just some
이 함수들이 동사 가족 안에서 어떤 자리를 차지하는지는 dplyr 입문을 참고하세요.
지저분한 임포트 헤더 정리하기
실제 CSV 헤더는 "Order ID", "unit.price ($)", "2024 Total"처럼 들어옵니다 - 공백과 기호가 섞여 있어 어디서나 백틱으로 감싸야 하는 이름들이죠. base R의 make.names()는 임의의 문자 벡터를 유효한 R 이름으로 변환합니다:
유효하기는 하지만 나름대로 볼썽사납습니다(X2024.Total). 그래서 많은 팀이 janitor 패키지의 clean_names()를 표준으로 씁니다. 한 번의 호출로 깔끔한 snake_case(order_id, unit_price, x2024_total)를 만들어 줍니다: df |> janitor::clean_names(). 매주 지저분한 파일을 임포트한다면 곧바로 값어치를 합니다.
핵심 정리
- 열 이름은 그저 벡터입니다:
names()/colnames()로 읽고, 거기에 할당해 이름을 바꿉니다. names(df)[2] <- "new"는 취약합니다. 외워야 할 관용구는names(df)[names(df) == "old"] <- "new"입니다.match()는 여러 개를 이름으로 바꾸고,setNames()는 파이프라인 안에서 바로 이름을 바꿉니다.- dplyr의
rename(new = old)는 없는 이름에 오류를 내고(좋은 점),rename_with()는 규칙으로 이름을 바꿉니다. - 지저분한 임포트 헤더에는 base R의
make.names(), 예쁜 결과를 원하면janitor::clean_names()를 쓰세요.
다음 주제: 정렬 - 벡터를 위한 sort(), 데이터 프레임을 위한 order() 기법, 그리고 arrange().
자주 묻는 질문
R에서 열 이름을 어떻게 바꾸나요?
가장 견고한 base R 관용구는 이름으로 매칭하는 것입니다: names(df)[names(df) == "old"] <- "new". old라는 열이 어디에 있든 찾아서 이름을 바꿉니다. dplyr에서는 rename(df, new = old)입니다 - 순서에 주의하세요. 새 이름이 먼저입니다.
names()와 colnames()의 차이는 무엇인가요?
데이터 프레임에서는 실질적인 차이가 없습니다 - 둘 다 열 이름을 읽고 설정합니다. colnames()는 행렬에서도 동작하지만(names()는 안 됩니다) 둘 다 처리해야 하는 코드는 보통 colnames()를 씁니다. 평범한 데이터 프레임에서는 취향대로 쓰면 됩니다.
R에서 위치로 열 이름을 바꾸려면 어떻게 하나요?
이름 벡터의 해당 위치에 할당합니다: names(df)[2] <- "price"는 두 번째 열의 이름을 바꿉니다. 동작하기는 하지만 취약합니다 - 열이 추가되거나 CSV의 열 순서가 바뀌는 날 2번 위치는 다른 열이 됩니다. 이름으로 바꾸는 편을 권합니다.
R에서 여러 열 이름을 한 번에 바꾸려면 어떻게 하나요?
벡터 전체를 할당합니다: names(df) <- c("id", "name", "price")는 모든 이름을 순서대로 교체합니다. 선택한 몇 개만 이름으로 바꾸려면 match()를 씁니다: names(df)[match(c("old1", "old2"), names(df))] <- c("new1", "new2"). dplyr에서는 rename(df, new1 = old1, new2 = old2)입니다.