假设我对数据框的某些列值进行了处理,如下所示:
id animal weight height ...
1 dog 23.0
2 cat NA
3 duck 1.2
4 fairy 0.2
5 snake BAD
df <- data.frame(id = seq(1:5),
animal = c("dog", "cat", "duck", "fairy", "snake"),
weight = c("23", NA, "1.2", "0.2", "BAD"))
假设处理需要在单独的表中工作,并作为结果给出以下数据框,它是原始的子集:
id animal weight
2 cat 2.2
5 snake 1.3
sub_df <- data.frame(id = c(2, 5),
animal = c("cat", "snake"),
weight = c("2.2", "1.3"))
现在我想把所有的东西放在一起,所以我使用这样的操作:
> df %>%
anti_join(sub_df, by = c("id", "animal")) %>%
bind_rows(sub_df)
id animal weight
4 fairy 0.2
1 dog 23.0
3 duck 1.2
2 cat 2.2
5 snake 1.3
是否存在直接使用联接操作执行此操作的方法?
如果子集只是关键列,而变量需要进行处理 (id,动物权重) 而不是总变量原始数据框(id,动物,体重,身高),如何用原始集合组合子集?
答案 0 :(得分:11)
您所描述的是一种连接操作,您可以在其中更新原始数据集中的某些值。使用data.table
可以很容易地实现这一功能,因为它具有快速连接和按引用更新的概念(:=
)。
以下是您的玩具数据示例:
library(data.table)
setDT(df) # convert to data.table without copy
setDT(sub_df) # convert to data.table without copy
# join and update "df" by reference, i.e. without copy
df[sub_df, on = c("id", "animal"), weight := i.weight]
现在数据已更新:
# id animal weight
#1: 1 dog 23.0
#2: 2 cat 2.2
#3: 3 duck 1.2
#4: 4 fairy 0.2
#5: 5 snake 1.3
您可以使用setDF
切换回普通data.frame
。
答案 1 :(得分:3)
首先删除na,然后简单地堆叠这些元素:
bind_rows(filter(df,!is.na(weight)),sub_df)
答案 2 :(得分:3)
对于正在寻找可在tidyverse管道中使用的解决方案的任何人:
我经常遇到这个问题,并编写了一个简短的函数,该函数主要使用tidyverse动词来解决此问题。如果原始df中有其他列,则会解决这种情况。
例如,如果OP的df中还有一个“高度”列:
library(dplyr)
df <- tibble(id = seq(1:5),
animal = c("dog", "cat", "duck", "fairy", "snake"),
weight = c("23", NA, "1.2", "0.2", "BAD"),
height = c("54", "45", "21", "50", "42"))
我们想要加入的数据子集是相同的:
sub_df <- tibble(id = c(2, 5),
animal = c("cat", "snake"),
weight = c("2.2", "1.3"))
如果我们仅使用OP的方法(anti_join %>% bind_rows
),则由于df中增加了“ height”列,因此无法使用。需要额外的一两个步骤。
在这种情况下,我们可以使用以下功能:
replace_subset <- function(df, df_subset, id_col_names = c()) {
# work out which of the columns contain "new" data
new_data_col_names <- colnames(df_subset)[which(!colnames(df_subset) %in% id_col_names)]
# complete the df_subset with the extra columns from df
df_sub_to_join <- df_subset %>%
left_join(select(df, -new_data_col_names), by = c(id_col_names))
# join and bind rows
df_out <- df %>%
anti_join(df_sub_to_join, by = c(id_col_names)) %>%
bind_rows(df_sub_to_join)
return(df_out)
}
现在查看结果:
replace_subset(df = df , df_subset = sub_df, id_col_names = c("id"))
## A tibble: 5 x 4
# id animal weight height
# <dbl> <chr> <chr> <chr>
#1 1 dog 23 54
#2 3 duck 1.2 21
#3 4 fairy 0.2 50
#4 2 cat 2.2 45
#5 5 snake 1.3 42
这是在管道中使用该函数的示例:
df %>%
replace_subset(df_subset = sub_df, id_col_names = c("id")) %>%
mutate_at(.vars = vars(c('weight', 'height')), .funs = ~as.numeric(.)) %>%
mutate(bmi = weight / (height^2))
## A tibble: 5 x 5
# id animal weight height bmi
# <dbl> <chr> <dbl> <dbl> <dbl>
#1 1 dog 23 54 0.00789
#2 3 duck 1.2 21 0.00272
#3 4 fairy 0.2 50 0.00008
#4 2 cat 2.2 45 0.00109
#5 5 snake 1.3 42 0.000737
希望这会有所帮助:)
答案 3 :(得分:1)
dplyr::rows_update
正是我们这里需要的吗?以下代码应该起作用:
df %>% dplyr::rows_update(sub_df, by = "id")
只要您的数据集具有唯一标识符(一个或多个变量),此方法就可以工作。