Как разделить при ранжировании по конкретному столбцу? - PullRequest
14 голосов
/ 01 апреля 2012

Все:

У меня есть фрейм данных, подобный следующему. Я знаю, что могу сделать глобальный порядок рангов следующим образом:

dt <- data.frame(
    ID = c('A1','A2','A4','A2','A1','A4','A3','A2','A1','A3'),
    Value = c(4,3,1,3,4,6,6,1,8,4)
);
> dt
   ID Value
1  A1     4
2  A2     3
3  A4     1
4  A2     3
5  A1     4
6  A4     6
7  A3     6
8  A2     1
9  A1     8
10 A3     4
dt$Order <- rank(dt$Value,ties.method= "first")
> dt
   ID Value Order
1  A1     4     5
2  A2     3     3
3  A4     1     1
4  A2     3     4
5  A1     4     6
6  A4     6     8
7  A3     6     9
8  A2     1     2
9  A1     8    10
10 A3     4     7

Но как я могу установить порядок рангов дляконкретный идентификатор вместо глобального порядка ранга.Как я могу это сделать?В T-SQL мы можем сделать это следующим синтаксисом:

RANK() OVER ( [ < partition_by_clause > ] < order_by_clause > )

Есть идеи?

Ответы [ 4 ]

13 голосов
/ 01 апреля 2012

Множество опций.

Использование ddply из пакета plyr :

library(plyr)
ddply(dt,.(ID),transform,Order = rank(Value,ties.method = "first"))
   ID Value Order
1  A1     4     1
2  A1     4     2
3  A1     8     3
4  A2     3     2
5  A2     3     3
6  A2     1     1
7  A3     6     2
8  A3     4     1
9  A4     1     1
10 A4     6     2

Или, если производительность является проблемой (т. Е. Очень большие данные), используя data.table пакет:

library(data.table)
DT <- data.table(dt,key = "ID")
DT[,transform(.SD,Order = rank(Value,ties.method = "first")),by = ID]
      ID Value Order
 [1,] A1     4     1
 [2,] A1     4     2
 [3,] A1     8     3
 [4,] A2     3     2
 [5,] A2     3     3
 [6,] A2     1     1
 [7,] A4     1     1
 [8,] A4     6     2
 [9,] A3     6     2
[10,] A3     4     1

или во всех его подробностях базовое решение R с использованием split lapply do.call и rbind:

do.call(rbind,lapply(split(dt,dt$ID),transform,
              Order = rank(Value,ties.method = "first")))
6 голосов
/ 01 апреля 2012

Вот пара подходов:

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

Rank <- function(x) rank(x, ties.method = "first")
transform(dt, rank = ave(Value, ID, FUN = Rank))

, что дает:

   ID Value rank
1  A1     4    1
2  A2     3    2
3  A4     1    1
4  A2     3    3
5  A1     4    2
6  A4     6    2
7  A3     6    2
8  A2     1    1
9  A1     8    3
10 A3     4    1

Обратите внимание, что приведенное выше решение сохраняет исходный порядок строк.После этого его можно отсортировать, если это необходимо.

sqldf с RPostgreSQL

# see FAQ #12 on the sqldf github home page for info on sqldf and PostgreSQL
# https://cran.r-project.org/web/packages/sqldf/README.html

library(RPostgreSQL)
library(sqldf)

sqldf('select 
          *, 
          rank() over (partition by "ID" order by "Value") rank 
       from "dt"
')

Это решение переупорядочивает строки.Предполагается, что это нормально, поскольку ваше примерное решение сделало это (но если нет, добавьте столбец порядкового номера к dt и добавьте соответствующий пункт order by, чтобы переупорядочить результат обратно в порядок порядкового номера).

4 голосов
/ 01 апреля 2012

Мой путь, но, вероятно, лучше.Никогда не использовал звание, даже не знаю об этом.Спасибо, может быть полезно.

#Your Data
dt <- data.frame(
    ID = c('A1','A2','A4','A2','A1','A4','A3','A2','A1','A3'),
    Value = c(4,3,1,3,4,6,6,1,8,4)
)
dt$Order <- rank(dt$Value,ties.method= "first")

#My approach
dt$id <- 1:nrow(dt) #needed for ordering and putting things back together
dt <- dt[order(dt$ID),]
dt$Order.by.group <- unlist(with(dt, tapply(Value, ID, function(x) rank(x, 
    ties.method = "first"))))
dt[order(dt$id), -4]

Выход:

   ID Value Order Order.by.group
1  A1     4     5              1
2  A2     3     3              2
3  A4     1     1              1
4  A2     3     4              3
5  A1     4     6              2
6  A4     6     8              2
7  A3     6     9              2
8  A2     1     2              1
9  A1     8    10              3
10 A3     4     7              1

РЕДАКТИРОВАТЬ:

Если вы не заботитесь о сохранении оригиналапорядок данных, то это работает с меньшим кодом:

dt <- dt[order(dt$ID),]
dt$Order.by.group <- unlist(with(dt, tapply(Value, ID, function(x) rank(x, 
   ties.method= "first"))))

   ID Value Order.by.group
1  A1     4              1
5  A1     4              2
9  A1     8              3
2  A2     3              2
4  A2     3              3
8  A2     1              1
7  A3     6              2
10 A3     4              1
3  A4     1              1
6  A4     6              2
0 голосов
/ 10 июля 2017

Вы можете использовать пакет data.table.

setDT(dt) dt[, Order := rank(Value, ties.method = "first"), by = "ID"] dt <- as.data.frame(dt)

, давая желаемый результат:

   ID Value Order
1  A1     4     1
2  A2     3     2
3  A4     1     1
4  A2     3     3
5  A1     4     2
6  A4     6     2
7  A3     6     2
8  A2     1     1
9  A1     8     3
10 A3     4     1
...