Определение, встречается ли одно значение один раз в ряду столбцов, но второе значение не появляется вообще - PullRequest
0 голосов
/ 19 декабря 2018

Вероятно, ужасный заголовок, но у меня есть таблица квалификаторов, хранящаяся как "1", "2" и "3".То, что я пытаюсь сделать, это посмотреть в каждой строке (примерно 300 000 строк, но переменной.) И определить, где встречается одна «3» (если она встречается более одного раза, мне это не интересно) и остальныестолбцы в этой строке имеют «1» и возвращают это в список.(Количество столбцов и имен столбцов изменяются в зависимости от входных файлов.)

Инстинктивно я хочу попытаться сделать это, выполнив вложенные циклы for, которые индексируют количество строк, а затем число столбцов, а затем некоторую функцию, которая выглядитза один "3" и не "2".- которое, вероятно, означает, что предпочтительным способом было бы использование некоторой правильной прикладной функции?

Еще один способ - суммировать количество столбцов, добавить 2, а затем суммировать строку, имея квалификатор, который не может быть в двоичных числах.строка.Но это казалось довольно сложным.

df1
  seq                        loc   Ball   Cat   Square   Water
1 AAAAAACCAGTCCCAGTTCGGATTG  t       3     1      1       1  
2 AAAAAACCAGTCTCAGTTCGGATTG  b       1     1      3       3
3 AAAAAACCAGTCTCAGTTCGGATTG  t       1     3      2       1
4 AAAAAACCGGTCACAGTTCAGATTG  b       1     1      1       2
5 AAAAAACCGGTCACAGTTCAGATTG  t       1     1      3       1


Expected Ouput:
  seq                        loc     Group   
1 AAAAAACCAGTCCCAGTTCGGATTG  t       Ball    
2 AAAAAACCGGTCACAGTTCAGATTG  t       Square 


dput of df1:
structure(list(seq = structure(c(1L, 2L, 2L, 3L, 3L), .Label = 
c("AAAAAACCAGTCCCAGTTCGGATTG", 
"AAAAAACCAGTCTCAGTTCGGATTG", "AAAAAACCGGTCACAGTTCAGATTG"), class = 
"factor"), 
loc = structure(c(2L, 1L, 2L, 1L, 2L), .Label = c("b", 
"t"), class = "factor"), Ball = c("3", "1", "1", "1", "1"
), Cat = c("1", "1", "3", "1", "1"), Square = c("1", "3", 
"2", "1", "3"), Water = c("1", "3", "1", "2", "1")), row.names = c(NA, 
-5L), class = c("tbl_df", "tbl", "data.frame"))

Ответы [ 5 ]

0 голосов
/ 21 декабря 2018

Мое решение было взлетом @Julius Vainora .. Мой более запутанный, но я использовал match() и добавил столбец индекса.

DF$index <- seq.int(nrow(DF))
col_names <- names(DF)[3:ncol(DF)]

