Как удалить подмножества или надмножества из данных без создания матрицы? - PullRequest
0 голосов
/ 10 декабря 2018

Предположим, у вас есть эти data.frame:

d <- data.frame(ID=c(1,1,1,2,2,3,3,4,4,6,6,6,8,8),FP=-c(1,2,3,1,2,3,2,1,4,1,4,3,1,4)*100)

Если вы создадите матрицу непредвиденных расходов:

table(d$FP,d$ID)

       1 2 3 4 6 8
  -400 0 0 0 1 1 1
  -300 1 0 1 0 1 0
  -200 1 1 1 0 0 0
  -100 1 1 0 1 1 1

Вы видите, что набор имен столбцов строки '-400'(c(4,6,8)) является подмножеством набора имен столбцов строки' -100 '(c(1,2,4,6,8)).
В таких случаях мне нужно удалить строки' superset ', т.е. здесь строка' -100 '.
Аналогично, вы можете видеть, что набор имен строк столбца '8' (c(-400,-100)) является подмножеством набора имен строк столбца '6' (c(-400,-300,-100)).
В таких случаяхВ случаях, когда мне нужно удалить столбцы «подмножество», то есть здесь столбец «8», но с дополнительным соображением я объясню ниже.

Важно, мне нужно сделать это до / без создание матрицы, потому что с типом d data.frames, который я использую, это часто вызывает проблемы с памятью.

Способ, которым я в настоящее время делаю удаление надмножества непосредственно на исходных данных, следующий:

  1. агрегирует данные по FP, используя unique в качестве функции, предотвращая упрощение для vecтор;сортировка по длине
  2. удаление дублированных наборов идентификаторов, поскольку любой дубликат по определению также является циклом
  3. надстроек для каждого набора идентификаторов во всех других наборах идентификаторов большей длины и удаляет последние, если ониsupersets

Т.е.:

t1 <- d
duprows <- aggregate(ID~FP,t1,function(x) {sort(unique(x))}, simplify=FALSE)
t1 <- t1[t1$FP %in% duprows[!(duplicated(duprows$ID)),][["FP"]],]
duprows["NIDs"] <- sapply(duprows[["ID"]],length)
duprows <- duprows[order(duprows[["NIDs"]]),]

# not run
#duprows
#    FP            ID NIDs
#1 -400       4, 6, 8    3
#2 -300       1, 3, 6    3
#3 -200       1, 2, 3    3
#4 -100 1, 2, 4, 6, 8    5

i = 1
Nconsts <- dim(duprows)[[1]]

while (i <= (Nconsts-1)) {

    FPs_to_remove = numeric()
    current_NIDs = duprows[["NIDs"]][i]

    #find the index of the first NIDs greater than the current one
    greater_NIDs_index = which(duprows[["NIDs"]]>current_NIDs)[1]

    if (is.na(greater_NIDs_index)) {break} else {

        ID.i = unlist(duprows[["ID"]][i])

        for (j in greater_NIDs_index:Nconsts) {

            ID.j = unlist(duprows[["ID"]][j])

            matches = (ID.i %in% ID.j)

            if (all(matches)) {
                FPs_to_remove = c(FPs_to_remove, duprows[["FP"]][j]) }
        }
        duprows = duprows[!(duprows[["FP"]] %in% FPs_to_remove),]
        Nconsts = dim(duprows)[[1]]
        i = i + 1
    }
}

t1 <- t1[t1$FP %in% duprows$FP,]

# not run
#duprows
#    FP      ID NIDs
#1 -400 4, 6, 8    3
#2 -300 1, 3, 6    3
#3 -200 1, 2, 3    3

В этом примере это работает достаточно быстро, но, как вы можете себе представить, с большими наборами данных это довольно катастрофично.

Вопрос : можете ли вы предложить более эффективный способ достижения того же результата?

Я посмотрел виньетки из пакета data.table, которые мне посоветовали в другом посте для другого заданияи в этом случае это действительно значительно ускорило вычисления.На самом деле я мог бы использовать тот же пакет для удаления дубликатов строк.Однако для вышеупомянутой задачи я не вижу непосредственного способа воспользоваться быстрыми операциями data.table.Любое предложение будет очень кстати.

Как я уже говорил, другая связанная задача, которую мне нужно выполнить, - это удаление столбцов subset , но с дополнительным рассмотрением числового свойства P.

Я принял стратегию, аналогичную приведенной выше:

  1. агрегирует данные по ID, используя unique в качестве функции, предотвращающей упрощение для вектора;сортировка по длине, а теперь также по убыванию P и ID в случае наличия связей в P
  2. удаляет дублированные наборы FP, потому что любой дубликат по определению также является подмножеством;наибольшее значение P сохраняется, потому что строки уже отсортированы
  3. цикл для каждого набора FP на всех других наборах FP большей длины, и удалите первый, если он является подмножеством и имеет меньшее значение P

Т.е.:

