Here's a tidyverse solution:
df %>%
#cast all columns except `id` longer:
pivot_longer(-id) %>%
# for each combination of ...
group_by(id, value) %>%
# ... count the frequencies of distinct values:
summarise(N = ifelse(is.na(value), NA, n())) %>%
# omit rows with `NA`:
na.omit() %>%
# remove duplicated rows:
slice_head() %>%
# for each `id`...
group_by(id) %>%
# ... cast back wider:
pivot_wider(names_from = value, values_from = N,
names_prefix = "N_") %>%
# replace `NA` with 0:
mutate(across(starts_with("N"), ~replace_na(., 0))) %>%
# bind result back to original `df`:
bind_cols(df%>% select(-id), .) %>%
# reorder columns:
select(id, everything())
id w1 w2 w3 w4 N_light N_medium N_heavy
1 1 light light light light 4 0 0
2 2 light light light light 4 0 0
3 3 light light medium medium 2 2 0
4 4 light light <NA> medium 2 1 0
5 5 light light medium medium 2 2 0
6 6 medium medium <NA> heavy 0 2 1
EDIT:
If the ultimate goal is to compute the mode for the three new columns, then this may be a way to go:
# First define a function for the mode:
getmode <- function(v) {
uniqv <- unique(v[!is.na(v)])
uniqv[which.max(table(match(v, uniqv)))]
}
# Second, do as before:
df %>%
#cast all columns except `id` longer:
pivot_longer(-id) %>%
# for each combination of ...
group_by(id, value) %>%
# ... count the frequencies of distinct values:
summarise(N = ifelse(is.na(value), NA, n())) %>%
# omit rows with `NA`:
na.omit() %>%
# remove duplicated rows:
slice_head() %>%
# for each `id`...
group_by(id) %>%
# ... cast back wider:
pivot_wider(names_from = value, values_from = N,
names_prefix = "N_") %>%
# replace `NA`with 0:
mutate(across(starts_with("N"), ~replace_na(., 0))) %>%
# bind result back to original `df`:
bind_cols(df%>% select(-id), .) %>%
select(id, everything()) %>%
# Third, add to this the computation of the mode:
# compute mode:
summarise(across(starts_with("N"), ~getmode(.)))
N_light N_medium N_heavy
1 2 2 0
Data:
df <- structure(list(id = 1:6, w1 = c("light", "light", "light", "light",
"light", "medium"), w2 = c("light", "light", "light", "light",
"light", "medium"), w3 = c("light", "light", "medium", NA, "medium",
NA), w4 = c("light", "light", "medium", "medium", "medium", "heavy"
)), class = "data.frame", row.names = c(NA, -6L))