Tpellirn
Tpellirn

Reputation: 796

How to add rows in R already grouped?

I have this dataset

 dat=structure(list(A = c("n", 
                                                                     
  "n", "F", "F"), Par = c(1, 
                                                                                                                
   1, 8, 3), var = c(1, 10, 1,5), dat = c("T", 
                                                                                                                                                       
   "T", "T", "T")), row.names = c(NA, 4L                                                                                                                                                    
    ), class = "data.frame")

I want to add by the groups in A , i tried this did not work:

  dat%>%group_by(A)%>% mutate(ye = c( "40-25", "25-200")) 
    

Error:

 ! Assigned data `value` must be compatible with existing data.
  ✖ Existing data has 4 rows.
  ✖ Assigned data has 2 rows.
  ℹ Only vectors of size 1 are recycled.
  Run `rlang::last_error()` to see where the error occurred.

Desired output:

    A Par var dat     ye
  1 n   1   1   T  "40-25"
  2 n   1  10   T  "25-200"
  3 F   8   1   T  "40-25"
  4 F   3   5   T  "25-200" 

Upvotes: 1

Views: 81

Answers (3)

SamR
SamR

Reputation: 20512

I cannot reproduce your error with the code as it is - it produces the desired output. However, if I add an extra row, I can:

dat[5, ] <- dat[4, ]
dat
#   A Par var dat
# 1 n   1   1   T
# 2 n   1  10   T
# 3 F   8   1   T
# 4 F   3   5   T
# 5 F   3   5   T

dat%>%group_by(A)%>% mutate(ye = c( "40-25", "25-200")) 

Error in `mutate()`:
! Problem while computing `ye = c("40-25", "25-200")`.    
x `ye` must be size 3 or 1, not 2.
i The error occurred in group 1: A = "F".
Run `rlang::last_error()` to see where the error occurred.

This is because you have an odd number of rows now and a vector of length 2 causes an error if you try to recycle it.

One way of resolving this without having separate logic for even/odd number of rows is to create a vector of the required length for every group and the subset it by row number:

dat |>
    group_by(A) |>
    mutate(
        ye= rep(vals_to_replace, ceiling(n()/length(vals_to_replace)))[row_number()]
    )

# # A tibble: 5 x 5
# # Groups:   A [2]
#   A       Par   var dat   ye    
#   <chr> <dbl> <dbl> <chr> <chr>
# 1 n         1     1 T     40-25
# 2 n         1    10 T     25-200
# 3 F         8     1 T     40-25
# 4 F         3     5 T     25-200
# 5 F         3     5 T     40-25

This will ensure that every new group will start at the beginning of the vector, i.e. with "40-25" in this case.

base R solution

You could also do this in base R:

dat  |>
    split(~A)  |>
    lapply(\(df) {
        times_to_repeat = nrow(df)/length(vals_to_replace)
        remainder = nrow(df) %% length(vals_to_replace)
        df$ye  <- c(
            rep(vals_to_replace, times_to_repeat), 
            vals_to_replace[seq_len(remainder)]
        )
        df
    })    |>
    bind_rows()

OK Technically bind_rows() is from dplyr but I prefer it because it works better in a pipe and drops the row names. You could replace it with %>% do.call(rbind, .) if you needed to do it in base R.

Upvotes: 2

ThomasIsCoding
ThomasIsCoding

Reputation: 102855

Probably you can try summarise_all + unnest

library(dplyr)
library(tidyr)

dat %>%
  group_by(A) %>%
  summarise_all(list) %>%
  mutate(ye = list(c("40-25", "25-200"))) %>%
  unnest(cols = c(Par, var, dat, ye))

which gives

  A       Par   var dat   ye    
  <chr> <dbl> <dbl> <chr> <chr>
1 F         8     1 T     40-25
2 F         3     5 T     25-200
3 n         1     1 T     40-25
4 n         1    10 T     25-200

Upvotes: 1

zephryl
zephryl

Reputation: 17309

If I understand your goal, you could split by group and add your ye values sequentially by group:

library(dplyr)
library(purrr)

dat %>%
  group_by(A) %>%
  group_split() %>%
  map2_dfr(
    c("40-25", "25-200"),
    ~ mutate(.x, ye = .y)
  )
# A tibble: 4 × 5
  A       Par   var dat   ye    
  <chr> <dbl> <dbl> <chr> <chr> 
1 F         8     1 T     40-25 
2 F         3     5 T     40-25 
3 n         1     1 T     25-200
4 n         1    10 T     25-200

If instead you want to create ye based on specific values of A, see @SamR’s answer or use dplyr::recode():

dat %>%
  mutate(ye = recode(
    A,
    F = "40-25", 
    n = "25-200"
  ))

Upvotes: 2

Related Questions