Замените определенные значения в фрейме данных значениями случайной строки

#r #dplyr

Вопрос:

Допустим, у меня есть следующий df

 df <- read.table(text = "team id result result_number
A 1 999 999
A 2 cat 45
A 3 dog 50
B 4 999 999
B 5 three 60
B 6 four 30
C 7 999 999
C 8 rabbit 45
C 9 monkey 11
D 10 dog 12
D 11 999 999
D 12 basket 10", header = T)
 

Для каждой строки, имеющей 999 в result категории, я хотел бы заменить это значение (и соответствующее result_number значение) значениями из случайной строки в той же команде (например, случайным образом в пределах A, B, C или D), которое не является 999.

Итак, вот пример вывода:

 df <- read.table(text = "team id result result_number
A 1 cat 45
A 2 cat 45
A 3 dog 50
B 4 four 30
B 5 three 60
B 6 four 30
C 7 rabbit 45
C 8 rabbit 45
C 9 monkey 11
D 10 dog 12
D 11 basket 10
D 12 basket 10", header = T)
 

Я не могу придумать элегантный способ сделать это в dplyr без действительно неуклюжей внешней функции

Ответ №1:

Сгруппированные по «команде», зациклите across столбцы «результат», «номер результата», replace , где значение равно 999 с sample числом элементов, которые не равны ( != ) 999, и верните одно значение

 library(dplyr)
df %>% 
   group_by(team) %>% 
   mutate(across(result:result_number,
      ~ replace(., as.character(.)==  "999", 
          sample(.[as.character(.) != "999"], 1)))) %>%
   ungroup
 

-выход

 # A tibble: 12 × 4
   team     id result result_number
   <chr> <int> <chr>          <int>
 1 A         1 dog               45
 2 A         2 cat               45
 3 A         3 dog               50
 4 B         4 four              30
 5 B         5 three             60
 6 B         6 four              30
 7 C         7 monkey            45
 8 C         8 rabbit            45
 9 C         9 monkey            11
10 D        10 dog               12
11 D        11 dog               10
12 D        12 basket            10
 

Комментарии:

1. Просто краткое примечание для OP о сравнении столбца символов result с числовым значением 999 (например . == 999 , и . != 999 ), что значения приводятся к одному и тому же типу в следующем порядке приоритета: символьный, комплексный, числовой, целочисленный, логический и необработанный перед сравнением.

2. @LMc да, это правда. Я использовал принуждение в replace отличие от того, в case_when котором строго для типа

3. Спасибо за комментарии. На всякий случай я обновил as.character

4. @TarJae, потому что вы sample используете весь столбец, который также включает 999. Вы можете изменить значение sample на sample(.[. != 999], 1, replace = TRUE) . также имеет значение преобразование типов в комментариях LMc

5. Большое спасибо, мастер акрун!!!

Ответ №2:

Этот результат был создан с помощью akrun! Большое спасибо!

 library(dplyr)
df %>% 
  group_by(team) %>% 
  mutate(across(contains("result"), ~ifelse(.==999, sample(.[. != 999], 1, replace = TRUE),.)))
 
 team     id result result_number
   <chr> <int> <chr>          <int>
 1 A         1 cat               50
 2 A         2 cat               45
 3 A         3 dog               50
 4 B         4 four              60
 5 B         5 three             60
 6 B         6 four              30
 7 C         7 monkey            45
 8 C         8 rabbit            45
 9 C         9 monkey            11
10 D        10 dog               12
11 D        11 dog               12
12 D        12 basket            10