Удалите дубликаты и манипулируйте данными в зависимости от условий из 2 столбцов. - PullRequest
0 голосов
/ 31 января 2020

У меня есть кадр данных, как в

+------+-----+----------+--------+
| from | to  | distance | weight |
+------+-----+----------+--------+
|    1 |   8 |        1 |     10 |
|    2 |   6 |        1 |      9 |
|    3 |   4 |        1 |      5 |
|    4 |   5 |        3 |      9 |
|    5 |   6 |        4 |      8 |
|    6 |   2 |        5 |      2 |
|    7 |   8 |        2 |      1 |
|    4 |   3 |        5 |      6 |
|    2 |   1 |        1 |      7 |
|    6 |   8 |        4 |      8 |
|    1 |   7 |        5 |      3 |
|    8 |   4 |        6 |      7 |
|    9 |   5 |        3 |      9 |
|   10 |   3 |        8 |      2 |
+------+-----+----------+--------+

Я хочу последовательно фильтровать данные на основе следующих критериев:

  1. Если число появляется в to в столбце его нельзя повторять ни в столбце to, ни в столбце from 1010 *
  2. Число в from можно повторить, если его соответствующий to является новым значением и недоступен в любая из ячеек в столбце to
  3. Я хочу повторять этот процесс до тех пор, пока все уникальные значения из объединенных from и to не появятся хотя бы один раз в одном из столбцов
  4. Если число в столбце from является новым числом и если соответствующее значение to уже присутствует в любом из столбцов, замените это to и значение расстояния на пустое

Таким образом, полученная таблица будет выглядеть так:

+------+-----+----------+--------+
| from | to  | Distance | weight |
+------+-----+----------+--------+
|    1 |   8 |        1 |     10 |
|    2 |   6 |        1 |      9 |
|    3 |   4 |        1 |      5 |
|    1 |   7 |        5 |      3 |
|    9 |   5 |        3 |      9 |
|   10 |     |          |      2 |
+------+-----+----------+--------+

1 Ответ

1 голос
/ 02 февраля 2020

Это попытка воспроизвести ожидаемый результат в соответствии с правилами ОП.

Я все еще пытаюсь найти решение, используя unique(), duplicated() для данных в широком формате, а также для преобразования в длинный формат.

Однако, здесь есть решение, использующее for l oop, который воспроизводит ожидаемый результат для данного образца набора данных:

library(data.table)
# append row numbers
setDT(DT)[, rn := .I]

# which values appear only once in the `to`` column?
single_to <- DT[, .N, by = to][N == 1L, to]
single_to
[1] 2 1 7
DT[, drop := NA]
for (i in seq_len(nrow(DT))) {
  print(i)
  print(DT[i])
  if (isTRUE(DT$drop[i])) next # row already has been eliminated
  act_to <- DT$to[i]
  # Rule 1: eliminate subsequent rows with repeated value in `to` column  
  DT[rn > i & (to == act_to), drop := TRUE]
  # Rule 1: eliminate subsequent rows with repeated value in `from` column 
  # Rule 2: but keep rows where value is unique in the `to` column  
  DT[rn > i & (from == act_to) & !(to %in% single_to), drop := TRUE]
  DT[i, drop := FALSE]
  print(DT[])
}
[1] 1
   from to distance weight rn drop
1:    1  8        1     10  1   NA
    from to distance weight rn  drop
 1:    1  8        1     10  1 FALSE
 2:    2  6        1      9  2    NA
 3:    3  4        1      5  3    NA
 4:    4  5        3      9  4    NA
 5:    5  6        4      8  5    NA
 6:    6  2        5      2  6    NA
 7:    7  8        2      1  7  TRUE
 8:    4  3        5      6  8    NA
 9:    2  1        1      7  9    NA
10:    6  8        4      8 10  TRUE
11:    1  7        5      3 11    NA
12:    8  4        6      7 12  TRUE
13:    9  5        3      9 13    NA
14:   10  3        8      2 14    NA
[1] 2
   from to distance weight rn drop
1:    2  6        1      9  2   NA
    from to distance weight rn  drop
 1:    1  8        1     10  1 FALSE
 2:    2  6        1      9  2 FALSE
 3:    3  4        1      5  3    NA
 4:    4  5        3      9  4    NA
 5:    5  6        4      8  5  TRUE
 6:    6  2        5      2  6    NA
 7:    7  8        2      1  7  TRUE
 8:    4  3        5      6  8    NA
 9:    2  1        1      7  9    NA
