Минус операции с фреймами данных
У меня есть 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
Мне нужно выполнить операцию установки этих 2 кадров данных. я использовал merge(df1,df2,all=TRUE)
а также merge(df1,df2,all=FALSE)
метод, чтобы получить объединение и пересечение этих фреймов данных и получил требуемый результат. Какова функция для получения минуса этих фреймов данных, то есть всех позиций, существующих в одном фрейме данных, но не в другом? Мне нужен следующий вывод.
c1 c2
1 a 1
2 b 2
9 ответов
Я помню, как сталкивался с этой проблемой несколько месяцев назад. Удалось просеять через мой 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
,
Я предпочитаю sqldf
пакет:
require(sqldf)
sqldf("select * from df1 except select * from df2")
## c1 c2
## 1 a 1
## 2 b 2
Я думаю, что самое простое решение с dplyr (tidyverse).
require(tidyverse)
anti_join(df1, df2)
Вы можете создать идентификатор столбца, а затем подмножество:
например
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>
Одна проблема с /questions/7135825/minus-operatsii-s-frejmami-dannyih/7135839#7135839 заключается в том, что предполагается, что ни в одном фрейме данных уже не было дублированных строк. Следующая функция снимает это ограничение и также работает с произвольными столбцами, определенными пользователем в 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
Если вы не планируете использовать какие-либо фактические данные в d2
тогда вам не нужно merge
совсем:
df1[!(df1$c1 %in% df2$c1), ]
Вы можете проверить значения в обоих столбцах и подмножестве следующим образом (просто добавив другое решение):
na.omit( df1[ sapply( 1:ncol(df1) , function(x) ! df1[,x] %in% df2[,x] ) , ] )
# c1 c2
#1 a 1
#2 b 2
Другим способом с базой R (включая сохранение имен строк и проверку всех строк) было бы двойное использование
duplicated()
:
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) )
all <- rbind(df1, df2)
all[!duplicated(all) & !duplicated(all, fromLast = TRUE), ]
Вы можете использовать
dplyr::setdiff
без труда
dplyr::setdiff(iris, iris[iris$Sepal.Length >6,])
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3.0 1.4 0.2 setosa
3 4.7 3.2 1.3 0.2 setosa
4 4.6 3.1 1.5 0.2 setosa
5 5.0 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
7 4.6 3.4 1.4 0.3 setosa
8 5.0 3.4 1.5 0.2 setosa
9 4.4 2.9 1.4 0.2 setosa
10 4.9 3.1 1.5 0.1 setosa
11 5.4 3.7 1.5 0.2 setosa
12 4.8 3.4 1.6 0.2 setosa
13 4.8 3.0 1.4 0.1 setosa
14 4.3 3.0 1.1 0.1 setosa
15 5.8 4.0 1.2 0.2 setosa
16 5.7 4.4 1.5 0.4 setosa
17 5.4 3.9 1.3 0.4 setosa
18 5.1 3.5 1.4 0.3 setosa
19 5.7 3.8 1.7 0.3 setosa
20 5.1 3.8 1.5 0.3 setosa
21 5.4 3.4 1.7 0.2 setosa
22 5.1 3.7 1.5 0.4 setosa
23 4.6 3.6 1.0 0.2 setosa
24 5.1 3.3 1.7 0.5 setosa
25 4.8 3.4 1.9 0.2 setosa
26 5.0 3.0 1.6 0.2 setosa
27 5.0 3.4 1.6 0.4 setosa
28 5.2 3.5 1.5 0.2 setosa
29 5.2 3.4 1.4 0.2 setosa
30 4.7 3.2 1.6 0.2 setosa
31 4.8 3.1 1.6 0.2 setosa
32 5.4 3.4 1.5 0.4 setosa
33 5.2 4.1 1.5 0.1 setosa
34 5.5 4.2 1.4 0.2 setosa
35 4.9 3.1 1.5 0.2 setosa
36 5.0 3.2 1.2 0.2 setosa
37 5.5 3.5 1.3 0.2 setosa
38 4.9 3.6 1.4 0.1 setosa
39 4.4 3.0 1.3 0.2 setosa
40 5.1 3.4 1.5 0.2 setosa
41 5.0 3.5 1.3 0.3 setosa
42 4.5 2.3 1.3 0.3 setosa
43 4.4 3.2 1.3 0.2 setosa
44 5.0 3.5 1.6 0.6 setosa
45 5.1 3.8 1.9 0.4 setosa
46 4.8 3.0 1.4 0.3 setosa
47 5.1 3.8 1.6 0.2 setosa
48 4.6 3.2 1.4 0.2 setosa
49 5.3 3.7 1.5 0.2 setosa
50 5.0 3.3 1.4 0.2 setosa
51 5.5 2.3 4.0 1.3 versicolor
52 5.7 2.8 4.5 1.3 versicolor
53 4.9 2.4 3.3 1.0 versicolor
54 5.2 2.7 3.9 1.4 versicolor
55 5.0 2.0 3.5 1.0 versicolor
56 5.9 3.0 4.2 1.5 versicolor
57 6.0 2.2 4.0 1.0 versicolor
58 5.6 2.9 3.6 1.3 versicolor
59 5.6 3.0 4.5 1.5 versicolor
60 5.8 2.7 4.1 1.0 versicolor
61 5.6 2.5 3.9 1.1 versicolor
62 5.9 3.2 4.8 1.8 versicolor
63 6.0 2.9 4.5 1.5 versicolor
64 5.7 2.6 3.5 1.0 versicolor
65 5.5 2.4 3.8 1.1 versicolor
66 5.5 2.4 3.7 1.0 versicolor
67 5.8 2.7 3.9 1.2 versicolor
68 6.0 2.7 5.1 1.6 versicolor
69 5.4 3.0 4.5 1.5 versicolor
70 6.0 3.4 4.5 1.6 versicolor
71 5.6 3.0 4.1 1.3 versicolor
72 5.5 2.5 4.0 1.3 versicolor
73 5.5 2.6 4.4 1.2 versicolor
74 5.8 2.6 4.0 1.2 versicolor
75 5.0 2.3 3.3 1.0 versicolor
76 5.6 2.7 4.2 1.3 versicolor
77 5.7 3.0 4.2 1.2 versicolor
78 5.7 2.9 4.2 1.3 versicolor
79 5.1 2.5 3.0 1.1 versicolor
80 5.7 2.8 4.1 1.3 versicolor
81 5.8 2.7 5.1 1.9 virginica
82 4.9 2.5 4.5 1.7 virginica
83 5.7 2.5 5.0 2.0 virginica
84 5.8 2.8 5.1 2.4 virginica
85 6.0 2.2 5.0 1.5 virginica
86 5.6 2.8 4.9 2.0 virginica
87 6.0 3.0 4.8 1.8 virginica
88 5.9 3.0 5.1 1.8 virginica