选择唯一的x和y对以最小化值

时间:2019-02-20 08:33:51

标签: r dplyr data.table

我需要为每个ID.y(形成唯一对)选择一个唯一的ID.x,以从最小的距离值开始最小化距离值。我觉得这有点像数独难题,因为每个x和y只能使用一次,因此每对的信息都可以匹配其他对。

在下面的示例中,ID.x 55与ID.x 56的匹配更好,因为ID.x 56与ID.y 2的匹配更好。类似地,ID.x 58可以与ID.y 4匹配,因为任何其他可用选项都将具有更大的距离,然后ID.y 5可以在距离4处获得ID.x59。但是,ID.y 7无法匹配,因为ID.x 61和ID.x 62同样接近。

示例:

DT = data.table(
  ID.x = c("55", "55", "55", "55", "55", "55", "55", "56", "56", "56", "56", "56", "56", "56", "57", "57", "57", "57", "57", "57", "57", "58", "58", "58", "58", "58", "58", "58", "59", "59", "59", "59", "59", "59", "59", "60", "60", "60", "60", "60", "60", "60", "61", "61", "61", "61", "61", "61", "61", "62", "62", "62", "62", "62", "62", "62"),
  ID.y = c("1", "2", "3", "4", "5", "6", "7", "1", "2", "3", "4", "5", "6", "7", "1", "2", "3", "4", "5", "6", "7", "1", "2", "3", "4", "5", "6", "7", "1", "2", "3", "4", "5", "6", "7", "1", "2", "3", "4", "5", "6", "7", "1", "2", "3", "4", "5", "6", "7", "1", "2", "3", "4", "5", "6", "7"),
  distance = c("2", "3", "3", "4", "6", "6", "7", "2", "1", "2", "5", "5", "5", "6", "4", "4", "3", "5", "5", "5", "6", "5", "5", "5", "4", "4", "5", "6", "7", "7", "7", "6", "4", "6", "7", "6", "6", "6", "6", "4", "2", "5", "7", "7", "7", "7", "5", "5", "5", "6", "6", "6", "6", "4", "4", "5")
  )

目标:

   ID.x ID.y distance
1:   55    1        2
2:   56    2        1
3:   57    3        3
4:   58    4        4
5:   59    5        4
6:   60    6        2
7:   NA    7        NA

第一次尝试inspired by this question无效:

DT[DT[, .I[distance == min(distance)], by=ID.x]$V1][DT[, .I[1], by = ID.y]$V1]

更新: 为了响应@ chinsoon12和@paweł-chabros的回答,以下是更新的data.table,该表修复了一些问题。它交换x和y(我最初的问题是将x与y匹配,但更自然的解释是将x与y匹配)。本示例删除了ID.y 7的歧义匹配。在本示例中,最小距离匹配ID.x63。另外,我还添加了一个新的ID.y 8,以阐明何时不可能进行明确匹配(它匹配ID。 x 64和65也一样)。答案不应随意选择匹配项。

DT = data.table(
  ID.y = c("55", "55", "55", "55", "55", "55", "55", "55", "56", "56", "56", "56", "56", "56", "56", "56", "57", "57", "57", "57", "57", "57", "57", "57", "58", "58", "58", "58", "58", "58", "58", "58", "59", "59", "59", "59", "59", "59", "59", "59", "60", "60", "60", "60", "60", "60", "60", "60", "61", "61", "61", "61", "61", "61", "61", "61", "62", "62", "62", "62", "62", "62", "62", "62", "63", "63", "63", "63", "63", "63", "63", "63", "64", "64", "64", "64", "64", "64", "64", "64", "65", "65", "65", "65", "65", "65", "65", "65"),
  ID.x = c("1", "2", "3", "4", "5", "6", "7", "8", "1", "2", "3", "4", "5", "6", "7", "8", "1", "2", "3", "4", "5", "6", "7", "8", "1", "2", "3", "4", "5", "6", "7", "8", "1", "2", "3", "4", "5", "6", "7", "8", "1", "2", "3", "4", "5", "6", "7", "8", "1", "2", "3", "4", "5", "6", "7", "8", "1", "2", "3", "4", "5", "6", "7", "8", "1", "2", "3", "4", "5", "6", "7", "8", "1", "2", "3", "4", "5", "6", "7", "8", "1", "2", "3", "4", "5", "6", "7", "8"),
  distance = c(2, 3, 3, 4, 6, 6, 7, 15, 2, 1, 2, 5, 5, 5, 6, 15, 4, 4, 3, 5, 5, 5, 6, 15, 5, 5, 5, 4, 4, 5, 6, 15, 7, 7, 7, 6, 4, 6, 7, 15, 6, 6, 6, 6, 4, 2, 5, 15, 7, 7, 7, 7, 5, 5, 6, 15, 6, 6, 6, 6, 4, 4, 10, 15, 11, 11, 11, 11, 11, 11, 5, 12, 11, 11, 11, 11, 11, 11, 11, 1, 11, 11, 11, 11, 11, 11, 11, 1)
  )

