R: сравнение столбца со значениями в другом столбце, представленном как объект списка - PullRequest
0 голосов
/ 05 июля 2019

Я создал DF1, используя цикл for, или получил его от кого-то.Я хочу проверить DF2, если значения nos (в виде списка или объединены в строку) для соответствующих дат.Я сталкивался с такой ситуацией много раз.Вот код

library(dplyr)
library(magrittr)
DF1 <- data.frame(det = as.Date(c('2013-02-02','2018-01-11','2011-07-10')),nos = c("1|3","4|2|1","3|4")) %>% print
DF2 <- data.frame(det1 = as.Date(c('2013-02-02','2018-01-11')),no = c(1,3)) %>% print
DF1 = left_join(DF2,DF1,by=c('det1'='det')) %>%
  mutate(list_column = strsplit(nos,split = "\\|")) %>% 
  print
# DF1
# det1 no   nos
# 1 2013-02-02  1   1|3
# 2 2018-01-11  2 4|2|1
for(i_ in 1:nrow(DF1)){
  # i_ = 1
  temp = DF1[i_,]
  list_vals = temp$list_column %>% as.vector() %>% unlist() %>% as.numeric() %>% print
  DF1$present[i_] = temp$no %in% list_vals
    }
#R>DF1
#        det1 no   nos list_column present
#1 2013-02-02  1   1|3        1, 3    TRUE
#2 2018-01-11  3 4|2|1     4, 2, 1   FALSE

Как лучше всего создать еще один логический столбец, в котором будет указано, является ли no одним из nos.Как добиться того, что я пытаюсь сделать, или лучше того, что я пытаюсь, наконец, получить?Я приветствую любое решение base, tidyverse или data.table.

EDIT-1

Я ищу устранение петли for.

Ответы [ 5 ]

2 голосов
/ 05 июля 2019

Мы можем использовать Map для циклического перебора 'list_column', проверьте наличие length из intersecting элементов

library(tidyverse)
DF1 %>%
   mutate(present = map2(list_column, DF2$no, ~ length(intersect(.x, .y))) > 0)
#        det1 no   nos list_column present
#1 2013-02-02  1   1|3        1, 3    TRUE
#2 2018-01-11  3 4|2|1     4, 2, 1   FALSE

или чуть более компактно без вызова анонимной функции

DF1 %>%
   mutate(present = lengths(map2(list_column, DF2$no, intersect)) > 0)
2 голосов
/ 05 июля 2019

** первая часть вашего кода **

library(dplyr)
DF1 <- data.frame(det = as.Date(c('2013-02-02','2018-01-11','2011-07-10')),nos = c("1|3","4|2|1","3|4")) %>% print
DF2 <- data.frame(det1 = as.Date(c('2013-02-02','2018-01-11')),no = c(1,3)) %>% print
DF1 = left_join(DF2,DF1,by=c('det1'='det')) # no need to strsplit()

Используйте grepl с apply, только одну строку:

DF1$present <- apply(DF1, 1, function(x){
  ifelse(grepl(x=x[3], pattern = paste0("(?<!\\d)", x[2], "(?!\\d)"), perl = TRUE), T, F)
})

Результат:

         det1 no   nos  present
1: 2013-02-02  1   1|3  TRUE
2: 2018-01-11  3 4|2|1 FALSE

Это решение "переносимо" на data.table, например:

library(data.table)

data.table::setDT(DF1) # into data.table

DF1[, present := apply(DF1, 1, function(x){ 
  ifelse(grepl(x=x[3], pattern = paste0("(?<!\\d)", x[2], "(?!\\d)"), perl = TRUE), T, F)
})] # the := is a symbol for assignment
2 голосов
/ 05 июля 2019
library(data.table)
setDT(DF1)
DF1[, present := as.character(no) %in% list_column[[1]], by = seq_len(nrow(DF1))][]

         det1 no   nos list_column present
1: 2013-02-02  1   1|3         1,3    TRUE
2: 2018-01-11  3 4|2|1       4,2,1   FALSE

Данные (просто добавив as.character () в одном месте)

DF1 <- data.frame(det = as.Date(c('2013-02-02','2018-01-11','2011-07-10')),nos = c("1|3","4|2|1","3|4")) %>% print
DF2 <- data.frame(det1 = as.Date(c('2013-02-02','2018-01-11')),no = c(1,3)) %>% print
DF1 = left_join(DF2,DF1,by=c('det1'='det')) %>%
  mutate(list_column = strsplit(as.character(nos),split = "\\|"))
0 голосов
/ 08 июля 2019

Другая опция, использующая data.table::tstrsplit:

library(data.table)
df1 <- setDT(DF1)[, .(no=as.integer(unlist(tstrsplit(nos, "\\|")))), by=.(det)]
setDT(DF2)[, present := FALSE][
    df1, on=c("det1"="det", "no"), present := !is.na(i.no)]

Выход:

         det1 no present
1: 2013-02-02  1    TRUE
2: 2018-01-11  3   FALSE

данные:

DF1 <- data.frame(det = as.Date(c('2013-02-02','2018-01-11','2011-07-10')),
    nos = c("1|3","4|2|1","3|4"))
DF2 <- data.frame(det1 = as.Date(c('2013-02-02','2018-01-11')), 
    no = as.integer(c(1,3)))
0 голосов
/ 05 июля 2019

Я считаю grepl полезным в этой ситуации.

DF3 = left_join(DF2, DF1, by=c('det1'='det'))
for(i in 1:nrow(DF3)){
  DF3[i, 'present'] = grepl(DF3[i,'no'], DF3[i, 'nos'])
}

> DF3
        det1 no   nos present
1 2013-02-02  1   1|3    TRUE
2 2018-01-11  3 4|2|1   FALSE

Данные (добавление в stringsAsFactors = F):

DF1 <- data.frame(det = as.Date(c('2013-02-02','2018-01-11','2011-07-10')),nos = c("1|3","4|2|1","3|4"), stringsAsFactors = F)
DF2 <- data.frame(det1 = as.Date(c('2013-02-02','2018-01-11')), no = c(1,3), stringsAsFactors = F)
...