{"action":"created","issue":{"url":"https://api.github.com/repos/hadley/purrr/issues/43","labels_url":"https://api.github.com/repos/hadley/purrr/issues/43/labels{/name}","comments_url":"https://api.github.com/repos/hadley/purrr/issues/43/comments","events_url":"https://api.github.com/repos/hadley/purrr/issues/43/events","html_url":"https://github.com/hadley/purrr/issues/43","id":55366042,"number":43,"title":"Making lowliner better understand data frames: by_group()","user":{"login":"lionel-","id":4465050,"avatar_url":"https://avatars.githubusercontent.com/u/4465050?v=3","gravatar_id":"","url":"https://api.github.com/users/lionel-","html_url":"https://github.com/lionel-","followers_url":"https://api.github.com/users/lionel-/followers","following_url":"https://api.github.com/users/lionel-/following{/other_user}","gists_url":"https://api.github.com/users/lionel-/gists{/gist_id}","starred_url":"https://api.github.com/users/lionel-/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lionel-/subscriptions","organizations_url":"https://api.github.com/users/lionel-/orgs","repos_url":"https://api.github.com/users/lionel-/repos","events_url":"https://api.github.com/users/lionel-/events{/privacy}","received_events_url":"https://api.github.com/users/lionel-/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":18,"created_at":"2015-01-24T10:14:43Z","updated_at":"2015-02-19T15:12:40Z","closed_at":null,"body":"This implements a functional that takes a grouped table and feed each group to `.f`. It returns a data frame and handles both mutating and summarising operations, the only constraint being that the number of returned rows is the same inside each group. The grouping variables are recycled to match the output size. If `.f` does not return a data frame, `by_group()` returns a list-column.\r\n\r\nI also wrote `set_groups()` to set the grouping attributes because it looks a bit funny to have\r\n```{r}\r\n  df %>%\r\n    group_by(col) %>%\r\n    by_group(fun)\r\n```\r\nand also this way we have a non-NSE version of `group_by()` that handles vectors of names and column positions.\r\n\r\nIn action:\r\n```{r}\r\n# Action operation\r\nmtcars %>%\r\n  set_groups(\"cyl\") %>%\r\n  by_group(partial(lm, mpg ~ disp))\r\n```\r\n```{r}\r\n## + + Source: local data frame [3 x 2]\r\n## \r\n##   cyl     out\r\n## 1   4 <S3:lm>\r\n## 2   6 <S3:lm>\r\n## 3   8 <S3:#lm>\r\n```\r\n\r\n```{r}\r\n# Summarizing operation\r\nmtcars %>%\r\n  set_groups(c(\"cyl\", \"am\")) %>%\r\n  by_group(map, mean)\r\n```\r\n```{r}\r\n## + + Source: local data frame [6 x 11]\r\n## \r\n##   cyl am    mpg    disp      hp   drat     wt   qsec    vs   gear   carb\r\n## 1   4  0 22.900 135.867  84.667 3.7700 2.9350 20.970 1.000 3.6667 1.6667\r\n## 2   4  1 28.075  93.612  81.875 4.1837 2.0423 18.450 0.875 4.2500 1.5000\r\n## 3   6  0 19.125 204.550 115.250 3.4200 3.3887 19.215 1.000 3.5000 2.5000\r\n## 4   6  1 20.567 155.000 131.667 3.8067 2.7550 16.327 0.000 4.3333 4.6667\r\n## 5   8  0 15.050 357.617 194.167 3.1208 4.1041 17.143 0.000 3.0000 3.0833\r\n## 6   8  1 15.400 326.000 299.500 3.8800 3.3700 14.550 0.000 5.0000 6.0000\r\n```\r\n\r\n```{r}\r\n# Muutating operation\r\nmtcars %>%\r\n  set_groups(c(\"cyl\", \"am\")) %>%\r\n  by_group(map, ~ . / sd(.))\r\n```\r\n```{r}\r\n## + + Source: local data frame [32 x 11]\r\n## \r\n##    cyl am     mpg    disp     hp   drat     wt   qsec     vs    gear\r\n## 1    4  0 16.7977 10.5015 3.1544 28.385 7.8278 11.966    Inf  6.9282\r\n## 2    4  0 15.6962 10.0792 4.8333 30.154 7.7296 13.701    Inf  6.9282\r\n## 3    4  0 14.8012  8.5974 4.9350 28.462 6.0487 11.972    Inf  5.1962\r\n## 4    4  1  5.0849  5.2743 4.1050 10.572 5.6675 16.538 2.8284  8.6410\r\n## 5    4  1  7.2259  3.8434 2.9132 11.203 5.3744 17.303 2.8284  8.6410\r\n## 6    4  1  6.7799  3.6969 2.2953 13.537 3.9453 16.458 2.8284  8.6410\r\n## 7    4  1  7.5605  3.4722 2.8691 11.588 4.4827 17.685 2.8284  8.6410\r\n## 8    4  1  6.0885  3.8580 2.9132 11.203 4.7270 16.796 2.8284  8.6410\r\n## 9    4  1  5.7986  5.8749 4.0167 12.164 5.2278 14.841 0.0000 10.8012\r\n## 10   4  1  6.7799  4.6443 4.9878 10.352 3.6961 15.019 2.8284 10.8012\r\n.. ... ..     ...     ...    ...    ...    ...    ...    ...     ...\r\n```"},"comment":{"url":"https://api.github.com/repos/hadley/purrr/issues/comments/75067972","html_url":"https://github.com/hadley/purrr/issues/43#issuecomment-75067972","issue_url":"https://api.github.com/repos/hadley/purrr/issues/43","id":75067972,"user":{"login":"lionel-","id":4465050,"avatar_url":"https://avatars.githubusercontent.com/u/4465050?v=3","gravatar_id":"","url":"https://api.github.com/users/lionel-","html_url":"https://github.com/lionel-","followers_url":"https://api.github.com/users/lionel-/followers","following_url":"https://api.github.com/users/lionel-/following{/other_user}","gists_url":"https://api.github.com/users/lionel-/gists{/gist_id}","starred_url":"https://api.github.com/users/lionel-/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lionel-/subscriptions","organizations_url":"https://api.github.com/users/lionel-/orgs","repos_url":"https://api.github.com/users/lionel-/repos","events_url":"https://api.github.com/users/lionel-/events{/privacy}","received_events_url":"https://api.github.com/users/lionel-/received_events","type":"User","site_admin":false},"created_at":"2015-02-19T15:12:40Z","updated_at":"2015-02-19T15:12:40Z","body":"Traced it further to `match.call()`, which is run by `lm()`. Apparently, evaluating an inlined call running `match.call()` has no effect on performance, *except* when it needs to deal with dots.\r\n\r\n@hadley, on a related matter, did you notice that even in `do.call()`, inlining calls has no performance cost anymore? It seems an R update has fixed this. You may want to update http://rpubs.com/hadley/do-call2. Inlining only has consequences if the function passed to `do.call()` runs `match.call()`, or if an exception is raised.\r\n\r\nI wrote a c++ version of `do.call()` that has none of those two problems. Should I send a PR so that `stitch()` and `match.call()` use it? It works by assigning each argument in an environment (with names `_1`, `_2`, etc) before evaluating, so there is no inlining.\r\n\r\nIt's a few micro-seconds slower than `do.call()`:\r\n```{r}\r\nneval: 100\r\n                                   expr median         unit\r\n1         do.call(nrow, list(diamonds))      7 microseconds\r\n2  do.call(nrow, list(quote(diamonds)))      7 microseconds\r\n3        map_call(list(diamonds), nrow)     10 microseconds\r\n4 map_call(list(quote(diamonds)), nrow)     11 microseconds\r\n```\r\n\r\nBut it has no perf cost when `match.call()` is used or an exception is raised:\r\n```{r}\r\n                                         expr median         unit\r\n1         do.call(match_call, list(diamonds))   1875 microseconds\r\n2  do.call(match_call, list(quote(diamonds)))      8 microseconds\r\n3        map_call(list(diamonds), match_call)      8 microseconds\r\n4 map_call(list(quote(diamonds)), match_call)      9 microseconds\r\n```\r\n\r\n```{r}\r\nsystem.time(do.call(halt, list(diamonds)))\r\n## Error in (function (x)  (from do-call.cpp!3846PsD#2) : !\r\n## Timing stopped at: 0.67 0.004 0.683 \r\n\r\nsystem.time(map_call(list(diamonds), halt))\r\n## Error in (function (x)  (from do-call.cpp!3846PsD#2) : !\r\n## Timing stopped at: 0 0 0 \r\n```"}}