Поиск строк, содержащих значение (или значения) в любом столбце
Скажем, у нас есть данные таблицы, содержащие строки в нескольких столбцах. Мы хотим найти индексы всех строк, которые содержат определенное значение или, еще лучше, одно из нескольких значений. Однако столбец неизвестен.
В настоящий момент я делаю следующее:
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)
Однако обработка полученного списка списков довольно утомительна.
Существует ли более эффективный способ поиска строк, содержащих значение (или больше) в любом столбце?
Ответы
Ответ 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"))