Подтвердить что ты не робот

Поиск строк, содержащих значение (или значения) в любом столбце

Скажем, у нас есть данные таблицы, содержащие строки в нескольких столбцах. Мы хотим найти индексы всех строк, которые содержат определенное значение или, еще лучше, одно из нескольких значений. Однако столбец неизвестен.

В настоящий момент я делаю следующее:

apply(df, 2, function(x) which(x == "M017"))

где df =

1 04.10.2009 01:24:51   M017  <NA>  <NA>    NA
2 04.10.2009 01:24:53   M018  <NA>  <NA>    NA
3 04.10.2009 01:24:54   M051  <NA>  <NA>    NA
4 04.10.2009 01:25:06   <NA>  M016  <NA>    NA
5 04.10.2009 01:25:07   <NA>  M015  <NA>    NA
6 04.10.2009 01:26:07   <NA>  M017  <NA>    NA
7 04.10.2009 01:26:27   <NA>  M017  <NA>    NA
8 04.10.2009 01:27:23   <NA>  M017  <NA>    NA
9 04.10.2009 01:27:30   <NA>  M017  <NA>    NA
10 04.10.2009 01:27:32   M017  <NA>  <NA>    NA
11 04.10.2009 01:27:34   M051  <NA>  <NA>    NA

Это также работает, если мы попытаемся найти более одного значения:

apply(df, 2, function(x) which(x %in% c("M017", "M018")))

Результат:

$`1`
integer(0)

$`2`
[1]  1  2 20

$`3`
[1] 16 17 18 19

$`4`
integer(0)

$`5`
integer(0)

Однако обработка полученного списка списков довольно утомительна.

Существует ли более эффективный способ поиска строк, содержащих значение (или больше) в любом столбце?

4b9b3361

Ответ 1

Как насчет

apply(df, 1, function(r) any(r %in% c("M017", "M018")))

i-й элемент будет TRUE, если i-я строка содержит одно из значений, а FALSE в противном случае. Или, если вы хотите просто номера строк, заключите вышеприведенный оператор в which(...).

Ответ 2

Если вы хотите найти rows, у которого есть какие-либо значения в векторе, один из них - это цикл вектора (lapply(v1,..)), создайте логический индекс (TRUE/FALSE) с помощью (==). Используйте Reduce и OR (|), чтобы уменьшить список до одной логической матрицы, проверив соответствующие элементы. Суммируйте строки (rowSums), double negate (!!), чтобы получить строки с любыми совпадениями.

indx1 <- !!rowSums(Reduce(`|`, lapply(v1, `==`, df)), na.rm=TRUE)

Или векторизовать и получить индексы строк с помощью which с помощью arr.ind=TRUE

indx2 <- unique(which(Vectorize(function(x) x %in% v1)(df),
                                     arr.ind=TRUE)[,1])

Бенчмарки

Я не использовал решение @kristang, поскольку он дает мне ошибки. На основе матрицы 1000x500 решение @konvas является наиболее эффективным (до сих пор). Но это может измениться, если число строк увеличивается

val <- paste0('M0', 1:1000)
set.seed(24)
df1 <- as.data.frame(matrix(sample(c(val, NA), 1000*500, 
  replace=TRUE), ncol=500), stringsAsFactors=FALSE) 
set.seed(356)
v1 <- sample(val, 200, replace=FALSE)

 konvas <- function() {apply(df1, 1, function(r) any(r %in% v1))}
 akrun1 <- function() {!!rowSums(Reduce(`|`, lapply(v1, `==`, df1)),
               na.rm=TRUE)}
 akrun2 <- function() {unique(which(Vectorize(function(x) x %in% 
              v1)(df1),arr.ind=TRUE)[,1])}


 library(microbenchmark)
 microbenchmark(konvas(), akrun1(), akrun2(), unit='relative', times=20L)
 #Unit: relative
 #   expr       min         lq       mean     median         uq      max   neval
 # konvas()   1.00000   1.000000   1.000000   1.000000   1.000000  1.00000    20
 # akrun1() 160.08749 147.642721 125.085200 134.491722 151.454441 52.22737    20
 # akrun2()   5.85611   5.641451   4.676836   5.330067   5.269937  2.22255    20
 # cld
 #  a 
 #  b
 #  a 

В случае ncol = 10 результаты неравнозначны:

expr       min        lq     mean    median        uq       max    neval
 konvas()  3.116722  3.081584  2.90660  2.983618  2.998343  2.394908    20
 akrun1() 27.587827 26.554422 22.91664 23.628950 21.892466 18.305376    20
 akrun2()  1.000000  1.000000  1.00000  1.000000  1.000000  1.000000    20

данные

 v1 <- c('M017', 'M018')
 df <- structure(list(datetime = c("04.10.2009 01:24:51",
"04.10.2009 01:24:53", 
"04.10.2009 01:24:54", "04.10.2009 01:25:06", "04.10.2009 01:25:07", 
"04.10.2009 01:26:07", "04.10.2009 01:26:27", "04.10.2009 01:27:23", 
"04.10.2009 01:27:30", "04.10.2009 01:27:32", "04.10.2009 01:27:34"
), col1 = c("M017", "M018", "M051", "<NA>", "<NA>", "<NA>", "<NA>", 
"<NA>", "<NA>", "M017", "M051"), col2 = c("<NA>", "<NA>", "<NA>", 
"M016", "M015", "M017", "M017", "M017", "M017", "<NA>", "<NA>"
), col3 = c("<NA>", "<NA>", "<NA>", "<NA>", "<NA>", "<NA>", "<NA>", 
"<NA>", "<NA>", "<NA>", "<NA>"), col4 = c(NA, NA, NA, NA, NA, 
NA, NA, NA, NA, NA, NA)), .Names = c("datetime", "col1", "col2", 
"col3", "col4"), class = "data.frame", row.names = c("1", "2", 
"3", "4", "5", "6", "7", "8", "9", "10", "11"))