выбор и идентификация подмножества элементов на основе критериев - PullRequest
1 голос
/ 20 марта 2012

Я хотел бы выбрать подмножество элементов из целого, которые удовлетворяют определенным условиям.Есть около 20 элементов, каждый из которых имеет несколько атрибутов.Я хотел бы выбрать пять элементов, которые предлагают наименьшее количество расхождений из фиксированного критерия для одного атрибута и предлагают самое высокое среднее значение для другого атрибута.

Наконец, я хотел бы применить функцию к нескольким наборамиз 20 элементов.

До сих пор я был в состоянии идентифицировать подмножества "вручную", но я хотел бы иметь возможность возвращать индекс значений в дополнение к возврату самих значений.

Цели:

  1. Я хотел бы найти набор из пяти значений для X1, которые меньше всего отличаются от фиксированного значения (55) и обеспечивают наибольшее значениедля среднего Х2.

  2. Я хотел бы сделать это для нескольких наборов.


#####  generating example data
#####  this has five groups, each with two variables x1 and x2
set.seed(271828)

grp <- gl(5,20)
x1 <- round(rnorm(100,45, 12), digits=0)
x2 <- round(rbeta(100,2,4), digits = 2)
id <- seq(1,100,1)

#####  this is how the data would arrive for me to analyze
dat <- as.data.frame(cbind(id,grp,x1,x2))

Данные будут поступать в этом форматес id в качестве уникального идентификатора для каждого элемента.


#####  pulling out the first group for demonstration
dat.grp.1 <- dat[ which(grp == 1), ]

crit <- 55
x <- t(combn(dat.grp.1$x1, 5))
y <- t(combn(dat.grp.1$x2, 5))

mean.x <- rowMeans(x)
mean.y <- rowMeans(y)
k <- (mean.x - crit)^2

out <- cbind(x, mean.x, k, y, mean.y)

#####  finding the sets with the least amount of discrepancy
pick <- out[ which(k == min(k)), ]
pick

#####  finding the sets with low discrepancy and high values of y (means of X2) by "hand"
sorted <- out[order(k), ]
head(sorted, n=20)

Что касается значений в pick, я могу видеть, что значения X1:

> pick
                    mean.x  k                          mean.y
[1,] 55 47 48 48 52     50 25 0.62 0.08 0.31 0.18 0.54  0.346
[2,] 55 48 48 47 52     50 25 0.62 0.31 0.18 0.48 0.54  0.426

Я хотел бы вернуть значение id для этих элементов, чтобы я знал, что выбираю элементы: 3, 8, 10, 11 и 18 (выбирая набор 2, так как расхождение с k то же самое, но среднее значение для y выше).

> dat.grp.1 
    id grp x1   x2
 1   1   1 45 0.12
 2   2   1 27 0.34
 3   3   1 55 0.62
 4   4   1 39 0.32
 5   5   1 41 0.18
 6   6   1 29 0.47
 7   7   1 47 0.08
 8   8   1 48 0.31
 9   9   1 35 0.48
10  10   1 48 0.18
11  11   1 47 0.48
12  12   1 31 0.29
13  13   1 39 0.15
14  14   1 36 0.54
15  15   1 36 0.20
16  16   1 38 0.40
17  17   1 30 0.31
18  18   1 52 0.54
19  19   1 44 0.37
20  20   1 31 0.20

Пока что это «вручную» работает, но было бы хорошо, чтобы это было как можно более сложным.

Любая помощь очень ценится.

1 Ответ

2 голосов
/ 20 марта 2012

Вы почти у цели.Вы можете изменить определение sorted на

sorted <- out[order(k, -mean.y), ]

И тогда sorted[1,] (или, если вы предпочитаете sorted[1,,drop=FALSE]) - ваш выбранный набор.

Если вы хотите, чтобы индексы скореечем / в дополнение к пунктам, то вы можете включить это ранее.Замените:

x <- t(combn(dat.grp.1$x1, 5))
y <- t(combn(dat.grp.1$x2, 5))

на

idx <- t(combn(1:nrow(dat.grp.1), 5))
x <- t(apply(idx, 1, function(i) {dat.grp.1[i,"x1"]}))
y <- t(apply(idx, 1, function(i) {dat.grp.1[i,"x2"]}))