10:    6  8        4      8 10  TRUE
11:    1  7        5      3 11    NA
12:    8  4        6      7 12  TRUE
13:    9  5        3      9 13    NA
14:   10  3        8      2 14    NA
[1] 3
   from to distance weight rn drop
1:    3  4        1      5  3   NA
    from to distance weight rn  drop
 1:    1  8        1     10  1 FALSE
 2:    2  6        1      9  2 FALSE
 3:    3  4        1      5  3 FALSE
 4:    4  5        3      9  4  TRUE
 5:    5  6        4      8  5  TRUE
 6:    6  2        5      2  6    NA
 7:    7  8        2      1  7  TRUE
 8:    4  3        5      6  8  TRUE
 9:    2  1        1      7  9    NA
10:    6  8        4      8 10  TRUE
11:    1  7        5      3 11    NA
12:    8  4        6      7 12  TRUE
13:    9  5        3      9 13    NA
14:   10  3        8      2 14    NA
[1] 4
   from to distance weight rn drop
1:    4  5        3      9  4 TRUE
[1] 5
   from to distance weight rn drop
1:    5  6        4      8  5 TRUE
[1] 6
   from to distance weight rn drop
1:    6  2        5      2  6   NA
    from to distance weight rn  drop
 1:    1  8        1     10  1 FALSE
 2:    2  6        1      9  2 FALSE
 3:    3  4        1      5  3 FALSE
 4:    4  5        3      9  4  TRUE
 5:    5  6        4      8  5  TRUE
 6:    6  2        5      2  6 FALSE
 7:    7  8        2      1  7  TRUE
 8:    4  3        5      6  8  TRUE
 9:    2  1        1      7  9    NA
10:    6  8        4      8 10  TRUE
11:    1  7        5      3 11    NA
12:    8  4        6      7 12  TRUE
13:    9  5        3      9 13    NA
14:   10  3        8      2 14    NA
[1] 7
   from to distance weight rn drop
1:    7  8        2      1  7 TRUE
[1] 8
   from to distance weight rn drop
1:    4  3        5      6  8 TRUE
[1] 9
   from to distance weight rn drop
1:    2  1        1      7  9   NA
    from to distance weight rn  drop
 1:    1  8        1     10  1 FALSE
 2:    2  6        1      9  2 FALSE
 3:    3  4        1      5  3 FALSE
 4:    4  5        3      9  4  TRUE
 5:    5  6        4      8  5  TRUE
 6:    6  2        5      2  6 FALSE
 7:    7  8        2      1  7  TRUE
 8:    4  3        5      6  8  TRUE
 9:    2  1        1      7  9 FALSE
10:    6  8        4      8 10  TRUE
11:    1  7        5      3 11    NA
12:    8  4        6      7 12  TRUE
13:    9  5        3      9 13    NA
14:   10  3        8      2 14    NA
[1] 10
   from to distance weight rn drop
1:    6  8        4      8 10 TRUE
[1] 11
   from to distance weight rn drop
1:    1  7        5      3 11   NA
    from to distance weight rn  drop
 1:    1  8        1     10  1 FALSE
 2:    2  6        1      9  2 FALSE
 3:    3  4        1      5  3 FALSE
 4:    4  5        3      9  4  TRUE
 5:    5  6        4      8  5  TRUE
 6:    6  2        5      2  6 FALSE
 7:    7  8        2      1  7  TRUE
 8:    4  3        5      6  8  TRUE
 9:    2  1        1      7  9 FALSE
10:    6  8        4      8 10  TRUE
11:    1  7        5      3 11 FALSE
12:    8  4        6      7 12  TRUE
13:    9  5        3      9 13    NA
14:   10  3        8      2 14    NA
[1] 12
   from to distance weight rn drop
1:    8  4        6      7 12 TRUE
[1] 13
   from to distance weight rn drop
1:    9  5        3      9 13   NA
    from to distance weight rn  drop
 1:    1  8        1     10  1 FALSE
 2:    2  6        1      9  2 FALSE
 3:    3  4        1      5  3 FALSE
 4:    4  5        3      9  4  TRUE
 5:    5  6        4      8  5  TRUE
 6:    6  2        5      2  6 FALSE
 7:    7  8        2      1  7  TRUE
 8:    4  3        5      6  8  TRUE
 9:    2  1        1      7  9 FALSE
