group_by() all columns except two, summarise() those two into one

Here are a couple of options. Let me know if these are on the right track.

# Set up fake data
library(tidyverse)

d = tribble(
  ~id, ~q1, ~q2, ~q3, ~v3, ~q4,
  0, "a", "b", "x", 1, "c",
  0, "a", "b", "y", 2, "c",
  0, "a", "b", "z", 3, "c"
)

Option 1. In summarise, return a list within a named list. The output is as requested in your example:

d2 = d %>% 
  group_by(across(-ends_with("3"))) %>% 
  summarise(qv3 = list(as.list(set_names(v3, q3))))

d2
# A tibble: 1 x 5
# Groups:   id, q1, q2 [1]
     id q1    q2    q4    qv3             
  <dbl> <chr> <chr> <chr> <list>          
1     0 a     b     c     <named list [3]>
d2$qv3
[[1]]
[[1]]$x
[1] 1

[[1]]$y
[1] 2

[[1]]$z
[1] 3
# Recover the original data frame
d2 %>% unnest(qv3) %>% unnest(qv3)

Option 2. In this case, the nested objects are data frames with only one level of nesting:

d3 = d %>% 
  group_nest(across(-ends_with("3")), .key="qv3")

# group_nest() is experimental, but this is equivalent
d3 = d %>% 
  group_by(across(-ends_with("3"))) %>% 
  nest(qv3=c(q3, v3))

d3
     id q1    q2    q4                   qv3
  <dbl> <chr> <chr> <chr> <list<tbl_df[,2]>>
1     0 a     b     c                [3 × 2]
d3$qv3
<list_of<
  tbl_df<
    q3: character
    v3: double
  >
>[1]>
[[1]]
# A tibble: 3 x 2
  q3       v3
  <chr> <dbl>
1 x         1
2 y         2
3 z         3
# Recover the original data frame
d3 %>% unnest(qv3)
2 Likes