Reputation: 796
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
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.
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
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
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