n个对象,两个对象的每个组合的值,找到R中每个对象的最小值

时间:2016-12-08 14:38:40

标签: r dplyr qgis

我想在数据帧中找到与对象关联的最小值。数据框包含两列,表示对象的所有组合,以及每个组合的值列。它看起来像这样:

     id_A        id_B     dist
     206         208 2385.5096
     207         208  467.8890
     207         209  576.4631
...
     208         209  1081.539
     208         210  8214.439
...

我尝试了以下推荐的dplyr功能:

df %>% 
 group_by(id_A) %>% 
 slice(which.min(dist))

但它不会产生所需的输出:

id_A        id_B      dist
...
207         208       467.8890
208         209       1081.5393
...

请注意,对于id 208,ID为207的组合具有最低值,但与id 208无关(当它在grouped_by列中时)。

我写了一个正确的功能,但是因为我有很多条目,所以这是慢的方法。它是一个循环,通过包含特定id的所有条目对数据进行子集化,然后在该子集中找到最小值,并将该值与该id相关联。

你有什么想法,如何做到这一点,例如使用dplyr

1 个答案:

答案 0 :(得分:0)

问题归结为需要一个长(而不是宽)的数据格式。首先,这里有一些可重现的数据(使用来自dplyr的管道):

df <-
  LETTERS[1:4] %>%
  combn(2) %>%
  t %>%
  data.frame() %>%
  mutate(val = 1:n()) %>%
  setNames( c("id_A", "id_B", "dist") )

给出:

  id_A id_B dist
1    A    B    1
2    A    C    2
3    A    D    3
4    B    C    4
5    B    D    5
6    C    D    6

我们想要的是一对列,使每个类别与其行的距离匹配。为此,我使用gather中的tidyr。它创建了新的列,告诉我们数据来自哪个列以及保留的值。在这里,我们告诉它从列id_Aid_B中提取每个ID条目的类别(然后根据需要复制dist列)

df %>%
  gather(whichID, Category, id_A, id_B)

给出

   dist whichID Category
1     1    id_A        A
2     2    id_A        A
3     3    id_A        A
4     4    id_A        B
5     5    id_A        B
6     6    id_A        C
7     1    id_B        B
8     2    id_B        C
9     3    id_B        D
10    4    id_B        C
11    5    id_B        D
12    6    id_B        D

然后,我们可以 data.frame传递给group_by,然后使用summarise向我们提供我们想要的任何信息。我知道你没有要求最大值,但我只是为了展示你可以用来得到你想要的任何类型结果的一般语法来包括它:

df %>%
  gather(whichID, Category, id_A, id_B) %>%
  group_by(Category) %>%
  summarise(minDist = min(dist)
            , maxDist = max(dist))

返回:

  Category minDist maxDist
     <chr>   <int>   <int>
1        A       1       3
2        B       1       5
3        C       2       6
4        D       3       6

我只是看了一下这个问题并意识到你想要显示哪个比较具有最小值。这是一种方法,通过跟踪匹配的索引(以便在gather时复制它)然后从原始df中提取正确的行并将两个比较值粘贴在一起来执行此操作:

df %>%
  mutate(whichComparison = 1:n()) %>%
  gather(whichID, Category, id_A, id_B) %>%
  group_by(Category) %>%
  summarise(minDist = min(dist)
            , whichMin = whichComparison[which.min(dist)]
            , maxDist = max(dist)
            , whichMax = whichComparison[which.max(dist)]) %>%
  mutate(
    minComp = sapply(whichMin, function(x){
      paste(df[x, "id_A"], df[x, "id_B"], sep = " vs " )})
    , maxComp = sapply(whichMax, function(x){
      paste(df[x, "id_A"], df[x, "id_B"], sep = " vs " )})
  )

返回

  Category minDist whichMin maxDist whichMax minComp maxComp
     <chr>   <int>    <int>   <int>    <int>   <chr>   <chr>
1        A       1        1       3        3  A vs B  A vs D
2        B       1        1       5        5  A vs B  B vs D
3        C       2        2       6        6  A vs C  C vs D
4        D       3        3       6        6  A vs D  C vs D

如果你真的想要一个列给出哪个比较给出了最小值(以及输出中的最大值),你可以改为使用索引同时从id_Aid_B中提取原始df,删除与感兴趣的类别匹配的那个,然后使用包use_first_valid_of中的janitor来抓住您感兴趣的那个。因为这会产生大量的中间列,我使用select来清理备份:

df %>%
  mutate(whichComparison = 1:n()) %>%
  gather(whichID, Category, id_A, id_B) %>%
  group_by(Category) %>%
  summarise(minDist = min(dist)
            , maxDist = max(dist)
            , whichMin = whichComparison[which.min(dist)]
            , whichMax = whichComparison[which.max(dist)]) %>%
  mutate(
    minA = df$id_A[whichMin]
    , minB = df$id_B[whichMin]
    , maxA = df$id_A[whichMax]
    , maxB = df$id_B[whichMax]
  ) %>%
  mutate_each(funs(ifelse(. == Category, NA, as.character(.)) )
              , minA:maxB) %>%
  mutate(minComp = use_first_valid_of(minA, minB)
         , maxComp = use_first_valid_of(maxA, maxB)) %>%
  select(-(whichMin:maxB))

返回:

  Category minDist maxDist minComp maxComp
     <chr>   <int>   <int>   <chr>   <chr>
1        A       1       3       B       D
2        B       1       5       A       D
3        C       2       6       A       D
4        D       3       6       A       C

另一种方法是首先将距离对转换为矩阵。在这里,我首先以相反的顺序复制比较,以确保矩阵完整(使用tidyrspread):

bind_rows(
  df
  , rename(df, id_A = id_B, id_B = id_A)
) %>%
  spread(id_B, dist)

返回:

  id_A  A  B  C  D
1    A NA  1  2  3
2    B  1 NA  4  5
3    C  2  4 NA  6
4    D  3  5  6 NA

然后,我们只是跨越行应用,就像我们使用距离矩阵(可能是您的数据实际开始的位置)一样:

bind_rows(
  df
  , rename(df, id_A = id_B, id_B = id_A)
) %>%
  spread(id_B, dist) %>%
  mutate(
    minDist = apply(as.matrix(.[, -1]), 1, min, na.rm = TRUE)
    , minComp = names(.)[apply(as.matrix(.[, -1]), 1, which.min) + 1]
    , maxDist = apply(as.matrix(.[, -1]), 1, max, na.rm = TRUE)
    , maxComp = names(.)[apply(as.matrix(.[, -1]), 1, which.max) + 1]
  ) %>%
  select(Category = `id_A`
         , minDist:maxComp)

返回:

  Category minDist minComp maxDist maxComp
1        A       1       B       3       D
2        B       1       A       5       D
3        C       2       A       6       D
4        D       3       A       6       C