5

I have this tibble:

library(tibble)
library(dplyr)

df <- tibble(id = c("one", "two", "three"),
             A = c(1,2,3), 
             B = c(4,5,6))

  id        A     B
  <chr> <dbl> <dbl>
1 one       1     4
2 two       2     5
3 three     3     6

I want to add a row to each group AND assign values to the new column BUT with a function (here the new row in each group should get A=4 B = the first group value of column B USING first(B)-> desired output:

id        A     B
<chr> <dbl> <dbl>
1 one       1     4
2 one       4     4
3 three     3     6
4 three     4     6
5 two       2     5
6 two       4     5

I have tried so far:

If I add a row in a ungrouped tibble with add_row -> this works perfect!

df %>% 
  add_row(A=4, B=4)

  id        A     B
  <chr> <dbl> <dbl>
1 one       1     4
2 two       2     5
3 three     3     6
4 NA        4     4

If I try to use add_row in a grouped tibble -> this works not:

df %>% 
  group_by(id) %>%
  add_row(A=4, B=4)

Error: Can't add rows to grouped data frames.
Run `rlang::last_error()` to see where the error occurred.

According to this post Add row in each group using dplyr and add_row() we could use group_modify -> this works great:

df %>% 
  group_by(id) %>% 
  group_modify(~ add_row(A=4, B=4, .x))

  id        A     B
  <chr> <dbl> <dbl>
1 one       1     4
2 one       4     4
3 three     3     6
4 three     4     4
5 two       2     5
6 two       4     4

I want to assign to column B the first value of column B (or it can be any function min(B), max(B) etccc.) -> this does not work:

df %>% 
  group_by(id) %>% 
  group_modify(~ add_row(A=4, B=first(B), .x))

Error in h(simpleError(msg, call)) : 
  Fehler bei der Auswertung des Argumentes 'x' bei der Methodenauswahl für Funktion 'first': object 'B' not found
TarJae
  • 72,363
  • 6
  • 19
  • 66
  • Can you translate "Fehler bei der Auswertung des Argumentes 'x' bei der Methodenauswahl für Funktion 'first': object 'B' not found"? – jpdugo17 Jan 02 '22 at 21:32
  • 1
    Something like: Error in evaluation of the argument 'x' within the method choice for function `first`; object `B` not found. – TarJae Jan 02 '22 at 21:34
  • `first(.$B)` works for me. – Maël Jan 02 '22 at 21:35
  • If I add `first(.$B)` I get `Error in (function (classes, fdef, mtable) : unable to find an inherited method for function ‘first’ for signature ‘"numeric"’` – TarJae Jan 02 '22 at 21:38
  • What about `group_modify(~ add_row(A=4, B=first(.x$B), .x))` or `group_modify(~ add_row(A=4, B=first(df$B), .x))`? – Maël Jan 02 '22 at 21:39
  • In both cases the same Error as described above with `first(.$B)` – TarJae Jan 02 '22 at 21:41
  • I'm surprised that it works well for me but not for you, have you tried with another function, e.g. `head(.x$B, 1)`? – Maël Jan 02 '22 at 21:48
  • I will try this out. Thank you for your replies. I am wondering also why it does not work for me. Maybe you could post your code and output as answer. – TarJae Jan 02 '22 at 22:00

4 Answers4

5
library(tidyverse)

df <- tibble(id = c("one", "two", "three"),
             A = c(1,2,3), 
             B = c(4,5,6))

df %>% 
    group_by(id) %>% 
    summarise(add_row(cur_data(), A = 4, B = first(cur_data()$B)))
#> `summarise()` has grouped output by 'id'. You can override using the `.groups` argument.
#> # A tibble: 6 × 3
#> # Groups:   id [3]
#>   id        A     B
#>   <chr> <dbl> <dbl>
#> 1 one       1     4
#> 2 one       4     4
#> 3 three     3     6
#> 4 three     4     6
#> 5 two       2     5
#> 6 two       4     5

Or

df %>% 
    group_by(id) %>% 
    group_split() %>% 
    map_dfr(~ add_row(.,id = first(.$id),  A = 4, B = first(.$B)))
#> # A tibble: 6 × 3
#>   id        A     B
#>   <chr> <dbl> <dbl>
#> 1 one       1     4
#> 2 one       4     4
#> 3 three     3     6
#> 4 three     4     6
#> 5 two       2     5
#> 6 two       4     5

Created on 2022-01-02 by the reprex package (v2.0.1)

jpdugo17
  • 6,816
  • 2
  • 11
  • 23
3

Maybe this is an option

library(dplyr)

df %>% 
  group_by(id) %>%
  summarise( A=c(A,4), B=c(B,first(B)) ) %>% 
  ungroup
`summarise()` has grouped output by 'id'. You can override using the `.groups` argument.
# A tibble: 6 x 3
  id        A     B
  <chr> <dbl> <dbl>
1 one       1     4
2 one       4     4
3 three     3     6
4 three     4     6
5 two       2     5
6 two       4     5
Andre Wildberg
  • 12,344
  • 3
  • 12
  • 29
3

According to the documentation of the function group_modify, if you use a formula, you must use ". or .x to refer to the subset of rows of .tbl for the given group;" that's why you used .x inside the add_row function. To be entirely consistent, you have to do it also within the first function.

df %>% 
  group_by(id) %>% 
  group_modify(~ add_row(A=4, B=first(.x$B), .x))

# A tibble: 6 x 3
# Groups:   id [3]
  id        A     B
  <chr> <dbl> <dbl>
1 one       1     4
2 one       4     4
3 three     3     6
4 three     4     6
5 two       2     5
6 two       4     5

Using first(.$B) or first(df$B) will provide the same results.

Maël
  • 45,206
  • 3
  • 29
  • 67
  • Bravo. That works. Sorry yesterday I thought I did the same .. and it did not work with your reply in the comments (today I am on a different setting...). Now it is clear and works perfect. – TarJae Jan 03 '22 at 09:16
  • 1
    No problem, I'm glad it works fine :) – Maël Jan 03 '22 at 09:17
1

A possible solution:

library(tidyverse)

df <- tibble(id = c("one", "two", "three"),
             A = c(1,2,3), 
             B = c(4,5,6))

df %>% 
  group_by(id) %>%
  slice(rep(1,2)) %>% mutate(A = if_else(row_number() > 1, first(df$B), A)) %>% 
  ungroup

#> # A tibble: 6 × 3
#>   id        A     B
#>   <chr> <dbl> <dbl>
#> 1 one       1     4
#> 2 one       4     4
#> 3 three     3     6
#> 4 three     4     6
#> 5 two       2     5
#> 6 two       4     5
PaulS
  • 21,159
  • 2
  • 9
  • 26