필드 중 하나에 일반적으로 7-8 개의 값이있는 데이터 프레임에 변수가 있습니다. 데이터 프레임 내의 새 변수 내에서 3 개 또는 4 개의 새 범주를 축소하고 싶습니다. 가장 좋은 방법은 무엇입니까?
SQL과 같은 도구를 사용했지만 R에서 이것을 공격하는 방법을 잘 모르겠다면 CASE 문을 사용합니다.
당신이 제공 할 수있는 어떤 도움이라도 대단히 감사 할 것입니다!
답변:
case_when()
2016 년 5 월에 dplyr에 추가 된은 memisc::cases()
.
예를 들면 :
library(dplyr)
mtcars %>%
mutate(category = case_when(
.$cyl == 4 & .$disp < median(.$disp) ~ "4 cylinders, small displacement",
.$cyl == 8 & .$disp > median(.$disp) ~ "8 cylinders, large displacement",
TRUE ~ "other"
)
)
dplyr 0.7.0부터,
mtcars %>%
mutate(category = case_when(
cyl == 4 & disp < median(disp) ~ "4 cylinders, small displacement",
cyl == 8 & disp > median(disp) ~ "8 cylinders, large displacement",
TRUE ~ "other"
)
)
.$
각 열 앞에는 필요하지 않습니다 .
.$
더 이상 필요하지 않습니다. 이 답변이 원래 작성되었을 때 그랬습니다.
switch
케이스에 대한 키 대신 일련의 표현식을 만들 수 있기 때문 입니다.
패키지 의 cases
기능을 살펴보십시오 memisc
. 사용하는 두 가지 방법으로 케이스 기능을 구현합니다. 패키지의 예에서 :
z1=cases(
"Condition 1"=x<0,
"Condition 2"=y<0,# only applies if x >= 0
"Condition 3"=TRUE
)
여기서, x
그리고 y
두 개의 벡터이다.
참조 : memisc 패키지 , 사례 예
그렇다면 factor
표준 방법으로 수준을 변경할 수 있습니다.
df <- data.frame(name = c('cow','pig','eagle','pigeon'),
stringsAsFactors = FALSE)
df$type <- factor(df$name) # First step: copy vector and make it factor
# Change levels:
levels(df$type) <- list(
animal = c("cow", "pig"),
bird = c("eagle", "pigeon")
)
df
# name type
# 1 cow animal
# 2 pig animal
# 3 eagle bird
# 4 pigeon bird
간단한 함수를 래퍼로 작성할 수 있습니다.
changelevels <- function(f, ...) {
f <- as.factor(f)
levels(f) <- list(...)
f
}
df <- data.frame(name = c('cow','pig','eagle','pigeon'),
stringsAsFactors = TRUE)
df$type <- changelevels(df$name, animal=c("cow", "pig"), bird=c("eagle", "pigeon"))
x
마지막 줄에 changelevels
있는가?
switch
문을 사용하는 방법은 다음과 같습니다 .
df <- data.frame(name = c('cow','pig','eagle','pigeon'),
stringsAsFactors = FALSE)
df$type <- sapply(df$name, switch,
cow = 'animal',
pig = 'animal',
eagle = 'bird',
pigeon = 'bird')
> df
name type
1 cow animal
2 pig animal
3 eagle bird
4 pigeon bird
이것의 한 가지 단점은 animal
각 항목에 대해 카테고리 이름 ( 등) 을 계속 작성해야한다는 것 입니다. 아래와 같이 범주를 정의 할 수있는 것이 구문 적으로 더 편리합니다 ( R의 데이터 프레임에 열을 추가하는 방법 과 매우 유사한 질문 참조 )
myMap <- list(animal = c('cow', 'pig'), bird = c('eagle', 'pigeon'))
어떻게 든이 매핑을 "반전"하고 싶습니다. 내 자신의 invMap 함수를 작성합니다.
invMap <- function(map) {
items <- as.character( unlist(map) )
nams <- unlist(Map(rep, names(map), sapply(map, length)))
names(nams) <- items
nams
}
그런 다음 위의지도를 다음과 같이 반전합니다.
> invMap(myMap)
cow pig eagle pigeon
"animal" "animal" "bird" "bird"
그리고 이것을 사용 type
하여 데이터 프레임에 열 을 추가하는 것은 쉽습니다 .
df <- transform(df, type = invMap(myMap)[name])
> df
name type
1 cow animal
2 pig animal
3 eagle bird
4 pigeon bird
Imho, 가장 간단하고 보편적 인 코드 :
dft=data.frame(x = sample(letters[1:8], 20, replace=TRUE))
dft=within(dft,{
y=NA
y[x %in% c('a','b','c')]='abc'
y[x %in% c('d','e','f')]='def'
y[x %in% 'g']='g'
y[x %in% 'h']='h'
})
y = 'else'
. 추가 조건을 충족하지 않는 요소는 변경되지 않습니다.
거기에있다 switch
문하지만 나는 내가 그것을해야한다 생각하는 방식으로 작동 얻을 수가 없다. 예제를 제공하지 않았으므로 요인 변수를 사용하여 하나를 만들 것입니다.
dft <-data.frame(x = sample(letters[1:8], 20, replace=TRUE))
levels(dft$x)
[1] "a" "b" "c" "d" "e" "f" "g" "h"
재 할당에 적합한 순서로 원하는 범주를 지정하는 경우 요인 또는 숫자 변수를 인덱스로 사용할 수 있습니다.
c("abc", "abc", "abc", "def", "def", "def", "g", "h")[dft$x]
[1] "def" "h" "g" "def" "def" "abc" "h" "h" "def" "abc" "abc" "abc" "h" "h" "abc"
[16] "def" "abc" "abc" "def" "def"
dft$y <- c("abc", "abc", "abc", "def", "def", "def", "g", "h")[dft$x] str(dft)
'data.frame': 20 obs. of 2 variables:
$ x: Factor w/ 8 levels "a","b","c","d",..: 4 8 7 4 6 1 8 8 5 2 ...
$ y: chr "def" "h" "g" "def" ...
나중에 두 가지 다른 스위치 기능이 있다는 것을 알게되었습니다. 일반적인 기능은 아니지만 switch.numeric
또는 로 생각해야합니다 switch.character
. 첫 번째 인수가 R '인자'인 switch.numeric
경우 대부분의 사람들은 요인이 문자로 표시되고 모든 함수가이를 처리 할 것이라는 잘못된 가정을하기 때문에 문제를 일으킬 가능성 이있는 행동이 발생합니다.
자동차 패키지에서 레코딩을 사용할 수 있습니다.
library(ggplot2) #get data
library(car)
daimons$new_var <- recode(diamonds$clarity , "'I1' = 'low';'SI2' = 'low';else = 'high';")[1:10]
sos::findFn("recode")
발견은 doBy::recodeVar
, epicalc::recode
, memisc::recode
,하지만 난 ... 자세히 그들을 못 봤어
나는 이것들 중 어느 것도 좋아하지 않으며 독자 또는 잠재적 인 사용자에게 명확하지 않습니다. 저는 익명의 함수를 사용합니다. 구문은 case 문처럼 매끄럽지 않지만 평가는 case 문과 비슷하며 그렇게 고통스럽지 않습니다. 이것은 또한 변수가 정의 된 위치 내에서 평가한다고 가정합니다.
result <- ( function() { if (x==10 | y< 5) return('foo')
if (x==11 & y== 5) return('bar')
})()
이 모든 ()는 익명 함수를 묶고 평가하는 데 필요합니다.
result <- (if (x==10 | y< 5) 'foo' else if (x==11 & y== 5) 'bar' )
. 경우 2)에만 작동 x
및 y
스칼라는; 벡터의 경우 원래 질문에서와 같이 중첩 된 ifelse
문이 필요합니다.
나는 당신이 언급하는 경우에 사용하고 있습니다 switch()
. 제어문처럼 보이지만 실제로는 함수입니다. 식이 평가되고이 값에 따라 목록의 해당 항목이 반환됩니다.
switch는 첫 번째 인수가 문자열인지 숫자인지에 따라 두 가지 다른 방식으로 작동합니다.
다음은 이전 범주를 새 범주로 축소하는 문제를 해결하는 간단한 문자열 예제입니다.
문자열 형식의 경우 명명 된 값 뒤에 기본값으로 명명되지 않은 단일 인수가 있습니다.
newCat <- switch(EXPR = category,
cat1 = catX,
cat2 = catX,
cat3 = catY,
cat4 = catY,
cat5 = catZ,
cat6 = catZ,
"not available")
사례 진술은 실제로 여기서 올바른 접근 방식이 아닐 수 있습니다. 이것이 요인 일 가능성이있는 경우 요인 수준을 적절하게 설정하십시오.
A부터 E까지의 요소가 있다고 가정 해 보겠습니다.
> a <- factor(rep(LETTERS[1:5],2))
> a
[1] A B C D E A B C D E
Levels: A B C D E
레벨 B와 C를 결합하고 이름을 BC로 지정하려면 해당 레벨의 이름을 BC로 변경하십시오.
> levels(a) <- c("A","BC","BC","D","E")
> a
[1] A BC BC D E A BC BC D E
Levels: A BC D E
결과는 원하는대로입니다.
나를 위해 혼합 plyr::mutate
하고 dplyr::case_when
작동하며 읽을 수 있습니다.
iris %>%
plyr::mutate(coolness =
dplyr::case_when(Species == "setosa" ~ "not cool",
Species == "versicolor" ~ "not cool",
Species == "virginica" ~ "super awesome",
TRUE ~ "undetermined"
)) -> testIris
head(testIris)
levels(testIris$coolness) ## NULL
testIris$coolness <- as.factor(testIris$coolness)
levels(testIris$coolness) ## ok now
testIris[97:103,4:6]
열이 char 대신 요인으로 mutate에서 나올 수 있다면 보너스 포인트! 일치하지 않는 모든 행을 포착하는 case_when 문의 마지막 줄은 매우 중요합니다.
Petal.Width Species coolness
97 1.3 versicolor not cool
98 1.3 versicolor not cool
99 1.1 versicolor not cool
100 1.3 versicolor not cool
101 2.5 virginica super awesome
102 1.9 virginica super awesome
103 2.1 virginica super awesome
케이스 스타일 다시 매핑 작업에 base
함수 merge
를 사용할 수 있습니다 .
df <- data.frame(name = c('cow','pig','eagle','pigeon','cow','eagle'),
stringsAsFactors = FALSE)
mapping <- data.frame(
name=c('cow','pig','eagle','pigeon'),
category=c('mammal','mammal','bird','bird')
)
merge(df,mapping)
# name category
# 1 cow mammal
# 2 cow mammal
# 3 eagle bird
# 4 eagle bird
# 5 pig mammal
# 6 pigeon bird
data.table v1.13.0 부터는 함수 fcase()
(빠른 경우)를 사용하여 SQL과 유사한 CASE
작업 을 수행 할 수 있습니다 (와 유사 dplyr::case_when()
).
require(data.table)
dt <- data.table(name = c('cow','pig','eagle','pigeon','cow','eagle'))
dt[ , category := fcase(name %in% c('cow', 'pig'), 'mammal',
name %in% c('eagle', 'pigeon'), 'bird') ]
dput()
b) base R, dplyr, data.table, tidyverse ...를 사용하여 예제 데이터 스 니펫을 게시하십시오 .