2017-10-10 101 views
6

Rozważmy następujące dwa data.frames:Usuń dokładne wierszy i częstotliwość rzędów data.frame które są w innym data.frame w r

a1 <- data.frame(A = c(1:5, 2, 4, 2), B = letters[c(1:5, 2, 4, 2)]) 
a2 <- data.frame(A = c(1:3,2), B = letters[c(1:3,2)]) 

Chciałbym usunąć dokładnych rzędów a1 że a2 są w taki sposób, że wynik powinien być:

A B 
4 d 
5 e 
4 d 
2 b 

należy zauważyć, że w jednym rzędzie z 2 b w A1 jest utrzymywany w wyniku końcowego. Obecnie używam instrukcji pętli, która staje się bardzo powolna, ponieważ mam wiele zmiennych i tysiące wierszy w moim data.frames. Czy jest jakaś wbudowana funkcja, aby uzyskać ten wynik?

+0

Nie jest jasne, czy twoje dane wyjściowe są poprawne, "2b" jest na początku, czy coś mi brakuje? – steveb

+0

@steveb '2b' jest dwa razy w' a1', więc tylko jeden zostaje anulowany, a jeden pozostaje na wyjściu. –

+0

Ahh, to jest to, co dostaję za czytanie zbyt szybko. – steveb

Odpowiedz

1

Chyba ten jest podobny do DWal's solution ale w podstawowej R

a1_temp = Reduce(paste, a1) 
a1_temp = paste(a1_temp, ave(seq_along(a1_temp), a1_temp, FUN = seq_along)) 

a2_temp = Reduce(paste, a2) 
a2_temp = paste(a2_temp, ave(seq_along(a2_temp), a2_temp, FUN = seq_along)) 

a1[!a1_temp %in% a2_temp,] 
# A B 
#4 4 d 
#5 5 e 
#7 4 d 
#8 2 b 
2

Można użyć dplyr to zrobić. Ustawię stringsAsFactors = FALSE, aby pozbyć się ostrzeżeń o niedopasowaniu czynników.

library(dplyr) 

a1 <- data.frame(A = c(1:5, 2, 4, 2), B = letters[c(1:5, 2, 4, 2)], stringsAsFactors = FALSE) 
a2 <- data.frame(A = c(1:3,2), B = letters[c(1:3,2)], stringsAsFactors = FALSE) 

## Make temp variables to join on then delete later. 
# Create a row number 
a1_tmp <- 
    a1 %>% 
    group_by(A, B) %>% 
    mutate(tmp_id = row_number()) %>% 
    ungroup() 
# Create a count 
a2_tmp <- 
    a2 %>% 
    group_by(A, B) %>% 
    summarise(count = n()) %>% 
    ungroup() 

## Keep all that have no entry int a2 or the id > the count (i.e. used up a2 entries). 
left_join(a1_tmp, a2_tmp, by = c('A', 'B')) %>% 
    ungroup() %>% filter(is.na(count) | tmp_id > count) %>% 
    select(-tmp_id, -count) 

## # A tibble: 4 x 2 
##  A  B 
## <dbl> <chr> 
## 1  4  d 
## 2  5  e 
## 3  4  d 
## 4  2  b 

EDIT

Oto podobne rozwiązanie, które jest trochę krótszy. Wykonuje następujące czynności: (1) dodaj kolumnę dla numeru wiersza, aby połączyć oba elementy data.frame (2) tymczasową kolumnę w a2 (2. data.frame), która będzie wyświetlana jako null w połączeniu z a1 (tj. Wskazuje, że jest unikalna dla a1) .

library(dplyr) 

left_join(a1 %>% group_by(A,B) %>% mutate(rn = row_number())    %>% ungroup(), 
      a2 %>% group_by(A,B) %>% mutate(rn = row_number(), tmpcol = 0) %>% ungroup(), 
      by = c('A', 'B', 'rn')) %>% 
filter(is.na(tmpcol)) %>% 
select(-tmpcol, -rn) 

## # A tibble: 4 x 2 
##  A  B 
## <dbl> <chr> 
## 1  4  d 
## 2  5  e 
## 3  4  d 
## 4  2  b 

Myślę, że to rozwiązanie jest trochę prostsze (może bardzo mało) niż pierwsze.

+0

Tak, robi to, Steveb; doceniam to. – RBL

+0

Znakomity! Bardzo kompaktowy! Doceniam to! – RBL

2

Chodzi o to, aby dodać licznik duplikatów do każdego pliku, dzięki czemu można uzyskać unikalne dopasowanie dla każdego wystąpienia wiersza. Tabela danych jest dobra, ponieważ łatwo zliczyć duplikaty (z .N), a także zapewnia niezbędną funkcję (fsetdiff) dla operacji zestawu.

library(data.table) 

a1 <- data.table(A = c(1:5, 2, 4, 2), B = letters[c(1:5, 2, 4, 2)]) 
a2 <- data.table(A = c(1:3,2), B = letters[c(1:3,2)]) 

# add counter for duplicates 
a1[, i := 1:.N, .(A,B)] 
a2[, i := 1:.N, .(A,B)] 

# setdiff gets the exception 
# "all = T" allows duplicate rows to be returned 
fsetdiff(a1, a2, all = T) 

# A B i 
# 1: 4 d 1 
# 2: 5 e 1 
# 3: 4 d 2 
# 4: 2 b 3 
+0

To również działa dobrze! Doceniam to. – RBL

1

Oto kolejny rozwiązanie z dplyr:

library(dplyr) 
a1 %>% 
    arrange(A) %>% 
    group_by(A) %>% 
    filter(!(paste0(1:n(), A, B) %in% with(arrange(a2, A), paste0(1:n(), A, B)))) 

Wynik:

# A tibble: 4 x 2 
# Groups: A [3] 
     A  B 
    <dbl> <fctr> 
1  2  b 
2  4  d 
3  4  d 
4  5  e 

Ten sposób filtrowania pozwala uniknąć tworzenia dodatkowych niepotrzebnych kolumn, które trzeba później usunąć w końcowym wyjściu . Ta metoda również sortuje dane wyjściowe. Nie jestem pewien, czy tego chcesz.