и включите idx в out позже.

Соедините все вместе:

#####  pulling out the first group for demonstration
dat.grp.1 <- dat[ which(grp == 1), ]

crit <- 55
idx <- t(combn(1:nrow(dat.grp.1), 5))
x <- t(apply(idx, 1, function(i) {dat.grp.1[i,"x1"]}))
y <- t(apply(idx, 1, function(i) {dat.grp.1[i,"x2"]}))

mean.x <- rowMeans(x)
mean.y <- rowMeans(y)
k <- (mean.x - crit)^2

out <- cbind(idx, x, mean.x, k, y, mean.y)

#####  finding the sets with the least amount of discrepancy and among
##### those the largest second mean
pick <- out[order(k, -mean.y)[1],,drop=FALSE]
pick

, который дает

                                 mean.x  k                          mean.y
[1,] 3 8 10 11 18 55 48 48 47 52     50 25 0.62 0.31 0.18 0.48 0.54  0.426

РЕДАКТИРОВАТЬ: запрошено описание применения свыше idx;Я хочу больше вариантов, чем просто то, что я могу сделать в комментарии, поэтому я добавляю его в свой ответ.Будут также рассмотрены циклы над подмножествами.

idx - это матрица (15504 x 5), каждая строка которой представляет собой набор (5) индексов для кадра данных.apply позволяет проходить строку за строкой (строка за строкой - поле 1), чтобы что-то делать с каждой строкой.Это что-то, возьмите значения и используйте их для индексации нужных строк dat.grp.1 и извлечения соответствующих значений x1.Я мог бы написать dat.grp.1[i,"x1"] как dat.grp.1$x1[i].Каждая строка idx становится столбцом, а результаты индексации в dat.grp.1 являются строками, поэтому все это необходимо транспонировать.

Вы можете разбить цикл на части, чтобы увидеть, как работает каждый шаг, еслитебе нравится.Превратите функцию в неанонимную функцию.

f <- function(i) {dat.grp.1[i,"x1"]}

и передайте ей строку idx.

> f(idx[1,])
[1] 45 27 55 39 41
> f(idx[2,])
[1] 45 27 55 39 29
> f(idx[3,])
[1] 45 27 55 39 47
> f(idx[4,])
[1] 45 27 55 39 48

Это то, что входит в x

> head(x,4)
     [,1] [,2] [,3] [,4] [,5]
[1,]   45   27   55   39   41
[2,]   45   27   55   39   29
[3,]   45   27   55   39   47
[4,]   45   27   55   39   48

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

find.best.set <- function(dat.grp.1) {
    crit <- 55
    idx <- t(combn(1:nrow(dat.grp.1), 5))
    x <- t(apply(idx, 1, function(i) {dat.grp.1[i,"x1"]}))
    y <- t(apply(idx, 1, function(i) {dat.grp.1[i,"x2"]}))

    mean.x <- rowMeans(x)
    mean.y <- rowMeans(y)
    k <- (mean.x - crit)^2

    out <- cbind(idx, x, mean.x, k, y, mean.y)

    out[order(k, -mean.y)[1],,drop=FALSE]
}

Это в основном то, что вы имели раньше, но избавляетесь от некоторых ненужных назначений.

Теперь оберните это в plyr вызов.

library("plyr")
ddply(dat, .(grp), find.best.set)

, который дает

  grp V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12  V13  V14  V15  V16  V17   V18
1   1  3  8 10 11 18 55 48 48 47  52  50  25 0.62 0.31 0.18 0.48 0.54 0.426
2   2  8 10 12 15 16 53 35 55 76  56  55   0 0.71 0.20 0.43 0.50 0.70 0.508
3   3  4 10 15 17 20 47 48 73 55  52  55   0 0.67 0.54 0.28 0.42 0.31 0.444
4   4  2 11 13 17 19 47 46 70 62  50  55   0 0.35 0.47 0.18 0.13 0.47 0.320
5   5  3  6 10 17 19 72 40 58 66  39  55   0 0.33 0.42 0.32 0.32 0.51 0.380

Я не знаю, что это лучший формат для ваших результатов, но он отражаетпример, который вы привели.

...