Как найти похожие строки в таблице? - PullRequest
1 голос
/ 17 января 2020

У меня есть проблема с данными опроса, когда существуют поддельные ответы, которые приходят с одного и того же IP-адреса, которые очень похожи - в основном люди просматривают и делают одни и те же ответы большую часть времени. Тем не менее, у меня есть некоторые подлинные ответы с того же IP-адреса, поэтому я не могу просто отфильтровать их.

Я надеюсь найти «похожие» строки таким же образом, как и поиск дубликатов. ряды, но где ограничения ослаблены, и результатом является оценка сходства (ie, какой процент значений в строке 1 соответствует в строке 2). Я не смог найти пакет для этого.

require(tidyverse)
require(janitor)
x <- structure(list(group = c("a", "a", "a", "a", "a", "a", "a", "a", 
                              "a", "a", "b", "b", "b", "b", "b", "c", "c", "c", "c", "c"), 
val1 = c(2, 1, 1, 1, 2, 2, 2, 1, 1, 1, 2, 2, 1, 1, 2, 1, 1, 1, 2, 1), 
val2 = c(1, 1, 1, 2, 1, 2, 2, 3, 3, 3, 1, 1, 3, 2, 3, 2, 3, 1, 3, 1), 
val3 = c(4, 3, 5, 1, 1, 5, 3, 5, 5, 4, 2, 3, 4, 5, 3, 2, 3, 1, 3, 4), 
val4 = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L), val5 = c(1, 2, 2, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 1, 2, 2, 1, 2), 
val6 = c(2, 2, 1, 2, 1, 2, 1, 1, 1, 2, 2, 2, 1, 2, 1, 2, 2, 2, 1, 2)), 
row.names = c(NA, -20L), class = c("tbl_df", "tbl", "data.frame"))

x %>% get_dupes()

#No variable names specified - using all columns.

# A tibble: 2 x 8
#  group  val1  val2  val3  val4  val5  val6 dupe_count
#  <chr> <dbl> <dbl> <dbl> <int> <dbl> <dbl>      <int>
#1 a         1     3     5     1     1     1          2
#2 a         1     3     5     1     1     1          2


x %>% get_dupes(val1, val2, val4, val5, val6)

# # A tibble: 14 x 8
# val1  val2  val4  val5  val6 dupe_count group  val3
# <dbl> <dbl> <int> <dbl> <dbl>      <int> <chr> <dbl>
#   1     1     1     1     2     2          3 a         3
# 2     1     1     1     2     2          3 c         1
# 3     1     1     1     2     2          3 c         4
# 4     1     2     1     2     2          2 a         1
# 5     1     2     1     2     2          2 b         5
# 6     1     3     1     1     1          3 a         5
# 7     1     3     1     1     1          3 a         5
# 8     1     3     1     1     1          3 b         4
# 9     1     3     1     2     2          2 a         4
# 10     1     3     1     2     2          2 c         3
# 11     2     1     1     1     2          2 a         4
# 12     2     1     1     1     2          2 b         2
# 13     2     3     1     1     1          2 b         3
# 14     2     3     1     1     1          2 c         3

Обратите внимание, что есть только 2 фактических дубликата, но это в основном из-за val3. Если мы удалим его из проверки на наличие дубликатов, их будет 14.

Я хотел бы выяснить, что для этих 14 строк они имеют ncol(x)-1 сходств или 1 различие. Мне все равно, где он находится, поэтому он должен быть динамичным c.

1 Ответ

0 голосов
/ 18 января 2020

Это подход, основанный главным образом на двойном l oop сравнении строк. По сути, вам нужно сравнить каждую из n строк с n-1 другими строками и вычислить сходство строк. Функция ниже делает именно это.

Если вы думаете о более общих способах справиться с дублированием, вы можете проверить пакет RecordLinkage, который позволит вам вычислить более общие оценки сходства между полями и поиграть с некоторыми неконтролируемые алгоритмы классификации для определения дублирования.

