【问题标题】:Implementation of skyline query or efficient frontier天际线查询或高效前沿的实现
【发布时间】:2013-03-29 13:33:24
【问题描述】:

我知道必须有一个简单的答案,但不知何故我似乎找不到它......

我有一个包含 2 个数字列的数据框。 我想从中删除具有属性的行,即数据框中至少存在另一行,两个列的值都大于该行中的值。

如果我有

    Col1 Col2  
1     2    3  
2     4    7  
3     5    6  

我想删除第一行,因为第二行满足属性并且只保留第 2 行和第 3 行。

非常感谢!

【问题讨论】:

  • 我无法设置编辑,因为它只有空格,但您的表格可以从使用代码格式中受益:在每行前多放 4 个空格,它会以相同的格式出现您使用过,并使其更具可读性。
  • 谢谢杰夫,我试图弄清楚如何做到这一点,但我失败了。

标签: r select dataframe


【解决方案1】:

这个问题被数据库管理员称为“天际线查询”(他们可能有其他算法),被经济学家称为“有效前沿”。 绘制数据可以清楚地说明我们在寻找什么。

n <- 40
d <- data.frame(
  x = rnorm(n),
  y = rnorm(n)
)
# We want the "extreme" points in the following plot
par(mar=c(1,1,1,1))
plot(d, axes=FALSE, xlab="", ylab="")
for(i in 1:n) {
  polygon( c(-10,d$x[i],d$x[i],-10), c(-10,-10,d$y[i],d$y[i]), 
  col=rgb(.9,.9,.9,.2))
}

算法如下:沿第一个坐标对点进行排序, 保留每个观察值,除非它比上次保留的观察值更差。

d <- d[ order(d$x, decreasing=TRUE), ]
result <- d[1,]
for(i in seq_len(nrow(d))[-1] ) {
  if( d$y[i] > result$y[nrow(result)] ) {
    result <- rbind(result, d[i,])  # inefficient
  } 
}
points(result, cex=3, pch=15)

【讨论】:

  • 是的,这正是我想做的,但我认为用“C”方式做这件事不合适,谢谢!
  • +1 并感谢您的出色回答。我特别感谢您与天际线查询建立联系,然后包括一个情节来说明为什么它有这个名字!我在下面添加了一个答案,受您的启发,用更 R 风格的矢量化结构替换了 for() 循环。
  • 第一次读到算法的巧妙之处,我并没有真正欣赏到,再次感谢!
  • 多么美妙的解释!
  • 谢谢!我认为这也被称为“帕累托最优”:一种资源分配状态,在这种状态下,不可能在不使至少一个人的情况变得更糟的情况下使任何一个人过得更好[维基百科“帕累托效率,一种资源分配状态,其中如果不让至少一个人变得更糟,就不可能让任何一个人变得更好”],突出显示的数据代表“帕累托前沿”。
【解决方案2】:
d <- matrix(c(2, 3, 4, 7, 5, 6), nrow=3, byrow=TRUE)
d2 <- sapply(d[, 1], function(x) x < d[, 1]) & 
      sapply(d[, 2], function(x) x < d[, 2])
d2 <- apply(d2, 2, any)
result <- d[!d2, ]

【讨论】:

  • 嗯,我不完全理解这一点,但我的感觉是它不会单独查看每一列。例如,如果您这样做 > d
  • 对,我误解了你的要求。我读它的意思是“如果 B 行中的每个数字都大于 A 行中的每个数字,则排除 A 行”。因为 4 和 8 不都大于 3 和 7(即 4 不大于 7),所以不满足标准,不排除该行。现在我知道您对 B 行中的值是否大于 A 行相应列中的值感兴趣。我已经编辑了答案以更正它(我认为)。
  • (现在应该排除那些存在另一行且每列具有较大值的行。我相信文森特的解决方案将排除存在另一行具有更大或等于的行每列中的值,尽管这可能确实是您打算在问题中写的内容..?)
