Я хочу посчитать, сколько раз элемент в требуемом диапазоне появляется в каждой строке matrix
с дополнительным условием, которое я хочу рассмотреть только для первых n таких элементов в строке.
Похожий вопрос, без добавленного условия, появляется здесь:
подсчет N вхождений в пределах верхнего предела строки матрицы
Iнаписал код R
, чтобы делать то, что я хочу, но он использует вложенный for-loops
. Я также заменил вложенные for-loops
на sapply
операторы, но они также кажутся неэффективными.
Я надеюсь, что кто-то может предложить более эффективный подход в идеале в base R
. Ниже приведен пример набора данных, желаемый результат и функциональный аннотированный код R
.
Вот пример набора данных. Мои фактические наборы данных будут намного больше, и у меня их будет огромное количество. Итак, эффективность важна.
my.data <- matrix( c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
74, 22, 12, 13, 56, 0, 0, 0, 0, 0,
88, 77, 5, 77, 34, 98, 0, 0, 0, 0,
92, 0, 0, 0, 0, 0, 0, 0, 0, 0,
89, 0, 0, 0, 0, 0, 0, 0, 0, 0,
86, 72, 64, 40, 75, 58, 28, 66, 13, 98,
18, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
70, 51, 83, 13, 50, 30, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
28, 54, 43, 86, 50, 0, 0, 0, 0, 0,
45, 83, 0, 0, 0, 0, 0, 0, 0, 0,
39, 57, 58, 90, 84, 47, 36, 0, 0, 0,
76, 14, 71, 29, 0, 0, 0, 0, 0, 0,
23, 0, 0, 0, 0, 0, 0, 0, 0, 0,
7, 0, 0, 0, 0, 0, 0, 0, 0, 0,
77, 58, 90, 91, 47, 40, 58, 89, 0, 0,
89, 90, 0, 0, 0, 0, 0, 0, 0, 0,
83, 34, 61, 0, 0, 0, 0, 0, 0, 0,
17, 0, 0, 0, 0, 0, 0, 0, 0, 0,
62, 0, 0, 0, 0, 0, 0, 0, 0, 0,
10, 42, 5, 87, 61, 0, 0, 0, 0, 0,
90, 39, 99, 10, 84, 90, 93, 96, 69, 0,
84, 40, 44, 82, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
nrow = 25, ncol = 10, byrow = TRUE)
Вот мои желаемые результаты. Я читаю каждую строку слева направо, и 0 игнорируются.
# These are the number of elements per row that satisfy all conditions
desired.n.kept <- c(0, 2, 3, 0, 0, 3, 0, 0, 3, 0, 3, 2, 3, 2, 0, 0, 3, 0, 3, 0, 1, 2, 3, 3, 0)
# These are the number of elements per row that do not satisfy all conditions
# up through the specified limit on number of elements that do satisfy all conditions
desired.n.discarded <- c(0, 3, 2, 1, 1, 1, 1, 0, 0, 0, 2, 0, 0, 2, 1, 1, 2, 2, 0, 1, 0, 3, 6, 0, 0)
Здесь я объясняю несколько примеров строк.
В первой строке нет элементов, которые удовлетворяют всем условиям. Другими словами, нет элементов, которые >= 30 & <= 85
. Также нет элементов, которые не удовлетворяют всем условиям, < 30 | > 85
, учитывая, что 0 игнорируются. Таким образом, < 30 | > 85
лучше всего рассматривать как: (> 0 & < 30) | > 85
.
В третьем ряду три элемента находятся в желаемом диапазоне. Это второй, четвертый и пятый элементы, два 77
и 34
, потому что они >= 30 & <= 85
. Два элемента (88
и 5
) находятся за пределами желаемого диапазона [(> 0 & < 30) | > 85
] слева от третьего элемента, который находится в пределах желаемого диапазона, т. Е. Слева от пятого элемента, 34
. Шестой элемент, 98
, возникает после того, как достигнут предел в 3 сохраненных элемента, то есть после двух 77
и 34
. Итак, шестой элемент, 98
, игнорируется.
В шестом ряду три элемента удовлетворяют всем условиям: 72
, 64
и 40
. Эти три элемента являются первыми тремя, попадающими в желаемый диапазон: >= 30 & <= 85
. Один элемент, 86
, не удовлетворяет всем условиям (это > 85
) вверх через третий элемент, который сохраняется, то есть, вверх через 40
. Поскольку 40
является третьим элементом, попадающим в требуемый диапазон (>= 30 & <= 85
), все шесть элементов справа от 40
игнорируются независимо от того, находятся ли они в пределах или за пределами требуемого диапазона (75
,58
, 28
, 66
, 13
и 98
игнорируются).
Вот мой исходный код с использованием вложенного for-loops
:
# specify the desired range for individual elements
my.min <- 30
my.max <- 85
# specify maximum number of elements to keep within desired range per row
my.limit <- 3
my.cols <- ncol(my.data)
my.rows <- nrow(my.data)
# indicator matrix identifies elements inside the desired range
in.range <- matrix(0, nrow = my.rows, ncol = my.cols)
in.range[my.data >= my.min & my.data <= my.max] <- 1
# indicator matrix identifies elements outside the desired range
outside.range <- matrix(0, nrow = my.rows, ncol = my.cols)
outside.range[my.data > 0 & (my.data < my.min | my.data > my.max)] <- 1
# count elements that are within the desired range
count.in.range <- t(apply(in.range, 1, cumsum))
# truncate rows after my limit is reached
truncate.rows <- matrix(1, nrow = my.rows, ncol = my.cols)
for(i in 1:my.rows) {
for(j in 2:my.cols) {
if((count.in.range[i,(j-1)] >= my.limit) & (count.in.range[i,j] >= my.limit)) {truncate.rows[i,j] = 0}
}
}
# count the number of elements per row that satisfy all conditions
n.kept <- rowSums(truncate.rows * in.range)
# count the number of elements per row that do not satisfy all conditions
n.discarded <- rowSums(truncate.rows * outside.range)
# verify that my code returns the desired results
all.equal(n.kept, desired.n.kept)
#[1] TRUE
all.equal(n.discarded, desired.n.discarded)
#[1] TRUE
Вот функция sapply
, которую я написал вместо вложенной for-loops
. Это работает, но вы можете видеть, что это кажется слишком сложным:
# This sapply approach returns a matrix with only 9 columns and many NULL elements
truncate.rows2 <- matrix(1, nrow = my.rows, ncol = my.cols)
truncate.rows2 <- t(sapply(1:my.rows, function (i) {
sapply(2:my.cols, function(j) {
if((count.in.range[i,(j-1)] >= my.limit) & (count.in.range[i,j] >= my.limit)) {truncate.rows2[i,j] = 0}
})
}))
truncate.rows2
# modify truncate.rows2 to eliminate NULL elements and restore the first column
truncate.rows3 <- matrix(as.numeric(as.character(truncate.rows2)), ncol = (my.cols-1), nrow = my.rows)
truncate.rows3[is.na(truncate.rows3)] <- 1
truncate.rows3 <- cbind(truncate.rows[,1], truncate.rows3)
truncate.rows3
all.equal(truncate.rows, truncate.rows3)
#[1] TRUE