假设我对数据框的某些列值进行了处理,如下所示:
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"))
Run Code Online (Sandbox Code Playgroud)
假设处理需要在单独的表中工作,并作为结果给出以下数据框,它是原始的子集:
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"))
Run Code Online (Sandbox Code Playgroud)
现在我想把所有的东西放在一起,所以我使用这样的操作:
> 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
Run Code Online (Sandbox Code Playgroud)
是否存在直接使用连接操作执行此操作的方法?
如果子集只是关键列,而变量需要进行处理 (id,动物权重), 而不是原始数据框的总变量(id,动物,重量,高度),如何组装子集用原来的套装?
tal*_*lat 12
您描述的是一个连接操作,您可以在其中更新原始数据集中的某些值.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]
Run Code Online (Sandbox Code Playgroud)
数据现已更新:
# 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
Run Code Online (Sandbox Code Playgroud)
您可以使用setDF切换回普通data.frame.
首先删除 na,然后简单地堆叠 tibbles:
bind_rows(filter(df,!is.na(weight)),sub_df)
Run Code Online (Sandbox Code Playgroud)
对于任何正在寻找在 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"))
Run Code Online (Sandbox Code Playgroud)
我们想要加入的数据子集是相同的:
sub_df <- tibble(id = c(2, 5),
animal = c("cat", "snake"),
weight = c("2.2", "1.3"))
Run Code Online (Sandbox Code Playgroud)
如果我们单独使用 OP 的方法 ( anti_join %>% bind_rows),由于 df 中额外的“高度”列,这将不起作用。需要一两个额外的步骤。
在这种情况下我们可以使用以下函数:
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)
}
Run Code Online (Sandbox Code Playgroud)
现在来看结果:
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
Run Code Online (Sandbox Code Playgroud)
这是在管道中使用该函数的示例:
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
Run Code Online (Sandbox Code Playgroud)
希望这有帮助:)
这不正是dplyr::rows_update我们所需要的吗?以下代码应该可以工作:
df %>% dplyr::rows_update(sub_df, by = "id")
Run Code Online (Sandbox Code Playgroud)
只要数据集有唯一标识符(一个或多个变量),这种方法就应该有效。