DF$Group <- col_names[which(DF[cols] == 3, arr.ind = TRUE)[,2][
  DF$index[match(
    DF$index, which(
       DF[cols] == 3, arr.ind = TRUE[,1])]]]
0 голосов
/ 19 декабря 2018

Просто чтобы показать альтернативу, где мы работаем с данными в длинном формате, а не по строкам.Здесь, используя data.table функции:

library(data.table)
d <- melt(setDT(df1), id.vars = c("seq", "loc"))
d[d[ , .I[sum(value == 3) == 1 & !any(value == 2)], by = .(seq, loc)]$V1][value == 3]
#                          seq loc variable value
# 1: AAAAAACCAGTCCCAGTTCGGATTG   t     Ball     3
# 2: AAAAAACCGGTCACAGTTCAGATTG   t   Square     3

melt данных для длинного формата, используя «sec» и «loc» в качестве переменных id.Если комбинация «sec» и «loc» не является уникальными идентификаторами строк, создайте уникальный индекс строки (например, ri := 1:.N).

Для каждого «sec» и «loc» (by = .(seq, loc);т. е. для каждой строки в исходных данных), создайте логический вектор для требуемого условия: один 3, а не 2 на строку (sum(value == 3) == 1 & !any(value == 2)).Возьмите соответствующие индексы строк (.I).Индексы с автоматическим названием «V1» затем используются для подмножества «d».

Наконец, выберите строки, в которых «значение» равно 3 ([value == 3]).

0 голосов
/ 19 декабря 2018

Я часто использую базовый apply при выполнении вычислений по строкам.Вы могли бы что-то сделать с фактическим dplyr::rowwise, если бы вы хотели найти решение в обратном направлении.Вот только использование базы R:

filter_on = apply(X = df1[3:6], 
                  MARGIN = 1, 
                  FUN = function(x){sum(x == 3) == 1 & sum(x == 1) == 3})
df1 = df1[filter_on,]

columns = colnames(df1)[3:6]

df1$Group = unlist(apply(X = df1[3:6], 
                         MARGIN = 1,
                         FUN = function(x){columns[x == 3]}))
0 голосов
/ 19 декабря 2018

Ввод дополнительной версии.Это относится только к выбору строки.

#create vector of wanted column names
cols <- c("Ball", "Cat", "Square", "Water")
#make values numeric
df1[, cols] <- df1[, cols] %>% mutate_if(is.character, as.numeric)

#filter rows
df1[which((rowSums(df1[, cols]) == (length(cols)+2) ) & (rowSums(df1[, cols] == 2) == 0)),]

                        seq loc Ball Cat Square Water
1 AAAAAACCAGTCCCAGTTCGGATTG   t    3   1      1     1
5 AAAAAACCGGTCACAGTTCAGATTG   t    1   1      3     1

Похоже, что версия apply самая быстрая из первых трех сообщений, но ненамного.

microbenchmark::microbenchmark(
which = df1[which((rowSums(df1[, cols]) == (length(cols)+2) ) & (rowSums(df1[, cols] == 2) == 0)),],
filter = df1[rowSums(df1[cols]) == (3 + length(cols) - 1) & rowSums(df1[cols] == 3) == 1, ],
apply = df1[apply(X = df1[3:6], 
          MARGIN = 1, 
          FUN = function(x){sum(x == 3) == 1 & sum(x == 1) == 3}),]
)

Unit: microseconds
   expr     min       lq     mean  median       uq      max neval cld
  which 429.043 436.4665 446.2817 445.811 451.3140  493.553   100   a
 filter 429.555 435.5715 447.8151 440.307 449.2670  724.202   100   a
  apply 339.958 346.9975 435.0437 351.222 362.2295 8141.819   100   a
0 голосов
/ 19 декабря 2018

Вот решение без tidyverse и даже * применения функций.Сначала давайте преобразуем эти четыре столбца в целые числа:

cols <- 3:6
df1[cols] <- lapply(df1[cols], as.integer)

Затем

df <- df1[rowSums(df1[cols]) == (3 + length(cols) - 1) & rowSums(df1[cols] == 3) == 1, ]
df$Group <- names(df)[cols][which(t(df[cols]) == 3, arr.ind = TRUE)[, 1]]
df
# A tibble: 2 x 7
#   seq                       loc    Ball   Cat Square Water Group 
#   <fct>                     <fct> <int> <int>  <int> <int> <chr> 
# 1 AAAAAACCAGTCCCAGTTCGGATTG t         3     1      1     1 Ball  
# 2 AAAAAACCGGTCACAGTTCAGATTG t         1     1      3     1 Square

В первой строке я выбираю правильные строки с двумя условиями: должен быть только один элемент, равныйдо 3 в этих cols столбцах (rowSums(df1[cols] == 3) == 1), а общая сумма строки должна составлять 3 + length(cols) - 1.Затем во второй строке я проверяю, какие столбцы имеют 3, и выбираю соответствующие имена df в качестве значений для Group.

...