Сопоставление и замена значений фактора с использованием идентификатора

У меня есть два кадра данных, каждый из которых содержит одну и ту же переменную и уникальный идентификатор для каждого наблюдения.

df.1 — это большой набор данных, который содержит пропущенные значения, обозначенные NA. Значения для этих отсутствующих записей содержатся в df.2, и я хотел бы заменить отсутствующие значения из df.1 значениями из df.2, сопоставив идентификаторы.

Мне не удалось найти здесь аналогичную проблему, которая учитывает, что они оба являются факторными переменными.

Для упрощения: если идентификатор совпадает, отсутствующее значение из df.1 следует заменить значением фактора из df.2.

df.1 <- data.frame(id = c(334,440,501,2304,2500), 
                v1 = c("4 dogs",NA,"3 dogs",NA,"No dogs"))

df.2 <- data.frame(id = c(440,2304), 
                v2 = c("4 dogs","5 dogs"))

Ваша помощь очень ценится.


person dayleymart    schedule 03.01.2018    source источник
comment
Вы проверили это, я думаю, что на это был дан ответ. здесь и здесь   -  person user5249203    schedule 03.01.2018
comment
К сожалению, это не тот случай. я бы хотел, чтобы код просто сопоставлял идентификаторы между двумя переменными и заменял значения с df.2 на df.1. Идентификаторы указывают только те значения, которые отсутствуют в df.1.   -  person dayleymart    schedule 03.01.2018
comment
Почему вам нужно сохранить их как обе факторные переменные? Просто конвертируйте в character.   -  person Gregor Thomas    schedule 03.01.2018
comment
В конечном счете, я хотел бы, чтобы все данные были в форме фактора в конце, поскольку я хотел бы провести дальнейший анализ, используя их в этом классе...   -  person dayleymart    schedule 03.01.2018
comment
вопрос на засыпку.   -  person MKR    schedule 03.01.2018
comment
В конце легко преобразовать обратно в фактор.   -  person Gregor Thomas    schedule 03.01.2018
comment
Да, но сохранится ли он на том же уровне, что и в начале? Если бы у него изначально был уровень, но он не использовался в качестве значения в наблюдениях, то превращение его в символ означало бы, что он теряет этот уровень, когда он, наконец, снова преобразуется в фактор?   -  person dayleymart    schedule 03.01.2018
comment
@Грегор Я бы тоже предпочел конвертировать в конце.   -  person MKR    schedule 03.01.2018
comment
Так что сохраните начальные уровни! init_levels = levels(df.1$v1) ... <all your code> ... result$v1 = factor(result$v1, levels = init_levels)   -  person Gregor Thomas    schedule 03.01.2018
comment
@Грегор Да, я это понимаю, и спасибо за код. Я просто беспокоился, что, поскольку это большой объем данных опроса, любая другая информация метаданных могла быть потеряна из него, как на этих уровнях?   -  person dayleymart    schedule 03.01.2018


Ответы (4)


Использование data.table и dplyr:-

library(data.table)
library(dplyr)
df <- left_join(df.1, df.2, by = "id")
setDT(df)
df[is.na(v1), v1 := v2]
df[, v2 := NULL]

Вы получите желаемый результат: -

     id      v1
1:  334  4 dogs
2:  440  4 dogs
3:  501  3 dogs
4: 2304  5 dogs
5: 2500 No dogs

До этого момента id будет числовым, а v1 будет множителем. Если вы хотите, чтобы id также было преобразовано в фактор. Вы можете сделать это, используя: -

df[, id := as.factor(id)]
person sm925    schedule 03.01.2018
comment
Не потеряет ли переменная свой факторный тип при использовании этого метода? - person dayleymart; 03.01.2018
comment
Таким образом, v1 будет фактором. id будет числовым. Если вы хотите сделать id снова фактором. Вы можете использовать df[, id := as.factor(id)]. Я добавлю это в ответ, если хотите. - person sm925; 03.01.2018
comment
Извините, идентификатор всегда был числовым и может оставаться числовым, поскольку это просто уникальный идентификатор. Спасибо. - person dayleymart; 03.01.2018
comment
Круто, хоть добавил. Если вам нужно, вы можете использовать его. - person sm925; 03.01.2018
comment
Только что воспользовавшись вашим кодом и взглянув на сводку недавно созданной переменной v1. Кажется, что «NA» был добавлен в качестве фактора для всех отсутствующих значений в df1, которые не были заменены ... у вас есть решение, чтобы превратить их обратно в просто отсутствующие значения? - person dayleymart; 04.01.2018
comment
Думаю, так было, когда вы создали df.1. АН приходили как факторы. - person sm925; 04.01.2018

