преобразовать фрейм данных «пропущенных» чисел в фрейм данных «хит» - PullRequest
0 голосов
/ 03 января 2019

У меня есть довольно конкретные сомнения, но это должно быть легко решить, я просто не могу понять, как ...

У меня есть простой кадр данных, подобный этому:

mydf <- data.frame(Shooter=1:3, Targets.missed=c(paste(sample(1:10,4),collapse=";"), paste(sample(1:10,5),collapse=";"), paste(sample(1:10,8),collapse=";")))
mydf
  Shooter   Targets.missed
1       1          3;8;4;7
2       2       10;1;5;7;4
3       3 5;9;4;10;8;1;6;7

Этот фрейм данных сообщает мне Targets (от 1 до 10), которые пропущены каждым Shooter.

. Я хотел бы получить другой фрейм данных, который сообщает мне, за Target, что Shooter \ s сделал это.

Результат будет:

Target   hit.by.Shooters
1        1
2        1;2;3
3        2;3
4        NA
5        1
6        1;2
7        NA
8        2
9        1;2
10       1

Ответы [ 3 ]

0 голосов
/ 03 января 2019

data.table подход

library( data.table )

#vector with all possible targets
targets.v <- 1:10
#split the missed targets to a list
missed.list <- strsplit( mydf$Targets.missed, ";")
#inverse, to get all hit targets
hit.list <- lapply( missed.list, function(x) as.data.table( targets.v[!targets.v %in% x] )  )
#bind hit targets to data.table
dt <- rbindlist( hit.list, idcol = "shooter" )
#summarise (paste with collapse), and join on all possible targets
dt[, .(hit.by.shooters = paste(shooter, collapse = ";")), by = .(target = V1)][data.table(target = targets.v), on = c("target")]
#     target hit.by.shooters
#  1:      1               1
#  2:      2           1;2;3
#  3:      3             2;3
#  4:      4            <NA>
#  5:      5               1
#  6:      6             1;2
#  7:      7            <NA>
#  8:      8               2
#  9:      9             1;2
# 10:     10               1
0 голосов
/ 03 января 2019

Другая tidyverse возможность.Сначала мы создаем фрейм данных со всеми возможными комбинациями Shooter и Targets, а затем удаляем строки, присутствующие в mydf, используя anti_join, заполняем недостающие Targets, добавляя их как NA и, наконец, суммируемTargets чтобы получить Shooters, кто действительно поразил цель.

library(tidyverse)

crossing(Shooter = unique(mydf$Shooter), Targets.missed = 1:10) %>%
anti_join(mydf %>% separate_rows(Targets.missed) %>% mutate_all(as.numeric)) %>%
        complete(Targets.missed = 1:10) %>%
        group_by(Targets.missed) %>%
        summarise(hit.by.Shooters = paste0(Shooter, collapse = ";"))


# Targets.missed hit.by.Shooters
#            <int> <chr>          
# 1              1 1;2            
# 2              2 1;2            
# 3              3 1              
# 4              4 1              
# 5              5 2              
# 6              6 1;3            
# 7              7 1;2            
# 8              8 2              
# 9              9 NA             
#10             10 3           

data

set.seed(987)
mydf <- data.frame(Shooter=1:3, 
        Targets.missed=c(paste(sample(1:10,4),collapse=";"), 
        paste(sample(1:10,5),collapse=";"), paste(sample(1:10,8),collapse=";")))
0 голосов
/ 03 января 2019

Мы расширяем данные, разделяя в ; Targets.missed в «длинный» формат, затем группируем по «Shooter», summarise с list числами, которых нет в ' Targets.missed 'из 1:10, unnest столбец list, сгруппированный по' Target ', summarise по paste, объединяющий элементы unique' Shooter 'в одну строку и заполняющий недостающие элементы от 1:10 с NA с помощью complete

library(tidyverse)
mydf %>% 
   separate_rows(Targets.missed) %>% 
   group_by(Shooter) %>%
   summarise(Target = list(setdiff(1:10, Targets.missed))) %>% 
   unnest %>% 
   group_by(Target) %>%
   summarise(hit.by.Shooters = paste(unique(Shooter), collapse=";")) %>% 
   complete(Target = 1:10) 
# A tibble: 10 x 2
#   Target hit.by.Shooters
#    <int> <chr>          
# 1      1 1              
# 2      2 1;2;3          
# 3      3 2;3            
# 4      4 <NA>           
# 5      5 1              
# 6      6 1;2            
# 7      7 <NA>           
# 8      8 2              
# 9      9 1;2            
#10     10 1        

Или другой вариант - base R путем разделения 'Targets.missed' (при условии character class) на list из vector s, цикл по list, получение значений, которые не находятся в 1:10 (с setdiff), задайте имена list в столбце Shooter, stack пары ключ / val list в два столбца data.frame, получите строки unique , aggregate с помощью paste в столбце 'ind', сгруппированном по 'values', merge с полным набором данных 'values' от 1:10

out <-  aggregate(ind ~ values, 
  unique(stack(setNames(lapply(strsplit(mydf$Targets.missed, ';'), 
    setdiff, x= 1:10), mydf$Shooter))), FUN = paste, collapse=";")
out1 <- merge(data.frame(values = 1:10), out, all.x = TRUE)

и при необходимости измените имена столбцов

names(out1) <- c('Target', 'hit.by.Shooters')

данные

mydf <- structure(list(Shooter = 1:3, Targets.missed = c("3;8;4;7", "10;1;5;7;4", 
 "5;9;4;10;8;1;6;7")), class = "data.frame", row.names = c("1", 
 "2", "3"))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...