P_vs_id <- data.frame(ID=c(1,2,3,4,6,8),P=c(0.5,0.8,0.1,0.6,0.9,0.75))
dupcols <- aggregate(FP~ID,t1,function(x) {sort(unique(x))}, simplify=FALSE)
dupcols <- merge(dupcols,P_vs_id,by="ID")
dupcols["NFPs"] <- sapply(dupcols[["FP"]],length)
dupcols <- dupcols[order(dupcols$NFPs,-dupcols$P,dupcols$ID),]
t1 <- t1[t1$ID %in% dupcols[!(duplicated(dupcols$FP)),][["ID"]],]

В этом примере был удален столбец с ID 4, который был дубликатом ID 8 и имел более низкий P.

Затем для точки 3:

dupcols <- aggregate(FP~ID,t1,function(x) {sort(unique(x))}, simplify=FALSE)
dupcols <- merge(dupcols,P_vs_id,by="ID")
dupcols["NFPs"] <- sapply(dupcols[["FP"]],length)
dupcols <- dupcols[order(dupcols$NFPs,-dupcols$P,dupcols$ID),]

# not run
#dupcols
#  ID         FP    P NFPs
#2  2       -200 0.80    1
#4  8       -400 0.75    1
#3  6 -400, -300 0.90    2
#1  1 -300, -200 0.50    2

i = 1
NIDs <- dim(dupcols)[[1]]

while (i <= (NIDs-1)) {

  current_NFPs = dupcols[["NFPs"]][i]

  #find the index of the first NFPs larger than the current one
  larger_NFPs_index = which(dupcols[["NFPs"]]>current_NFPs)[1]

  if (is.na(larger_NFPs_index)) {break} else {

    FP.i = unlist(dupcols[["FP"]][i])
    P.i = unlist(dupcols[["P"]][i])
    j = larger_NFPs_index
    flag = 0

    while (j <= NIDs) {

      FP.j = unlist(dupcols[["FP"]][j])
      P.j = unlist(dupcols[["P"]][j])

      matches = (FP.i %in% FP.j)

      if (all(matches) & (P.i <= P.j)) {
        dupcols = dupcols[-i,]
        flag = 1
        break} else {j = j + 1}
    }

    NIDs = dim(dupcols)[[1]]
    if (flag == 0) {i = i + 1}
  }
}

# not run
#dupcols
#  ID         FP    P NFPs
#2  2       -200 0.80    1
#3  6 -400, -300 0.90    2
#1  1 -300, -200 0.50    2

t1 <- t1[(t1$ID %in% dupcols$ID),]

В этом примере был удален столбец с ID 8, который соответствовал набору поднаборов FP из FP * из 1080 * 6 ибыл ниже P.Он не удалил столбец с ID 2, несмотря на тот факт, что это подмножество столбца с ID 1, потому что первый имеет более высокий P.

Опять же, все прошло довольно хорошодля этого небольшого data.frame, но для типа данных, которые я обрабатываю, это может занять несколько часов.
Мне удалось немного ускорить его, исключив из проверки наборы FP.i, которые никогда не могут быть подмножествами каких-либодругой набор, но это оказало лишь незначительное влияние.
И учтите, что часто необходимо несколько раз повторить удаление надмножества и подмножества, потому что, как вы можете себе представить, удаление определенных строк или определенных столбцов иногда приводит к некоторому изменению матрицыэто делает необходимым дальнейший запуск.

Итак ... любая помощь, делающая это более эффективным, действительно будет много значить.

Спасибо!


РЕДАКТИРОВАТЬ после дальнейшего изучения

Я обнаружил, что xtabs может создавать разреженные матрицы на случай непредвиденных обстоятельств;возможно, это обойдёт ошибки памяти, которые я получил с полными матрицами.Затем, поскольку описанные выше задачи, по-видимому, довольно сильно связаны с концепцией линейной зависимости, я мог бы, возможно, использовать пакет Matrix для выполнения QR-декомпозиции и взять его оттуда.
Похоже ли это на хорошийпуть вперед?Или QR будет таким же плохим, как мои петли?

Я еще раз (и короткую попытку) посмотрел на data.table.Помимо ускорения поиска данных в исходном файле data.frame, я не знаю, можно ли улучшить описанные мной операции.Я нашел, как написать функцию, которая сообщает, должна ли быть удалена данная строка или нет, но потом мне не удалось заставить ее работать в data.table;Я все еще недостаточно знаком с синтаксисом.


EDIT 2

Подход матричной алгебры оказался тупиком, по крайней мере, настолько, насколькоЯ мог бы сказать.
С другой стороны, немного изучив синтаксис data.table, я создал скрипт, который работает значительно лучше, чем приведенный выше (см. Ниже).

Тем не менее, я подозреваю,Я не использую потенциал data.table в лучшем виде, поэтому, если любой опытный пользователь не возражает проверить приведенный ниже сценарий и посоветовать, что можно сделать лучше, было бы замечательно.

d <- data.frame(ID=c(1,1,1,2,2,3,3,4,4,6,6,6,8,8),FP=-c(1,2,3,1,2,3,2,1,4,1,4,3,1,4)*100)

#
# SETUP
#

require(data.table)

# transfer or original data.frame data into a data.table

t1 <- data.table(d)

