다중 선택 설문 조사 질문을 분할하기 위해 티블 내부에 여러 티블을 중첩 해제
설문 조사의 '다중 응답'질문을 별도의 열로 확장하기위한 프로그래밍 솔루션을 생성하려고합니다. 설정에는 측량 데이터 (df1)와 변수를 변수에 대한 정보와 연결하는 도우미 파일이 포함됩니다. 아래의 샘플 데이터를 사용하여 목표는 DVar 및 EVar의 응답을 DVar.A, DVar.b 등과 같은 별도의 열로 확장하는 것입니다. 해당 ID가 해당 상자를 선택했는지 여부에 관계없이 바이너리 1,0을 사용합니다.
df1 <- tibble(ID = rep(1:8), AVar = sample(1:10, 8), BVar = rnorm(8),
CVar = c("Got", "Some", "Stuff", "In", "Here", "Got", "Others", "Too"),
DVar = c("A,B", NA , "C", "A,C", "B,D", "C", "D", "B,D"),
EVar = c("Banana,Apple", "Orange,Raspberry", "Apple", NA, "Orange", "Banana", "Banana", "Raspberry"))
Helper <- tibble(VariableName = c("ID", "AVar", "BVar", "CVar", "DVar", "EVar"),
QuestionType = c("ID", "Numeric", "Numeric", "Single Response", "Multiple Response", "Multiple Response"))
현재 작업 함수는 확산 할 ID와 열을받습니다. 내 현재 목적을 위해이 기능은 훌륭하게 작동합니다. 열에 NA가없는 경우 (비정상적 임)를 제외하고 최종 select 문에서 데이터 세트에없는 'None'에 대한 오류가 발생합니다.
MultiToCol <- function(ID, toSpread) {
X <- tibble(ID, toSpread)
X %>% mutate(varLong = strsplit(as.character(replace_na(toSpread, "None")),split=",")) %>%
unnest(varLong) %>% mutate(tmpValue = 1) %>% spread(varLong, tmpValue, fill = 0) %>% select(-None, -ID, -toSpread, None)
}
mutate (across)를 사용하면 필요한 데이터를 다시 가져올 수 있으며, 그런 다음 전체 데이터 세트에 다시 결합됩니다 (또는 예제에있을 수 있음).
getCols <- Helper %>% filter(QuestionType == "Multiple Response") %>% select(VariableName)
spreadCols <- df1 %>% select_if(names(.) %in% c('ID', getCols$VariableName)) %>%
mutate(across(.cols = !ID, .fns = ~MultiToCol1(ID,.)))
데이터를 볼 때 rstudio는 내가 원하는 것을 제공합니다!
ID DVar.A DVar.B DVar.C DVar.D DVar.None EVar.Apple EVar.Banana EVar.Orange EVar.Raspberry Evar.None
1 1 1 0 0 0 1 1 0 0 0
2 0 0 0 0 1 0 0 1 1 0
3 0 0 1 0 0 1 0 0 0 0
⋮
그러나 데이터를 쓸 때 일치하지 않는 차원에 대한 오류가 발생합니다. 이는 결과 데이터 구조가 열이 (Int, Tibble, Tibble) 인 8x3 tibble이기 때문입니다. 그리고 내부 Tibbles가 조옮김 한 것 같습니다.
tibble [8 x 3] (S3: tbl_df/tbl/data.frame)
$ ID : int [1:8] 1 2 3 4 5 6 7 8
$ DVar: tibble [8 x 5] (S3: tbl_df/tbl/data.frame) ..$ A : num [1:8] 1 0 0 1 0 0 0 0
..$ B : num [1:8] 1 0 0 0 1 0 0 1 ..$ C : num [1:8] 0 0 1 1 0 1 0 0
..$ D : num [1:8] 0 0 0 0 1 0 1 1 ..$ None: num [1:8] 0 1 0 0 0 0 0 0
$ EVar: tibble [8 x 5] (S3: tbl_df/tbl/data.frame) ..$ Apple : num [1:8] 1 0 1 0 0 0 0 0
..$ Banana : num [1:8] 1 0 0 0 0 1 1 0 ..$ Orange : num [1:8] 0 1 0 0 1 0 0 0
..$ Raspberry: num [1:8] 0 1 0 0 0 0 0 1 ..$ None : num [1:8] 0 0 0 1 0 0 0 0
unnest 함수를 사용하면 일치하지 않는 차원에 대해 write_ 함수 와 동일한 오류 가 발생합니다.
또한 unnest_wider 를 사용하려고 시도했지만 unnest_wider 함수가 단일 열만 인수 로 사용 하므로 여러 tibble-column 문제가 발생합니다.
pivot_wider 를 사용 하려고 했지만 getCols $ VariableName에서 열 이름을 올바르게 전달하는 방법을 알아낼 수 없습니다.
실패한 시도 중 일부를 추가 할 수 있지만, 이것이지도를 사용한 간단한 해결책이라고 생각합니다.
티블 내부에서 여러 티블의 중첩을 해제하는 간단한 솔루션이 있습니까? 더 큰 문제에 대한 더 깔끔하고 우아한 솔루션을 만들기 위해 다른 피드백을 듣게되어 기쁩니다.
답변
우리는 사용할 수 있습니다 cSplit_e
library(splitstackshape)
library(dplyr)
df1 %>%
select_if(names(.) %in% c('ID', getCols$VariableName)) %>%
cSplit_e("DVar", type = "character", fill = 0, sep=",") %>%
cSplit_e("EVar", type = "character", fill = 0, sep=",")
또는 여러 열에 사용하려는 경우 옵션은 map
library(purrr)
tmp <- df1 %>%
select_if(names(.) %in% c('ID', getCols$VariableName))
map_dfc(setdiff(names(tmp), "ID"), ~
tmp %>%
select(.x) %>%
cSplit_e( .x, type = "character", fill = 0, sep=",") %>%
select(-.x)) %>%
bind_cols(tmp, .)
OP의 기능을 사용하여 쉽게 평평하게 할 수 있습니다. as.data.frame
out <- df1 %>%
select_if(names(.) %in% c('ID', getCols$VariableName)) %>% mutate(across(.cols = !ID, .fns = ~MultiToCol(ID,.))) %>% do.call(data.frame, .) out ID DVar.A DVar.B DVar.C DVar.D DVar.None EVar.Apple EVar.Banana EVar.Orange EVar.Raspberry EVar.None 1 1 1 1 0 0 0 1 1 0 0 0 2 2 0 0 0 0 1 0 0 1 1 0 3 3 0 0 1 0 0 1 0 0 0 0 4 4 1 0 1 0 0 0 0 0 0 1 5 5 0 1 0 1 0 0 0 1 0 0 6 6 0 0 1 0 0 0 1 0 0 0 7 7 0 0 0 1 0 0 1 0 0 0 8 8 0 1 0 1 0 0 0 0 1 0 str(out) #'data.frame': 8 obs. of 11 variables: # $ ID : int 1 2 3 4 5 6 7 8
# $ DVar.A : num 1 0 0 1 0 0 0 0 # $ DVar.B : num 1 0 0 0 1 0 0 1
# $ DVar.C : num 0 0 1 1 0 1 0 0 # $ DVar.D : num 0 0 0 0 1 0 1 1
# $ DVar.None : num 0 1 0 0 0 0 0 0 # $ EVar.Apple : num 1 0 1 0 0 0 0 0
# $ EVar.Banana : num 1 0 0 0 0 1 1 0 # $ EVar.Orange : num 0 1 0 0 1 0 0 0
# $ EVar.Raspberry: num 0 1 0 0 0 0 0 1 # $ EVar.None : num 0 0 0 1 0 0 0 0
또는 사용할 수 있습니다 invoke
....
%>% invoke(data.frame, .)