Однако вот функциональный подход:

require(tidyverse)
require(janitor)

x <- structure(list(group = c("a", "a", "a", "a", "a", "a", "a", "a", 
                              "a", "a", "b", "b", "b", "b", "b", "c", "c", "c", "c", "c"), 
                    val1 = c(2, 1, 1, 1, 2, 2, 2, 1, 1, 1, 2, 2, 1, 1, 2, 1, 1, 1, 2, 1), 
                    val2 = c(1, 1, 1, 2, 1, 2, 2, 3, 3, 3, 1, 1, 3, 2, 3, 2, 3, 1, 3, 1), 
                    val3 = c(4, 3, 5, 1, 1, 5, 3, 5, 5, 4, 2, 3, 4, 5, 3, 2, 3, 1, 3, 4), 
                    val4 = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L), val5 = c(1, 2, 2, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 1, 2, 2, 1, 2), 
                    val6 = c(2, 2, 1, 2, 1, 2, 1, 1, 1, 2, 2, 2, 1, 2, 1, 2, 2, 2, 1, 2)), 
               row.names = c(NA, -20L), class = c("tbl_df", "tbl", "data.frame"))

x %>% get_dupes()
#> No variable names specified - using all columns.
#> # A tibble: 2 x 8
#>   group  val1  val2  val3  val4  val5  val6 dupe_count
#>   <chr> <dbl> <dbl> <dbl> <int> <dbl> <dbl>      <int>
#> 1 a         1     3     5     1     1     1          2
#> 2 a         1     3     5     1     1     1          2

# A helper function for comparing rows and returning the number of non-matches
row_comp <- function(row1, row2) {
  sum(row1 != row2)
}

# The function for comparing all rows. You can set the 
# number of non-matches you'll tolerate 
get_n_dupes <- function(x, unmatch.tol = 1) {
  n <- nrow(x)
  dist.mat <- vector(length = n)
  for (i in seq_len(n)){
    dist.mat[i] <- any(apply(x[-i,], 1, 
                             function(row){row_comp(row, x[i,])}) <= unmatch.tol)
  }
  x[dist.mat,]
}

# By default this returns values that are a match bar-one
get_n_dupes(x)
#> # A tibble: 6 x 7
#>   group  val1  val2  val3  val4  val5  val6
#>   <chr> <dbl> <dbl> <dbl> <int> <dbl> <dbl>
#> 1 a         1     3     5     1     1     1
#> 2 a         1     3     5     1     1     1
#> 3 b         2     3     3     1     1     1
#> 4 c         1     1     1     1     2     2
#> 5 c         2     3     3     1     1     1
#> 6 c         1     1     4     1     2     2

# If you up it to two potential non-matches nearly all the rows find a match
x %>% get_n_dupes(unmatch.tol = 2)
#> # A tibble: 19 x 7
#>    group  val1  val2  val3  val4  val5  val6
#>    <chr> <dbl> <dbl> <dbl> <int> <dbl> <dbl>
#>  1 a         2     1     4     1     1     2
#>  2 a         1     1     3     1     2     2
#>  3 a         1     1     5     1     2     1
#>  4 a         1     2     1     1     2     2
#>  5 a         2     1     1     1     1     1
#>  6 a         2     2     5     1     1     2
#>  7 a         2     2     3     1     1     1
#>  8 a         1     3     5     1     1     1
#>  9 a         1     3     5     1     1     1
#> 10 a         1     3     4     1     2     2
#> 11 b         2     1     2     1     1     2
#> 12 b         2     1     3     1     2     2
#> 13 b         1     3     4     1     1     1
#> 14 b         1     2     5     1     2     2
#> 15 b         2     3     3     1     1     1
#> 16 c         1     3     3     1     2     2
#> 17 c         1     1     1     1     2     2
#> 18 c         2     3     3     1     1     1
#> 19 c         1     1     4     1     2     2
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...