10:    6  8        4      8 10  TRUE
11:    1  7        5      3 11 FALSE
12:    8  4        6      7 12  TRUE
13:    9  5        3      9 13 FALSE
14:   10  3        8      2 14    NA
[1] 14
   from to distance weight rn drop
1:   10  3        8      2 14   NA
    from to distance weight rn  drop
 1:    1  8        1     10  1 FALSE
 2:    2  6        1      9  2 FALSE
 3:    3  4        1      5  3 FALSE
 4:    4  5        3      9  4  TRUE
 5:    5  6        4      8  5  TRUE
 6:    6  2        5      2  6 FALSE
 7:    7  8        2      1  7  TRUE
 8:    4  3        5      6  8  TRUE
 9:    2  1        1      7  9 FALSE
10:    6  8        4      8 10  TRUE
11:    1  7        5      3 11 FALSE
12:    8  4        6      7 12  TRUE
13:    9  5        3      9 13 FALSE
14:   10  3        8      2 14 FALSE

Результат пока отличается от ожидаемого результата

result <- DT[!(drop)]
result
   from to distance weight rn  drop
1:    1  8        1     10  1 FALSE
2:    2  6        1      9  2 FALSE
3:    3  4        1      5  3 FALSE
4:    6  2        5      2  6 FALSE
5:    2  1        1      7  9 FALSE
6:    1  7        5      3 11 FALSE
7:    9  5        3      9 13 FALSE
8:   10  3        8      2 14 FALSE

Строки с 1 по 3, 11, 13 и 14 соответствуют ожидаемому результату, но строки 6 и 9 сохраняются здесь, потому что значения 2 и 1 являются уникальными в столбце to.

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

Чтобы удалить их, result преобразуется из широкого в длинный формат и упорядочивается по номеру строки:

ldt <- melt(result, "rn", c("to", "from"))[order(rn)]
ldt
    rn variable value
 1:  1       to     8
 2:  1     from     1
 3:  2       to     6
 4:  2     from     2
 5:  3       to     4
 6:  3     from     3
 7:  6       to     2
 8:  6     from     6
 9:  9       to     1
10:  9     from     2
11: 11       to     7
12: 11     from     1
13: 13       to     5
14: 13     from     9
15: 14       to     3
16: 14     from    10

Теперь нам нужно определить номера строк дубликатов, которые относятся к single_to значениям:

ldt[duplicated(value) & variable == "to" & value %in% single_to]
   rn variable value
1:  6       to     2
2:  9       to     1

Эти строки удалены из result от anti-join :

result2 <-
  result[!ldt[duplicated(value) & variable == "to" & value %in% single_to], on = .(rn)]
result2
   from to distance weight rn  drop
1:    1  8        1     10  1 FALSE
2:    2  6        1      9  2 FALSE
3:    3  4        1      5  3 FALSE
4:    1  7        5      3 11 FALSE
5:    9  5        3      9 13 FALSE
6:   10  3        8      2 14 FALSE

Теперь это почти соответствует ожидаемому результату. Только правило 4 должно быть реализовано. Для этого используется тот же подход, что и раньше: изменение формы на длинный, определение номеров строк и объединение. Однако здесь используется объединение обновлений :

ldt2 <- melt(unique(result2, by = "from"), "rn", c("to", "from"))[order(rn)]
result2[ldt2[duplicated(value)], on = .(rn), c("to", "distance") := NA_integer_]
result2
   from to distance weight rn  drop
1:    1  8        1     10  1 FALSE
2:    2  6        1      9  2 FALSE
3:    3  4        1      5  3 FALSE
4:    1  7        5      3 11 FALSE
5:    9  5        3      9 13 FALSE
6:   10 NA       NA      2 14 FALSE

Обсуждение

Это решение не претендует на эффективность с точки зрения скорости кодирования или исполнения. Он просто нацелен на воспроизведение ожидаемого результата из данного набора данных.

Это требует гораздо большего тестирования. Например, OP запросил в правиле 3

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

Реализуя правила 1 и 2, мы не проверяем, наконец, выполнено ли это условие.

Кроме того, я считаю, что могут быть и другие способы достижения той же цели.

...