Каков наиболее эффективный способ вернуть ранги вектора на уровнях фактора, когда вектор имеет тот же порядок / длину, что и исходный вектор? - PullRequest
3 голосов
/ 31 января 2011

С еще одним требованием - чтобы результирующий вектор был в том же порядке, что и оригинал.

У меня есть очень простая функция, которая перцентирует вектор и работает так, как я хочу:

ptile <- function(x) {
  p <- (rank(x) - 1)/(length(which(!is.na(x))) - 1)
  p[p > 1] <- NA
  p 
}

data <- c(1, 2, 3, 100, 200, 300)

Например, ptile(data) генерирует:

[1] 0.0 0.2 0.4 0.6 0.8 1.0

Что я действительно хотел бы сделать, так это использовать эту же функцию (ptile) и заставить ее работать в уровняхфактора.Итак, предположим, что у меня есть «коэффициент» f следующим образом:

f <- as.factor(c("a", "a", "b", "a", "b", "b"))

Я хотел бы иметь возможность преобразовать «данные» в вектор, который говорит мне, для каждого наблюдения, что его соответствующий процентиль является относительнымк другим наблюдениям в пределах того же уровня, как это:

0.0 0.5 0.0 1.0 0.5 1.0

Как выстрел в темноте, я попытался:

tapply(data,f,ptile)

и вижу, что на самом деле это удаетсяделает ранжирование / процентилирование, но делает это так, что я понятия не имею, какие наблюдения соответствуют их показателям в исходном векторе:

[1] a a b a b b
Levels: a b
> tapply(data,f,ptile)
$a
[1] 0.0 0.5 1.0

$b
[1] 0.0 0.5 1.0

Это важно, потому что фактические данные, с которыми я работаю, могуту меня 1000-3000 наблюдений (запасы) и 10-55 уровней (такие как сектора, группировки по другим характеристикам запаса и т. д.), и мне нужно, чтобы результирующий вектор был в том же порядке, в каком он был, для всегоВыстраиваться в ряд, строка за строкой в ​​моей матрице.

Есть ли какой-нибудь вариант применения, который бы делал то, что я ищу?Или несколько быстрых строк, которые бы сработали?Я написал эту функциональность на C # и F # с гораздо большим количеством строк кода, но понял, что в R должно быть какое-то действительно прямое, элегантное решение.Есть ли?

Заранее спасибо!

Ответы [ 3 ]

10 голосов
/ 31 января 2011

Функция Ave очень полезна.Основная проблема заключается в том, чтобы помнить, что вам всегда нужно называть функцию с помощью FUN=:

 dt <- data.frame(data, f)
 dt$rank <-  with(dt, ave(data, list(f), FUN=rank))
     dt
    #---
      data f rank
    1    1 a    1
    2    2 a    2
    3    3 b    1
    4  100 a    3
    5  200 b    2
    6  300 b    3

Редактировать: я думал, что отвечаю на вопрос в названии, но меня попросили включить код, который используетфункция "ptile":

> dt$ptile <-  with(dt, ave(data, list(f), FUN=ptile))
> dt
  data f rank ptile
1    1 a    1   0.0
2    2 a    2   0.5
3    3 b    1   0.0
4  100 a    3   1.0
5  200 b    2   0.5
6  300 b    3   1.0
2 голосов
/ 31 января 2011

Когда вы звоните tapply() с INDEX=f, вы получаете результат, который подмножествен f и разбит на список в порядке уровней f.Чтобы полностью изменить этот процесс, просто:

unlist(tapply(data, f, ptile))[order(order(f))]

Ваш пример data вектор оказался в числовом порядке, но это работает, даже если данные в случайном порядке ...

ptile <- function(x) {
  p <- (rank(x) - 1)/(length(which(!is.na(x))) - 1)
  p[p > 1] <- NA
  # concatenated with the original data to make the match clear
  paste(round(p * 100, 2), x, sep="% ") 
}

data <- sample(c(1:5, (1:5)*100), 10)
f <- sample(letters[1:2], 10, replace=TRUE)
result <- unlist(tapply(data, f, ptile))[order(order(f))]

data.frame(result, data, f)
2 голосов
/ 31 января 2011

Для того, что вы пытаетесь сделать, я бы сначала поместил акции, сектора, значения в виде столбцов в фрейме данных. Например, с некоторыми вымышленными данными:

> set.seed(1)
> df <- data.frame(stock = 1:10,
+                  sector = sample(letters[1:2], 10, repl = TRUE),
+                  val = sample(1:10))
> df
   stock sector val
1      1      a   3
2      2      a   2
3      3      b   6
4      4      b  10
5      5      a   5
6      6      b   7
7      7      b   8
8      8      b   4
9      9      b   1
10    10      a   9

Затем вы можете использовать функцию ddply из пакета plyr, чтобы сделать "секторный" процентиль (есть и другие способы, но я считаю plyr очень полезным, и рекомендую вам взглянуть на это):

require(plyr)
df.p <- ddply(df, .(sector), transform, pct = ptile(val))

Теперь, конечно, в df.p строки будут упорядочены по коэффициенту (т. Е. sector), и восстановить его в исходном порядке очень просто, например ::

> df.p[ order(df.p$stock),]
   stock sector val       pct
1      1      a   3 0.3333333
2      2      a   2 0.0000000
5      3      b   6 0.4000000
6      4      b  10 1.0000000
3      5      a   5 0.6666667
7      6      b   7 0.6000000
8      7      b   8 0.8000000
9      8      b   4 0.2000000
10     9      b   1 0.0000000
4     10      a   9 1.0000000

В частности, столбец pct - это последний вектор, который вы ищете в своем исходном вопросе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...