Как разделять при ранжировании по определенному столбцу?
Все:
У меня есть кадр данных, как показано ниже. Я знаю, что могу сделать глобальный порядок рангов следующим образом:
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 > )
Любая идея?
Ответы
Ответ 1
Мой путь, но там, скорее всего, лучше. Никогда не использовал ранг, даже не знал об этом. Спасибо, может быть полезно.
#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
EDIT:
Если вы не заботитесь о сохранении исходного порядка данных, тогда это работает с меньшим количеством кода:
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
Ответ 2
Множество опций.
Используя 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")))
Ответ 3
Вот несколько подходов:
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
и добавить соответствующее предложение, чтобы переупорядочить результат обратно в порядковый номер последовательности).
Ответ 4
Вы можете использовать пакет 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