Объединить разное количество столбцов в R - PullRequest
3 голосов
/ 22 января 2020

Я пытаюсь объединить два или более столбцов в автоматическом режиме в некоторых данных опроса. Пример данных:

data <- data.frame("Q1: What is your gender?" = c("Male","Male",NA,NA,"Male"),
                   "Q1: What is your gender?" = c(NA,NA,"Female","Female",NA),
                   "Q2: Where do you live?" = c("North","North",NA,NA,NA),
                   "Q2: Where do you live?" = c(NA,NA,NA,NA,"South"),
                   "Q2: Where do you live?" = c(NA,NA,NA,"West",NA),
                   "Q2: Where do you live?" = c(NA,NA,"East",NA,NA))
data[] <- lapply(data, as.character)

И это то, чего я хочу достичь:

data.wanted <- data.frame("Q1: What is your gender?" = c("Male","Male","Female","Female","Male"),
                          "Q2: Where do you live?" = c("North","North","East","West","East"))
data.wanted[] <- lapply(data.wanted, as.character)

У каждого респондента есть только один не NA ответ на вопрос. Я рассмотрел (среди прочего) Объединение двух столбцов в один в R , но не могу понять, как использовать coalesce во многих вопросах, которые могут представлять различное количество столбцов. Я мог бы сделать для каждого вопроса:

data["Q1"] <- coalesce(data[,1],data[,2])
data["Q2"] <- coalesce(data[,3],data[,4],data[,5],data[,6])

то есть ручным способом. Тем не менее, поскольку у меня много вопросов, каждый из которых соответствует описанной выше структуре, я действительно ищу автоматический способ сделать это, либо через цикл, где я обращаюсь к именам столбцов через grep, либо каким-либо альтернативным методом.

Любые предложения очень ценятся!

Ответы [ 3 ]

3 голосов
/ 22 января 2020

Вы можете использовать некоторые функции dplyr и tidyr для изменения формы данных, чтобы справиться с незнанием того, сколько столбцов нужно охватить каждым вопросом. Назначьте номер строки для систематизации данных, затем преобразуйте в длинный формат.

Полагаю, вы заметили, что имена фреймов данных преобразуются в синтаксически правильные (без повторяющихся имен, без пробелов и т. Д. c). Поэтому после изменения формы очистите текст, полученный из имен столбцов, чтобы "Q1..What.is.your.gender." и "Q1..What.is.your.gender..1" стали "Q1..What.is.your.gender". Затем отфильтруйте, чтобы сохранить только строки с фактическими значениями, и измените их обратно на широкий. Удалите столбец номера строки, если он вам больше не нужен.

library(dplyr)

data %>%
  mutate(row = row_number()) %>%
  tidyr::pivot_longer(-row) %>%
  mutate(name = sub("\\.+\\d?$", "", name)) %>%
  filter(!is.na(value)) %>%
  tidyr::pivot_wider()
#> # A tibble: 5 x 3
#>     row Q1..What.is.your.gender Q2..Where.do.you.live
#>   <int> <fct>                   <fct>                
#> 1     1 Male                    North                
#> 2     2 Male                    North                
#> 3     3 Female                  East                 
#> 4     4 Female                  West                 
#> 5     5 Male                    South
3 голосов
/ 22 января 2020

Вот один из способов сделать это в базе R:

as.data.frame(sapply(split.default(data, sub("\\.\\d+$", "", names(data))), 
              function(x) do.call(pmax, c(x, na.rm = TRUE))))

#  Q1..What.is.your.gender. Q2..Where.do.you.live.
#1                     Male                  North
#2                     Male                  North
#3                   Female                   East
#4                   Female                   West
#5                     Male                  South

Объяснение:

Понять, как это работает, проще, если мы разберем его пошагово -step:

names возвращает имена столбцов данных

names(data)
#[1] "Q1..What.is.your.gender." "Q1..What.is.your.gender..1" "Q2..Where.do.you.live."     
#    "Q2..Where.do.you.live..1" "Q2..Where.do.you.live..2"   "Q2..Where.do.you.live..3"  

Используя sub, мы удаляем дополнительные . и число из каждого имени, чтобы мы получили общее имена столбцов, подобные этому

sub("\\.\\d+$", "", names(data))
#[1] "Q1..What.is.your.gender." "Q1..What.is.your.gender." "Q2..Where.do.you.live."
#    "Q2..Where.do.you.live." "Q2..Where.do.you.live." "Q2..Where.do.you.live."

Мы используем это в split.default для разделения данных на основе похожих имен столбцов

split.default(data, sub("\\.\\d+$", "", names(data)))
#$Q1..What.is.your.gender.
#  Q1..What.is.your.gender. Q1..What.is.your.gender..1
#1                     Male                       <NA>
#2                     Male                       <NA>
#3                     <NA>                     Female
#4                     <NA>                     Female
#5                     Male                       <NA>

#$Q2..Where.do.you.live.
#  Q2..Where.do.you.live. Q2..Where.do.you.live..1 Q2..Where.do.you.live..2 Q2..Where.do.you.live..3
#1                  North                     <NA>                     <NA>                     <NA>
#2                  North                     <NA>                     <NA>                     <NA>
#3                   <NA>                     <NA>                     <NA>                     East
#4                   <NA>                     <NA>                     West                     <NA>
#5                   <NA>                    South                     <NA>                     <NA>

Мы используем sapply для итерации по каждому списку и выбора Максимальное значение из каждой строки, удаляющее NA значения (на самом деле, в этом случае, использование pmin также будет работать), которое возвращает матрицу, и мы конвертируем ее в массив данных.

as.data.frame(sapply(split.default(data, sub("\\.\\d+$", "", names(data))), 
        function(x) do.call(pmax, c(x, na.rm = TRUE))))

#  Q1..What.is.your.gender. Q2..Where.do.you.live.
#1                     Male                  North
#2                     Male                  North
#3                   Female                   East
#4                   Female                   West
#5                     Male                  South
2 голосов
/ 22 января 2020

Решение с использованием базы R:

result = data.frame(row.names = rownames(data))
Q_names = unique(gsub("\\.\\..*", "", names(data)))
for(q in Q_names) {
  x = data.frame(apply(data[grepl(paste0(q, "..", collapse = ""), names(data))], 1,
                       function(x) {x[which(!is.na(x))]}))
  names(x) = q
  result = cbind(result, x)
}
print(result)

#       Q1    Q2
# 1   Male North
# 2   Male North
# 3 Female  East
# 4 Female  West
# 5   Male South
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...