Объединить неравные фреймы данных и заменить отсутствующие строки на 0

#r #merge #dataframe

#r #объединить #фрейм данных

Вопрос:

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

 df1 = data.frame(x=c('a', 'b', 'c', 'd', 'e'))
df2 = data.frame(x=c('a', 'b', 'c'),y = c(0,1,0))
merge(df1, df2)
  x y
1 a 0
2 b 1
3 c 0 
  

Я хочу объединить df1 и df2. Символы a, b и c хорошо слились и также имеют 0, 1, 0, но d и e не имеют ничего. Я хочу, чтобы d и e также были в таблице слияния с условием 0 0. Таким образом, для каждой отсутствующей строки в df2 data.frame 0 должно быть помещено в таблицу df1, например:

   x y
1 a 0
2 b 1
3 c 0
4 d 0
5 e 0
  

Комментарии:

1. Обычно мы называем символы также значениями, поэтому ваш столбец y будет называться числовым.

Ответ №1:

Взгляните на страницу справки по слиянию. all Параметр позволяет указать различные типы слияний. Здесь мы хотим установить all = TRUE . Это приведет к возврату слиянием NA для значений, которые не совпадают, которые мы можем обновить до 0 с помощью is.na() :

 zz <- merge(df1, df2, all = TRUE)
zz[is.na(zz)] <- 0

> zz
  x y
1 a 0
2 b 1
3 c 0
4 d 0
5 e 0
  

Обновлено много лет спустя для решения последующего вопроса

Вам нужно определить имена переменных во второй таблице данных, которые вы не объединяете — для этого я использую setdiff() . Проверьте следующее:

 df1 = data.frame(x=c('a', 'b', 'c', 'd', 'e', NA))
df2 = data.frame(x=c('a', 'b', 'c'),y1 = c(0,1,0), y2 = c(0,1,0))

#merge as before
df3 <- merge(df1, df2, all = TRUE)
#columns in df2 not in df1
unique_df2_names <- setdiff(names(df2), names(df1))
df3[unique_df2_names][is.na(df3[, unique_df2_names])] <- 0 
  

Создано 2019-01-03 пакетом reprex (версия 0.2.1)

Комментарии:

1. Привет, Чейз, могу ли я использовать команду «all = true» только для df1. Иногда эта команда включает данные, которые недоступны в df1, но доступны в df2

2. @jbest — есть аргументы, all.x и all.y где x == первый объект data.frame и y == второй, именно для этой ситуации. Подробности см. на странице справки для ?merge .

Ответ №2:

Или, в качестве альтернативы коду @Chase, будучи недавним поклонником plyr с опытом работы в базах данных:

 require(plyr)
zz<-join(df1, df2, type="left")
zz[is.na(zz)] <- 0
  

Ответ №3:

Другой вариант с data.table.

ПРИМЕР ДАННЫХ

 dt1 <- data.table(df1)
dt2 <- data.table(df2)
setkey(dt1,x)
setkey(dt2,x)
  

код

 dt2[dt1,list(y=ifelse(is.na(y),0,y))]
  

Комментарии:

1. В версии 1.10.4 вам не нужно setkey и вы можете использовать df2[df1, on="x"][is.na(y), y := 0] сразу после создания data.tables для получения желаемого результата.

Ответ №4:

Предполагая, что df1 имеются все интересующие x значения, вы могли бы использовать a dplyr::left_join() для объединения, а затем либо a, base::replace() либо tidyr::replace_na() заменить NA s на 0 s:

 library(tidyverse)

# dplyr only:
df_new <- 
  left_join(df1, df2, by = 'x') %>% 
  mutate(y = replace(y, is.na(y), 0))

# dplyr and tidyr:
df_new <- 
  left_join(df1, df2, by = 'x') %>% 
  mutate(y = replace_na(y, 0))

# In the sample data column `x` is a factor, which will give a warning with the join. This can be prevented by converting to a character before the join:
df_new <- 
  left_join(df1 %>% mutate(x = as.character(x)), 
            df2 %>% mutate(x = as.character(x)), 
            by = 'x') %>% 
    mutate(y = replace(y, is.na(y), 0))
  

Ответ №5:

Я использовал ответ, данный Chase (ответил 11 ’11 мая в 14: 21), но я добавил немного кода, чтобы применить это решение к моей конкретной проблеме.

У меня был фрейм ставок (пользователь, загрузка) и фрейм итогов (пользователь, загрузка), которые должны были быть объединены пользователем, и я хотел включить каждую ставку, даже если не было соответствующего итога. Однако не может быть отсутствующих итогов, и в этом случае выбор строк для замены NA на ноль завершится неудачей.

Слияние выполняется в первой строке кода. Следующие две строки изменяют имена столбцов в объединенном фрейме. Оператор if заменяет NA на ноль, но только если есть строки с NA.

 # merge rates and totals, replacing absent totals by zero
graphdata <- merge(rates, totals, by=c("user"),all.x=T)
colnames(graphdata)[colnames(graphdata)=="download.x"] = "download.rate"
colnames(graphdata)[colnames(graphdata)=="download.y"] = "download.total"
if(any(is.na(graphdata$download.total))) {
    graphdata[is.na(graphdata$download.total),]$download.total <- 0
}
  

Ответ №6:

Вот data.table ответ. Это может быть использовано в выбранных столбцах, изменяя cols_added_df2 определение

 df1 = data.frame(x=c('a', 'b', 'c', 'd', 'e'))
df2 = data.frame(x=c('a', 'b', 'c'),y = c(0,1,0))
setDT(df1)
setDT(df2)
df3 <- merge(df1, df2, by = "x", all.x = TRUE)

cols_added_df2 <- setdiff(names(df2), names(df1)) 
df3[, 
  paste0(cols_added_df2) := lapply(.SD, function(col){
    fifelse(is.na(col), 1, col)
  }),
  .SDcols = cols_added_df2
]
  

Ответ №7:

С помощью {powerjoin} мы можем сделать:

 df1 = data.frame(x=c('a', 'b', 'c', 'd', 'e'))
df2 = data.frame(x=c('a', 'b', 'c'),y = c(0,1,0))
powerjoin::power_full_join(df1, df2, fill = 0)
#> Joining, by = "x"
#>   x y
#> 1 a 0
#> 2 b 1
#> 3 c 0
#> 4 d 0
#> 5 e 0
  

Создано 2022-04-28 пакетом reprex (версия 0.1)