2016-06-30 3 views
1

У меня есть два столбца, хотелось бы сохранить только не коммутативные строки. Для данных ниже мой вывод должен содержать одну комбинацию (1 2). т. е. для моего запроса (1 2) совпадает с (2 1). Есть простой способ сделать это в R. Уже пробовал транспонирование. и удерживая верхнюю трагическую матрицу. но это становится болью, повторно переносящей данные.Удаление повторяющихся строк из фрейма данных в R

A B prob 
1 2 0.1 
1 3 0.2 
1 4 0.3 
2 1 0.3 
2 3 0.1 
2 4 0.4 

Мой окончательный вывод должен быть:

A B prob 
1 2 0.1 
1 3 0.2 
1 4 0.3 
2 3 0.1 
2 4 0.4 
+0

Я не уверен, что это то, что вы хотите, но моя первая мысль заключалась в том, чтобы удалить только все строки, где 'B' меньше, чем' A'. –

+0

К сожалению, в моей фактической проблеме столбики a и b не являются номерами, они являются кодами продуктов – Mukul

ответ

2

Мы можем использовать data.table. Преобразуйте 'data.frame' в 'data.table' (setDT(df1)), сгруппированные по pmin(A, B) и pmax(A,B), if, количество строк больше 1, мы получаем первую строку или else, возвращаем строки.

library(data.table) 
setDT(df1)[, if(.N >1) head(.SD, 1) else .SD ,.(A=pmin(A, B), B= pmax(A, B))] 
# A B prob 
#1: 1 2 0.1 
#2: 1 3 0.2 
#3: 1 4 0.3 
#4: 2 3 0.1 
#5: 2 4 0.4 

Или мы просто использовали duplicated на pmax, pmin выхода для возврата логического индекса и подмножества данных, основанных на этом.

setDT(df1)[!duplicated(cbind(pmax(A, B), pmin(A, B)))] 
# A B prob 
#1: 1 2 0.1 
#2: 1 3 0.2 
#3: 1 4 0.3 
#4: 2 3 0.1 
#5: 2 4 0.4 
1

Это должно работать:

d <- data.frame(A=rep(1:2, each=4), B=rep(1:4, 2), p=rnorm(n=8)) 
> d 
    A B   p 
1 1 1 -1.26282557 
2 1 2 -0.03627707 
3 1 3 1.50063527 
4 1 4 -0.30038114 
5 2 1 -0.01509190 
6 2 2 0.13634069 
7 2 3 -0.39612927 
8 2 4 -0.10895007 
l <- 1:nrow(d) # Create an index vector 
v <- apply(cbind(d$A, d$B), 1, sort) # Make (1, 2) look like (2, 1) 
v <- paste(v[1,], v[2,]) # Create vector where (1, 2) and (2, 1) both look like '1 2' 
fun <- function(x) return(x[1]) # Function for tapply to only return the first match for the pattern 
i <- tapply(l, v, fun) # get relevant index 
res <- d[i, ] # Create result vektor 
> res 
    A B   p 
1 1 1 -0.6742351 
2 1 2 -1.5895396 
3 1 3 -1.5975784 
4 1 4 -1.4764792 
6 2 2 -0.1682946 
7 2 3 -0.5799141 
8 2 4 2.4104019 

Имейте в виду, что это будет использовать первое вхождение шаблона.

7

Мы можем независимо друг от друга sort() каждая строка, а затем использовать !duplicated(), чтобы найти, какие строки для сохранения:

df[!duplicated(t(apply(df[1:2],1L,sort))),]; 
## A B prob 
## 1 1 2 0.1 
## 2 1 3 0.2 
## 3 1 4 0.3 
## 5 2 3 0.1 
## 6 2 4 0.4 

данных

df <- data.frame(A=c(1L,1L,1L,2L,2L,2L),B=c(2L,3L,4L,1L,3L,4L),prob=c(0.1,0.2,0.3,0.3,0.1,0.4 
)); 

Объяснение

Первый шаг должен извлечь только две колонки, представляющие интерес:

df[1:2]; 
## A B 
## 1 1 2 
## 2 1 3 
## 3 1 4 
## 4 2 1 
## 5 2 3 
## 6 2 4 

Тогда мы самостоятельно сортировать каждую строку с apply() и sort():

apply(df[1:2],1L,sort); 
##  [,1] [,2] [,3] [,4] [,5] [,6] 
## [1,] 1 1 1 1 2 2 
## [2,] 2 3 4 2 3 4 

Как вы можете видеть, apply() возвращает свои результаты в неожиданные перестановки, так что мы должны исправить с t() подготовиться к предстоящей duplicated() вызова:

t(apply(df[1:2],1L,sort)); 
##  [,1] [,2] 
## [1,] 1 2 
## [2,] 1 3 
## [3,] 1 4 
## [4,] 1 2 
## [5,] 2 3 
## [6,] 2 4 

Теперь мы можем использовать duplicated(), чтобы получить логический вектор, указывающий, какие строки являются дубликатами предыдущих строк:

duplicated(t(apply(df[1:2],1L,sort))); 
## [1] FALSE FALSE FALSE TRUE FALSE FALSE 

Затем инвертировать логический вектор с отрицанием, чтобы получить только те строки, которые не дубликатами все предыдущие строки:

!duplicated(t(apply(df[1:2],1L,sort))); 
## [1] TRUE TRUE TRUE FALSE TRUE TRUE 

Наконец, мы можем использовать полученный логический вектор индексировать только те из рядов df, которые не являются дубликатами всех предыдущих строк:

df[!duplicated(t(apply(df[1:2],1L,sort))),]; 
## A B prob 
## 1 1 2 0.1 
## 2 1 3 0.2 
## 3 1 4 0.3 
## 5 2 3 0.1 
## 6 2 4 0.4 

Поэтому первый будет сохраняться каждый набор после сортировки, остальная часть будет удалена.


Отличное предложение от @RichardScriven; мы можем заменить t() вызов с MARGIN аргументом duplicated(), который, вероятно, будет немного быстрее:

df[!duplicated(apply(df[1:2],1L,sort),MARGIN=2L),]; 
## A B prob 
## 1 1 2 0.1 
## 2 1 3 0.2 
## 3 1 4 0.3 
## 5 2 3 0.1 
## 6 2 4 0.4 
+0

Это сработало, спасибо большое, можете ли вы также объяснить, что происходит на бэкэнде. В каком случае он сохранит 1 2 один или 2 1, я хотел бы сохранить 1 2 единицы. – Mukul

+0

Пожалуйста, взгляните на мой ответ. В основном сценарий bgoldst работает в бэкэнд таким же образом. За исключением части 'paste'. Будет возвращено только FIRST. – FlorianSchunke

+0

Вам фактически не нужно транспонировать. Вы можете «дублировать» (применять (df [-3], 1, sort), MARGIN = 2) 'для проверки дублирования по столбцам. Но в любом случае работает. –

0

Вот еще одно решение с использованием базы R. Идея заключается в том, чтобы искать во второй половине df (с использованием sapply), если есть какие-либо дублируется там. Затем мы возвращаемся обратно secondHalf вектор. Мы также удалим эти строки с df.

n <- nrow(df) 
secondHalf <- sapply(seq(n/2), function(i) nrow(df[df$A==df[i,2] & df$B==df[i,1],])) 
# [1] 1 0 0 
toRemove <- ((floor(n/2)+1):n)*secondHalf 
df <- df[-toRemove,] 

    # A B prob 
# 1 1 2 0.1 
# 2 1 3 0.2 
# 3 1 4 0.3 
# 5 2 3 0.1 
# 6 2 4 0.4 
Смежные вопросы