【问题标题】:Create groups based on percent_rank in dplyr根据 dplyr 中的 percent_rank 创建组
【发布时间】:2015-11-06 18:13:19
【问题描述】:

我正在尝试根据dplyr 中某些值的百分比排名创建一些组。

下面的代码创建一个数据框,然后sapply 一个函数来确定组。缺点是我无法为tbl_postgres 工作,只能为数据帧工作。所以我很好奇是否有其他解决方案。

我曾考虑过使用 ntile 的东西,但我想创建的组有一些任意的截止点。此外,我没有太多运气让它与dplyr 一起工作(也许纯 sql 可能工作)。

library(dplyr)

n <- 100

df1 <- data.frame(idx = 1:n, x = rnorm(n))

df1 <- df1 %>%
            arrange(x) %>%
            mutate(pc_x = percent_rank(x))

index <- function(x) {
    if (x < 0) {
        return(NA)
    } else if (x < 0.3) {
        return(1)
    } else if (x < 0.7) {
        return(2)
    } else if (x <= 1) {
        return(3)
    } else {
        return(NA)
    }
}

df1 <- df1 %>%
            mutate(group = sapply(pc_x, index))

【问题讨论】:

标签: r dplyr data-manipulation


【解决方案1】:

也许cut 会满足您的需求:

library(dplyr)
n <- 100
set.seed(42)
df1 <- data.frame(idx = 1:n, x = rnorm(n))
df1 <- df1 %>%
    arrange(x) %>%
    mutate(pc_x = percent_rank(x))

我在breaks 中使用-1e9,因为cut 是“左开”,所以如果我使用breaks &lt;- c(0, ...),那么第一行将是NA 而不是1。

breaks <- c(-1e9, 0.3, 0.7, 1)
df1 %>%
    mutate(grp = cut(pc_x, breaks=breaks, labels=FALSE)) %>%
    group_by(grp)
## Source: local data frame [100 x 4]
## Groups: grp [3]
##      idx          x       pc_x   grp
##    (int)      (dbl)      (dbl) (int)
## 1     59 -2.9930901 0.00000000     1
## 2     18 -2.6564554 0.01010101     1
## 3     19 -2.4404669 0.02020202     1
## 4     39 -2.4142076 0.03030303     1
## 5     22 -1.7813084 0.04040404     1
## ..   ...        ...        ...   ...

【讨论】:

  • 谢谢。这正是我想要的。事实证明我的问题是重复的。
  • 我一直在问自己为什么在 dplyr 中找不到 cut 函数,直到我意识到这是来自 base R 的。你的方法的一个缺点是,因为它不在 dplyr 中,所以它没有不适用于本地数据框以外的任何内容。
  • 是的......因为我即将(但还没有)在数据库环境中使用 dplyr,所以我不知道。也许您可以在dplyr github 上提出一个问题来扩展ntile 以接受任意中断?这似乎是一个合理的用例,尽管我不确定底层的 SQL 翻译。
  • 在对这个问题的评论中,dplyr 的作者已经说过 cut 在列表中。 stackoverflow.com/questions/21714457/…
  • 不幸的是,那是 21 个月前的事了,current milestones 不包含任何对 ntile 的参考削减、中断或类似的增强。
【解决方案2】:

根据 @joranE 和 @krlmlr 对您在 GitHub 上发布的 issue 的建议,您可以使用 sql() 构建自己的自定义 sql 查询:

library(dplyr)
library(microbenchmark)

n <- 100
set.seed(42)
df <- data.frame(idx = 1:10e5, x = rnorm(n))
copy_to(my_db, df, "df")

mbm <- microbenchmark(
joranE = tbl(my_db, sql("
  SELECT x,
    CASE
      WHEN x > 0   AND x <= 0.3 THEN 1
      WHEN x > 0.3 AND x <= 0.6 THEN 2
      WHEN x > 0.6 AND x <= 1   THEN 3
      ELSE NULL
    END
    FROM df")),
krlmlr = tbl(my_db, sql("
  SELECT x,
    CASE
      WHEN x <= 0.3 THEN
        CASE WHEN x <= 0 THEN NULL
        ELSE 1
        END
      ELSE
        CASE WHEN x <= 0.6 THEN 2
        WHEN x <= 1 THEN 3
        ELSE NULL
      END
    END
    FROM df")),
times = 100
)

两种方法产生相似的结果:

#Unit: milliseconds
#   expr      min       lq     mean   median       uq       max neval cld
# joranE 3.070625 3.118589 3.548202 3.206681 3.307202 30.688142   100   a
# krlmlr 3.058583 3.109567 3.250952 3.205483 3.278453  3.933817   100   a

【讨论】:

  • 我终于有机会仔细看看这个。我在 github 页面上看到了它(我只是因为上面的建议才提交的),但感谢您在此处发布。一个非常有用的调整是在最后一个 END 附近添加一个 AS 语句。否则很难引用新变量。
  • @John - 您对此答案的编辑已获得原始发帖人的批准,但请避免将来对代码进行重大更改。建议的代码更改应在 cmets 中进行,而不是通过编辑,因为它正在更改帖子的内容。
  • @skrrgwasme 无意中点击了“接受”,回滚了。
  • @StevenBeaupré 请问你为什么回滚?
  • @John 不要误会我的意思。感谢您的努力,但我认为他们在这里放错了位置。编辑应该用于改进格式、拼写、语法、单词选择等——提高可读性和理解力但不会改变内容的事情。您的更改改变了代码的功能;这是一个重大的变化,应该是评论,而不是编辑。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-12
相关资源
最近更新 更多