尝试在自定义函数中重用dplyr代码块;我错过了什么?

时间:2016-03-13 07:47:45

标签: r dplyr

Hadley说,“每当你复制并粘贴一段代码超过两次时,你应该考虑编写一个函数” - 我经常在dplyr中写这个链:

df %>%
  group_by(col) %>%
  summarise(n = n()) %>%
  mutate(percent = round((n / sum(n)) * 100, 2) %>%
  arrange(desc(n))

我想用两个参数创建一个函数:数据框和变量或列名。这就是我现在正在尝试的事情:

value_counts = function(df, col) {
  group_by_(df, col) %>%
  summarise_(n = n()) %>%
  mutate_(percent = round((n / sum(n)) * 100, 2)) %>%
  arrange(desc(n))
}

它不起作用,我在本网站上尝试了其他一些建议,但我不太明白它们是如何工作的,例如:

value_counts = function(df, col) {
  group_by_(df, .dots = col) %>%
  summarise_(n = n()) %>%
  mutate_(percent = round((n / sum(n)) * 100, 2)) %>%
  arrange(desc(n))
}

我真的想编写一个使用管道并依赖于dplyr的函数。我可以继续编写一遍又一遍的代码,但我想开始在R中编写有用的函数以节省时间。

我是geom_text()的忠实粉丝,喜欢快速轻松地从数据框中获取dplyr中的信息,这样我就可以快速获得大量图表!

我应阅读的任何资源或其后的链接都会很有用。谢谢!

2 个答案:

答案 0 :(得分:2)

当您将列名称作为字符串传递给它们时,仅将NSE函数替换为其标准等效函数。在您的情况下,仅在group_by_函数中,col是一个变量,假设您想要调用您的函数value_counts(df, "some_column")。中间人npercent不依赖于变量,因此根本不需要更改。

value_counts <- function(df, col) {
  group_by_(df, col) %>%
    summarise(n = n()) %>%
    mutate(percent = round(n / sum(n) * 100, 2)) %>%
    arrange(desc(n))
}
value_counts(iris, "Species")

答案 1 :(得分:2)

@ jenesaisquoi的答案很棒,但每当我编写dplyr - y函数时,我都会尝试以与该软件包类似的方式编写它们。我想有一对SE和NSE函数,你可以使用裸变量名。

有几点需要注意。

  • 我摆脱了管道(%>%),使它们更快,更容易调试。管道在交互式使用中非常方便,但我倾向于在编程功能时避免它们。
  • 我在使用特定于包的功能的地方使用了::。这意味着您现在无需加载dplyr即可使用该功能。
  • 我认为没有理由将使用限制在一列,所以改为使用...参数,接受根据需要分组的数量。
  • 有关vignette(NSE)包的使用以及lazyeval处理dplyr...的方式的详细信息,请参阅.dots

功能

value_counts <- function(df, ...) {
  value_counts_(df, .dots = lazyeval::lazy_dots(...))
}

value_counts_ <- function(df, ..., .dots) {
  dots <- lazyeval::all_dots(.dots, ..., all_named = TRUE)
  df <- dplyr::group_by_(df, .dots = dots)
  df <- dplyr::summarise(df, n = n())
  df <- dplyr::mutate(df, percent = round(n / sum(n) * 100, 2))
  df <- dplyr::arrange(df, desc(n))
  return(df)
}

实施例

value_counts(mtcars, cyl)
value_counts(mtcars, cyl, vs)

value_counts_(mtcars, ~cyl)
value_counts_(mtcars, ~cyl, ~vs)
value_counts_(mtcars, .dots = list(~cyl, ~vs))

你可以轻松地将它们与其他dplyr动词一起管道:

library(dplyr)
mtcars %>%
  filter(cyl != 4) %>%
  value_counts()