【问题标题】:How to sort a data.table based on a set of inequality constraints?如何根据一组不等式约束对 data.table 进行排序?
【发布时间】:2019-03-10 09:03:00
【问题描述】:

我有一组“x

例如,

library(data.table)
set.seed(0)
ineqs <- unique(data.table(
  X = sample(letters, 10, replace = T),
  Rel = "<",
  Y = sample(letters, 10, replace = T)
))
ineqs
    X Rel Y
 1: x   < b
 2: g   < f
 3: j   < e
 4: o   < r
 5: x   < j
 6: f   < u
 7: x   < m
 8: y   < s
 9: r   < z
10: q   < j

所以,如果我从一个排序字母表开始,

dt <- data.table(Foo = letters)
    Foo
 1:   a
 2:   b
 3:   c
---    
24:   x
25:   y
26:   z

如何调整行顺序以满足我的限制条件?另外,我确信我的约束是有效的(即没有一个约束相互矛盾)。

【问题讨论】:

  • 不清楚,你的意思是ineqs[order(X, Y)]
  • @ANG 没有。我有兴趣对dt 的行进行排序,而不是ineqs。但是,我希望 dt 的行满足约束。例如,带有“x”的行应该在带有“b”的行之前。

标签: r sorting data.table


【解决方案1】:
library(igraph)
g = ineqs[, graph_from_edgelist(cbind(X,Y), directed=TRUE)]
o = names(topo_sort(g))

dt[, v := factor(Foo, levels = o, ordered=TRUE)]
dt[order(v)]


    Foo    v
 1:   x    x
 2:   g    g
 3:   o    o
 4:   y    y
 5:   q    q
 6:   b    b
 7:   m    m
 8:   f    f
 9:   r    r
10:   s    s
11:   j    j
12:   u    u
13:   z    z
14:   e    e
15:   a <NA>
16:   c <NA>
17:   d <NA>
18:   h <NA>
19:   i <NA>
20:   k <NA>
21:   l <NA>
22:   n <NA>
23:   p <NA>
24:   t <NA>
25:   v <NA>
26:   w <NA>
    Foo    v

所有不在ineqs 中的词都排在最后。

如果您的关系图有循环,您应该在topo_sort 中收到警告。这告诉您您的任务在ineqs 中的某些术语没有得到很好的定义。

【讨论】:

  • 非常有趣。我很好奇:由于订单不一定是唯一的,这如何选择一个特定的订单?是随机的吗?
  • @MauritsEvers 是的,我也很好奇。文档只是说“每个 DAG 至少有一个拓扑排序,并且可能有很多。此函数返回其中可能的拓扑排序。”
  • 非常整洁,我非常喜欢这个!我不得不承认我对图论不是很熟悉,但似乎topological sorting 的运行时间与顶点和边的数量呈线性关系(这显然比我的穷举快得多 “创建所有排列”方法)。 Wiki 文章还列出了不同的算法并(简要地)讨论了如何创建其他解决方案。
  • @MauritsEvers 酷,感谢您提供的信息。我只是想“这看起来像一个有向图”,并在 igraph 文档中搜索排序函数 (igraph.org/r/doc),但对此并不了解。
  • 啊哈!我知道这是一个图论问题,但完全忘记了topological sorting 是一回事。谢谢弗兰克!
【解决方案2】:

也许我理解错了,但这不是一个简单的排序,也不一定存在一个唯一的顺序。

让我举个例子。考虑条件

    X Rel Y
1: x   < b
2: g   < f

各种订单都是可以想象的

        x < g < f < b
g     < x     <     b   <   f
g     < x     < f < b
g < f < x         < b
        x < g <     b     < f
        x <         b < g < f

所有这些都满足前两行中列出的条件。


我很想看看详尽而粗略的实现会如何做,我们预先计算所有可能的排列,然后消除那些不满足成对条件的排列。

为了说明,我们将仅使用 4 个字母和成对条件数据的前两行。

这是我的结果:

  1. 首先,我们定义四个字母并使用gtools::permutations 计算所有排列。

    char <- c("b", "f", "g", "x")
    
    library(gtools)
    perm <- as.data.frame(permutations(length(char), length(char), char))
    

    有 24 种可能的排列。

  2. 我们现在读入成对条件数据

    df <- read.table(text =
        "X Rel Y
    x   < b
    g   < f", header = T)
    
    # Convert factors to character vectors
    df[] <- sapply(df, as.character)
    
  3. 我们现在循环抛出排列和成对条件,并标记排列数据中不满足任何成对条件的行。

    rmv <- c()
    for (i in 1:nrow(perm)) {
        # Here we loop throw all possible permutations and eliminate those that
        # do not fulfil the pairwise conditions
        for (j in 1:nrow(df)) {
            # Here we loop throw the pairwise conditions
            cond <- eval(parse(text = sprintf("`%s`", df[j, "Rel"])))(
                which(perm[i, ] == df[j, "X"]),
                which(perm[i, ] == df[j, "Y"]))
            if (cond == FALSE) {
                rmv <- c(rmv, i)
                break
            }
        }
    }
    
  4. 剩下的满足条件的排列就是

    perm[-rmv, ]
    #   V1 V2 V3 V4
    #16  g  f  x  b
    #17  g  x  b  f
    #18  g  x  f  b
    #20  x  b  g  f
    #23  x  g  b  f
    #24  x  g  f  b
    

【讨论】:

  • 我同意可以有 多个 排序来满足我的限制,但我不确定如何实现其中的任何一个。你能想出一种机制来实现一次有效的排序吗?
  • @Ben 正如我所说,这不是一个小问题。一种详尽而粗略的方法是预先计算所有排序(对于字母a-z),然后消除那些不满足条件的排序。然后,您将获得满足成对条件的那些排序。我不知道这对于您的真实数据有多现实,因为订购数量会随着字母数量的增加而迅速增加。
  • @Ben 我已经对 4 个字母和前两行进行了详尽而粗略的方法来展示一个潜在的起点。请看一看。
  • 做得很好。我很感激我的努力,但我的真实数据太大,这种技术无法工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-21
  • 2013-10-22
  • 2021-04-05
  • 2020-05-15
相关资源
最近更新 更多