Как разделить при ранжировании в определенном столбце?

Все:

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

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 > )

Есть идеи?


person RobinMin    schedule 01.04.2012    source источник


Ответы (4)


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

#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
person Tyler Rinker    schedule 01.04.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")))
person joran    schedule 01.04.2012
comment
Хороший ответ, как обычно. Чтобы получить максимальную производительность от data.table, лучше избегать .SD, когда это возможно. Это должно быть быстрее для больших data.tables (где вы, скорее всего, будете использовать пакет в первую очередь!): DT <- data.table(dt,key = c("ID", "Value")); DT[, list(Value, Order=seq_len(.N)), by=ID] - person Josh O'Brien; 01.04.2012
comment
Я пытался реализовать ваше решение data.table, но ранг всего 1 для каждой строки. Я использовал ваш код почти дословно, только изменив имена переменных. Не подскажете, какую ошибку я могу совершить? Я знаю, что вы не видите код, так что это сложный вопрос, но я не хотел повторять вопрос. - person Kory; 28.07.2015
comment
Думаю, я просто добавлю в комментарий здесь, что data.table теперь предоставляет функцию Fast-Rank frank... должна в значительной степени входить там, где у вас есть rank выше... К сожалению, для меня это все еще чертовски медленно, поскольку я Я имею дело с несколькими миллионами групп. Опубликую больше, если найду более быстрый метод. - person Chris J.T. Auld; 22.05.2016
comment
Вместо этого уродливого do.call(rbind(lapply())) используйте функцию, предназначенную для этого: ave(). - person Joris Meys; 10.07.2017
comment
может кто-нибудь переписать это с помощью dplyr, пожалуйста - person userJT; 06.10.2017

Вот несколько подходов:

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, чтобы переупорядочить результат обратно в порядок порядкового номера).

person G. Grothendieck    schedule 01.04.2012
comment
Я знаю, что это было давно, но не могли бы вы рассказать о своем первом методе? Кажется, он дает мне ранг один для каждой записи в моей таблице. У меня есть только столбец, который я хочу сгруппировать по второму, и столбец, который я хочу ранжировать в первом аргументе, как у вас здесь. - person Kory; 28.07.2015
comment
Я добавил некоторые пояснения и вывод. - person G. Grothendieck; 28.07.2015

Вы можете использовать пакет 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
person User16    schedule 10.07.2017