Как упомянул @Gregor, вы можете преобразовать df обратно в факторы. Удобная функция здесь — функция coalesce от @MrFlick. Решение говорит само за себя

library(dplyr)

df.1 %>%
  left_join(df.2, by = "id") %>%
  mutate_if(is.factor, as.character) %>%
  mutate(final = coalesce(v1, v2))  %>% mutate_if(is.character, as.factor)

вывод

   id      v1     v2   final
1  334  4 dogs   <NA>  4 dogs
2  440    <NA> 4 dogs  4 dogs
3  501  3 dogs   <NA>  3 dogs
4 2304    <NA> 5 dogs  5 dogs
5 2500 No dogs   <NA> No dogs

сохраните приведенный выше результат в переменной (df), затем проверьте str(df)

'data.frame':   5 obs. of  4 variables:
 $ id   : num  334 440 501 2304 2500
 $ v1   : Factor w/ 3 levels "3 dogs","4 dogs",..: 2 NA 1 NA 3
 $ v2   : Factor w/ 2 levels "4 dogs","5 dogs": NA 1 NA 2 NA
 $ final: Factor w/ 4 levels "3 dogs","4 dogs",..: 2 2 1 3 4

если вы хотите удалить столбцы v1 и v2, просто передайте окончательный результат в %>% select(id,final)

Надеюсь, что это работает.

person user5249203    schedule 03.01.2018

Используя подход tidyverse, у вас есть два решения:

Первое решение:

library(dplyr)
df.1 <- data.frame(id = c(334,440,501,2304,2500), 
                   v1 = c("4 dogs",NA,"3 dogs",NA,"No dogs"),stringsAsFactors=F) 

df.2 <- data.frame(id = c(440,2304), 
                   v2 = c("4 dogs","5 dogs"),stringsAsFactors=F) %>% 
    rename(v1=v2)

df_mix <- bind_rows(df.1,df.2) %>% 
    drop_na(...=v1)

Второе решение:

df.1 <- data.frame(id = c(334,440,501,2304,2500), 
                   v1 = c("4 dogs",NA,"3 dogs",NA,"No dogs"),stringsAsFactors=F)

df.2 <- data.frame(id = c(440,2304), 
                   v2 = c("4 dogs","5 dogs"),stringsAsFactors=F) 

df_mix <- left_join(df.1,df.2,by="id") %>% 
    mutate(v1=case_when(
        is.na(v1) ~ v2,
        !is.na(v1) ~ v1
    )) %>% 
    select(1:2)

PS: я предпочитаю загружать строки как векторы символов

person Scipione Sarlo    schedule 03.01.2018

Вы можете объединить df.1 и df.2, чтобы сохранить v1 и v2 в объединенном data.frame. Логика запуска для замены отсутствующих v1 значением v2.

library(dplyr)

df.1 <- data.frame(id = c(334,440,501,2304,2500), 
                   v1 = c("4 dogs",NA,"3 dogs",NA,"No dogs"))

df.2 <- data.frame(id = c(440,2304), 
                   v2 = c("4 dogs","5 dogs"))
#merge using left_join to keep all rows from df.1
final <- df.1 %>%
  left_join(df.2, by = "id")
#> final
#    id      v1     v2
#1  334  4 dogs   <NA>
#2  440    <NA> 4 dogs
#3  501  3 dogs   <NA>
#4 2304    <NA> 5 dogs
#5 2500 No dogs   <NA>

#Define a function to replace missing v1
replMissing <- function(x, y){
  ifelse(is.na(x), y, x )
}

#call replMissing function using mapply. Modified to handle factor
final$v1 <- as.factor(mapply(replMissing, as.character(final$v1), as.character(final$v2)))

#results is
#> final
#    id      v1     v2
#1  334  4 dogs   <NA>
#2  440  4 dogs 4 dogs
#3  501  3 dogs   <NA>
#4 2304  5 dogs 5 dogs
#5 2500 No dogs   <NA>

Столбец v2 теперь можно удалить

person MKR    schedule 03.01.2018
comment
Есть ли возможное решение, чтобы затем изменить их обратно на фактор и сохранить все уровни, которые у него были изначально? - person dayleymart; 03.01.2018
comment
@dayleymart изменил решение для обработки значения factor. На самом деле при вызове mapply необходимо выполнить преобразование. - person MKR; 03.01.2018