#
# REMOVE DUPLICATED ROWS
#

# count of how many ID's there are for each FP group

t1[,NIDs:=length(ID),by="FP"]

# sort

setorder(t1,NIDs,FP,ID)

# remove FP's that contain duplicate sets of ID's (duplicate rows)

duprows <- t1[,.(ID=list(c(ID))),by="FP"]
duprows[,is.dupl:=duplicated(ID)]
t1 <- t1[(FP %in% duprows[is.dupl == FALSE,FP])]

#
# REMOVE DUPLICATED COLUMNS
#

# count of how many FP's there are for each ID group

t1[,NFPs:=length(FP),by="ID"]

# sort

setorder(t1,-NFPs,ID,FP)

# remove ID's that contain duplicate sets of FP's (duplicate columns)

dupcols <- t1[,.(FP=list(c(FP))),by="ID"]
dupcols[,is.dupl:=duplicated(FP)]
t1 <- t1[(ID %in% dupcols[is.dupl == FALSE,ID])]

#
# REMOVE SUPERSET ROWS
#

# count of how many ID's there are for each FP group

t1[,NIDs:=length(ID),by="FP"]

# sort

setorder(t1,NIDs,FP,ID)

# for each FP group, write the first and last ID (they are already sorted)

t1[,ID.f:=first(c(ID)),by="FP"]
t1[,ID.l:=last(c(ID)),by="FP"]

# create an index for each FP group

t1[,FP.ind:=.GRP,by="FP"]

# initialise FP_to_remove column

t1[,FP_to_remove:=FALSE]

# mark FP groups that contain at least one ID corresponding to only one FP: the ID's in such FP groups can never be a subset of the ID's in another FP group

t1[,unique_ID:=any(NFPs==1),by="FP"]

# calculate the maximal FP group index

FP.ind.max <- t1[nrow(t1),FP.ind]

# for each FP group, check if its ID's are a subset of the ID's of other FP groups, and if so, mark the latter for removal

i = 1
while (i < FP.ind.max) {
  FP.i = t1[FP.ind == i,FP][1]
  ID.i = t1[FP == FP.i,c(ID),by=FP]$V1
  NIDs.i = t1[FP.ind == i,NIDs][1]
  ID.f.i = t1[FP.ind == i,ID.f][1]
  ID.l.i = t1[FP.ind == i,ID.l][1]  
  if ((t1[FP.ind == i,unique_ID][1] == FALSE) & (t1[FP.ind == i,FP_to_remove][1] == FALSE)) {
    t1[(ID.f <= ID.f.i & ID.l >= ID.l.i & FP.ind > i & NIDs > NIDs.i & FP_to_remove == FALSE),FP_to_remove:=all(ID.i %in% c(ID)),by=FP.ind]
  }
  i = i + 1
}
t1 <- t1[FP_to_remove == FALSE]

#
# REMOVE DUPLICATED COLUMNS (which may appear after removing superset rows)
#

# count of how many FP's there are for each ID group

t1[,NFPs:=length(FP),by="ID"]

# sort
setorder(t1,-NFPs,ID,FP)

# remove ID's that contain duplicate sets of FP's (duplicate columns)

dupcols <- t1[,.(FP=list(c(FP))),by="ID"]
dupcols[,is.dupl:=duplicated(FP)]
t1 <- t1[(ID %in% dupcols[is.dupl == FALSE,ID])]

#
# REMOVE SUBSET COLUMNS
#

# count of how many ID's there are for each FP group

t1[,NIDs:=length(ID),by="FP"]

# create an index for each ID group

t1[,ID.ind:=.GRP,by="ID"]

# for each ID group, write the first and last FP (they are already sorted)

t1[,FP.f:=first(c(FP)),by="ID"]
t1[,FP.l:=last(c(FP)),by="ID"]

# initialise ID_to_remove column

t1[,ID_to_remove:=FALSE]

# mark ID groups that contain at least one FP corresponding to only one ID: the FP's in such ID groups can never be a subset of the FP's in another ID group

t1[,unique_FP:=any(NIDs==1),by="ID"]

# calculate the maximal ID group index

ID.ind.max <- t1[nrow(t1),ID.ind]

# for each ID group, check if its FP's are a superset of the FP's of other ID groups, and if so, mark the latter for removal

i = 1
while (i < ID.ind.max) {
  ID.i = t1[ID.ind == i,ID][1]
  FP.i = t1[ID == ID.i,c(FP),by=ID]$V1
  NFPs.i = t1[ID.ind == i,NFPs][1]
  FP.f.i = t1[ID.ind == i,FP.f][1]
  FP.l.i = t1[ID.ind == i,FP.l][1]  
  if (t1[ID.ind == i,ID_to_remove][1] == FALSE) {
    t1[(unique_FP == FALSE & FP.f >= FP.f.i & FP.l <= FP.l.i & ID.ind > i & NFPs < NFPs.i & ID_to_remove == FALSE),ID_to_remove:=all(c(FP) %in% FP.i),by=ID.ind]
  }
  i = i + 1
}
t1 <- t1[ID_to_remove == FALSE]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...