预期结果:

   ID.y ID.x distance
1:   55    1        2
2:   56    2        1
3:   57    3        3
4:   58    4        4
5:   59    5        4
6:   60    6        2
7:   63    7        5
8:   NA    8        NA

I'm using this code is to complete a fuzzy join using stringdist_join, as described in this question.我有两个需要匹配的数据集(因此ID.x和ID.y)。就我而言,我的测验前和测验分数需要与多个不可靠的特征相匹配。

3 个答案:

答案 0 :(得分:1)

我不清楚我为什么ID.x 62和ID.y 7距离5不可行。

假设ID.x 62,ID.y 7和距离5是可以接受的,则可以使用data.table

setorder(DT, distance)
choseny <- c()
ans <- DT[,
    {
        y <- setdiff(ID.y, choseny)[1L]
        choseny <- c(choseny, y)  
        .(ID.y=y, dist=.SD[ID.y==y, distance[1L]])
    },
    by=.(ID.x)]
setorder(ans, ID.x)[]

输出:

   ID.x ID.y dist
1:   55    1    2
2:   56    2    1
3:   57    3    3
4:   58    4    4
5:   59    5    4
6:   60    6    2
7:   61 <NA> <NA>
8:   62    7    5

答案 1 :(得分:0)

我不确定这是否真的是理想的解决方案,但这应该会有所帮助。虽然不是超级优雅,但是看起来很像所需的输出。

 DT[, .(ID.y
     , distance
     , Row.Num = rank(distance)
     , Row.Num.ID = rank(ID.y)), by = list(ID.x)][, .SD[Row.Num == min(Row.Num) ], by = ID.x][, .SD[Row.Num.ID == min(Row.Num.ID) ], by = ID.x] 
 >  ID.x ID.y distance Row.Num Row.Num.ID
1:   55    1        2     1.0          1
2:   56    2        1     1.0          2
3:   57    3        3     1.0          3
4:   58    4        4     1.5          4
5:   59    5        4     1.0          5
6:   60    6        2     1.0          6
7:   61    5        5     2.0          5
8:   62    5        4     1.5          5

答案 2 :(得分:0)

我不太了解data.table,因此我只能给您tidyverse解决方案。但这也许会对您有所帮助:)

library(tidyverse)

ID_y <- unique(DT$ID.y)

DT %>%
  as_tibble() %>%
  group_by(ID.x) %>%
  mutate(min_dist = min(distance)) %>%
  arrange(min_dist) %>%
  nest() %>%
  mutate(data = data %>% map(~ {
    min_row <- .x %>%
      filter(ID.y %in% ID_y) %>%
      filter(distance == min(distance)) %>%
      slice(1)
    ID_y <<- ID_y[ID_y != min_row$ID.y]
    min_row
  })) %>%
  unnest() %>%
  select(-min_dist) %>%
  arrange(ID.x)

我正在保存ID.y的所有唯一值。然后,我计算所有组合的最小距离,并以此最小距离进行排列,以首先在map循环中解决那些组合。在过滤了最小距离之后,我从向量中删除了ID.y,因此其他ID.x仅在左边的ID.y中进行搜索。