【解决方案3】:

一行:

d <- matrix(c(2, 3, 4, 7, 5, 6), nrow=3, byrow=TRUE)
d[!apply(d,1,max)<max(apply(d,1,min)),]

     [,1] [,2]
[1,]    4    7
[2,]    5    6

编辑:鉴于您对 jbaums 响应的精确性,以下是分别检查两列的方法。

d <- matrix(c(2, 3, 3, 7, 5, 6, 4, 8), nrow=4, byrow=TRUE)
d[apply(d,1,min)>min(apply(d,1,max)) ,]

     [,1] [,2]
[1,]    5    6
[2,]    4    8

【讨论】:

  • 谢谢,但我需要独立处理每一列,例如,如果我有 d both 列中没有其他行具有更高的值。
【解决方案4】:

这是一个sqldf解决方案,其中DF是data的数据框:

library(sqldf)
sqldf("select * from DF a
 where not exists (
   select * from DF b
   where b.Col1 >= a.Col1 and b.Col2 >  a.Col2  
      or b.Col1 >  a.Col1 and b.Col2 >= a.Col2
 )"
)

【讨论】:

    【解决方案5】:

    编辑 (2015-03-02): 要获得更有效的解决方案,请参阅 Patrick Roocks 的 rPref,这是“数据库首选项和天际线计算”的包,(也链接到他的回答如下)。为了表明它找到了与我的代码相同的解决方案,我在此处的原始答案中附加了一个使用它的示例。


    借鉴 Vincent Zoonekynd 的启发性回应,这里有一个完全矢量化的算法,并且可能更有效:

    set.seed(100)
    d <- data.frame(x = rnorm(100), y = rnorm(100))
    
    D   <- d[order(d$x, d$y, decreasing=TRUE), ]
    res <- D[which(!duplicated(cummax(D$y))), ]
    #             x         y
    # 64  2.5819589 0.7946803
    # 20  2.3102968 1.6151907
    # 95 -0.5302965 1.8952759
    # 80 -2.0744048 2.1686003
    
    
    # And then, if you would prefer the rows to be in 
    # their original order, just do:
    d[sort(as.numeric(rownames(res))), ]
    #            x         y
    # 20  2.3102968 1.6151907
    # 64  2.5819589 0.7946803
    # 80 -2.0744048 2.1686003
    # 95 -0.5302965 1.8952759
    

    或者,使用 rPref 包:

    library(rPref)
    psel(d, high(x) | high(y))
    #             x         y
    # 20  2.3102968 1.6151907
    # 64  2.5819589 0.7946803
    # 80 -2.0744048 2.1686003
    # 95 -0.5302965 1.8952759
    

    【讨论】:

      【解决方案6】:

      这个问题很老了,但同时有一个新的解决方案。我希望在这里做一些自我推销是可以的:我开发了一个包rPref,由于 C++ 算法,它可以进行高效的 Skyline 计算。安装 rPref 包后,可以通过以下方式查询问题(假设 df 是数据集的名称):

      library(rPref)
      psel(df, high(Col1) | high(Col2))
      

      这只会删除那些元组,其中一些其他元组在两个维度上都更好。

      如果需要另一个元组在一个维度上严格更好(在另一个维度上更好或相等),请改用high(Col1) * high(Col2)

      【讨论】:

      • 哈!只需点击进入您的个人资料,看到指向rPref 的链接,然后直接来到这里记录一下现在有更好的解决方案。将在我的答案中添加一个注释,将人们指向这里,否则你的答案几乎会消失在杂草中!
      • 这个包是否处理具有两个以上变量的有效边界?假设我们想要包含 n 变量
      • 是的,确实如此。变量/维度的数量没有主要限制,high(x1) | high(x2) | ... | high(xn) 也是可能的,但是计算成本(以及因此运行时间)会随着维度的数量而增加。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多