Минус работа кадров данных
У меня есть 2 кадра данных df1
и df2
.
df1 <- data.frame(c1=c("a","b","c","d"),c2=c(1,2,3,4) )
df2 <- data.frame(c1=c("c","d","e","f"),c2=c(3,4,5,6) )
> df1
c1 c2
1 a 1
2 b 2
3 c 3
4 d 4
> df2
c1 c2
1 c 3
2 d 4
3 e 5
4 f 6
Мне нужно выполнить операцию установки этих двух кадров данных. Я использовал метод merge(df1,df2,all=TRUE)
и merge(df1,df2,all=FALSE)
, чтобы получить объединение и пересечение этих кадров данных и получить требуемый результат. Какова функция, чтобы получить минус этих кадров данных, то есть все позиции, существующие на одном кадре данных, но не другие? Мне нужен следующий вывод.
c1 c2
1 a 1
2 b 2
Ответы
Ответ 1
Я помню, как через несколько месяцев я встретил этот точный вопрос. Умело просеивать мои однострочники Evernote.
Примечание. Это не мое решение. Кредит принадлежит тому, кто его написал (которого я пока не могу найти).
Если вы не беспокоитесь о rownames
, то вы можете сделать:
df1[!duplicated(rbind(df2, df1))[-seq_len(nrow(df2))], ]
# c1 c2
# 1 a 1
# 2 b 2
Изменить: Решение A data.table
:
dt1 <- data.table(df1, key="c1")
dt2 <- data.table(df2)
dt1[!dt2]
или лучше однострочный (из v1.9.6 +):
setDT(df1)[!df2, on="c1"]
Это возвращает все строки в df1
, где df2$c1
не имеет соответствия с df1$c1
.
Ответ 2
Я предпочитаю sqldf
пакет:
require(sqldf)
sqldf("select * from df1 except select * from df2")
## c1 c2
## 1 a 1
## 2 b 2
Ответ 3
Вы можете создать столбцы идентификаторов, а затем подмножество:
например.
df1 <- data.frame(c1=c("a","b","c","d"),c2=c(1,2,3,4), indf1 = rep("Y",4) )
df2 <- data.frame(c1=c("c","d","e","f"),c2=c(3,4,5,6),indf2 = rep("Y",4) )
merge(df1,df2)
# c1 c2 indf1 indf2
#1 c 3 Y Y
#2 d 4 Y Y
bigdf <- merge(df1,df2,all=TRUE)
# c1 c2 indf1 indf2
#1 a 1 Y <NA>
#2 b 2 Y <NA>
#3 c 3 Y Y
#4 d 4 Y Y
#5 e 5 <NA> Y
#6 f 6 <NA> Y
Затем подмножество, как вы пожелаете:
bigdf[is.na(bigdf$indf1) ,]
# c1 c2 indf1 indf2
#5 e 5 <NA> Y
#6 f 6 <NA> Y
bigdf[is.na(bigdf$indf2) ,] #<- output you requested those not in df2
# c1 c2 indf1 indf2
#1 a 1 Y <NA>
#2 b 2 Y <NA>
Ответ 4
Если вы не планируете использовать какие-либо фактические данные в d2
, вам вообще не нужно merge
:
df1[!(df1$c1 %in% df2$c1), ]
Ответ 5
Вы можете проверить значения как в столбцах, так и в подмножестве (просто добавив другое решение):
na.omit( df1[ sapply( 1:ncol(df1) , function(x) ! df1[,x] %in% df2[,x] ) , ] )
# c1 c2
#1 a 1
#2 b 2
Ответ 6
Одна проблема с fooobar.com/questions/377332/... заключается в том, что ни один фрейм данных уже не имеет дублированных строк. Следующая функция удаляет это ограничение и также работает с произвольными определенными пользователем столбцами в x или y.
Реализация использует аналогичную идею для реализации duplicated.data.frame
в конкатенации столбцов вместе с разделителем. duplicated.data.frame
использует "\r"
, что может вызвать конфликты, если записи имеют встроенные символы "\r"
. Это использует разделитель ASCII-записей "\30"
, который будет иметь гораздо меньший шанс появиться во входных данных.
setdiff.data.frame <- function(x, y,
by = intersect(names(x), names(y)),
by.x = by, by.y = by) {
stopifnot(
is.data.frame(x),
is.data.frame(y),
length(by.x) == length(by.y))
!do.call(paste, c(x[by.x], sep = "\30")) %in% do.call(paste, c(y[by.y], sep = "\30"))
}
# Example usage
# remove all 4 or 6 cylinder 4 gear cars or 8 cylinder 3 gear rows
to_remove <- data.frame(cyl = c(4, 6, 8), gear = c(4, 4, 3))
mtcars[setdiff.data.frame(mtcars, to_remove), ]
#> mpg cyl disp hp drat wt qsec vs am gear carb
#> Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
#> Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1
#> Toyota Corona 21.5 4 120.1 97 3.70 2.465 20.01 1 0 3 1
#> Porsche 914-2 26.0 4 120.3 91 4.43 2.140 16.70 0 1 5 2
#> Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.90 1 1 5 2
#> Ford Pantera L 15.8 8 351.0 264 4.22 3.170 14.50 0 1 5 4
#> Ferrari Dino 19.7 6 145.0 175 3.62 2.770 15.50 0 1 5 6
#> Maserati Bora 15.0 8 301.0 335 3.54 3.570 14.60 0 1 5 8
# with differing column names
to_remove2 <- data.frame(a = c(4, 6, 8), b = c(4, 4, 3))
mtcars[setdiff.data.frame(mtcars, to_remove2, by.x = c("cyl", "gear"), by.y = c("a", "b")), ]
#> mpg cyl disp hp drat wt qsec vs am gear carb
#> Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
#> Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1
#> Toyota Corona 21.5 4 120.1 97 3.70 2.465 20.01 1 0 3 1
#> Porsche 914-2 26.0 4 120.3 91 4.43 2.140 16.70 0 1 5 2
#> Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.90 1 1 5 2
#> Ford Pantera L 15.8 8 351.0 264 4.22 3.170 14.50 0 1 5 4
#> Ferrari Dino 19.7 6 145.0 175 3.62 2.770 15.50 0 1 5 6
#> Maserati Bora 15.0 8 301.0 335 3.54 3.570 14.60 0 1 5 8
Ответ 7
Это то, что я пробовал: у меня есть 2 кадра данных df2 и df_name
df2
имя2 возраст
b 10
c 20
d 30
df_name
имя возраст хобби
10 танцев
b 20 пение
c 30 play
Найти df_name MINUS df2:
1. Измените 2 блока данных.
dfmerge <-merge (x = df_name, y = df2, by.x = c ( "name" ), by.y = c ( "name2" ), all = TRUE)
dfmerge
имя age.x хобби age.y
1 a 10 dance NA
2 b 20 sing 10
3 c 30 играть 20
4 d NA 30
dfmerge [is.na(dfmerge $age.y),]
имя age.x хобби age.y
1 a 10 танец NA