我有一个数据框“m”,如下所示:
我正在尝试查找每个帐户最活跃的月份(大多数为V1)。例如对于帐户“2”,它将是“第6个月”,对于帐户3,它将是“第1个月”,....
我编写了下面的循环,它工作正常,但只需要很长时间,即使我只使用8000行,整个数据集有250,000行,所以下面的代码是不可用的。有没有人可以提出更好的方法来实现这一目标?
非常感谢。
答案 0 :(得分:3)
你可以使用plyr
来做到这一点library(plyr)
ddply(m, "AccountID", subset, V1==max(V1))
编辑:要按月获得结果,只需更改de“id”变量
即可library(plyr)
ddply(m, "Month", subset, V1==max(V1))
答案 1 :(得分:2)
我认为Owe Jessen的评论是正确的,这不是问题的答案。所以这是我在data.table
的帮助下拍摄的。
首先,让我们使用一个更容易理解的例子:
library(data.table)
DT <- data.table(AccountID = rep(1:3, each=4),
V1 = sample(1:100, 12, replace=FALSE),
Month = rep(1:4, times=3))
AccountID V1 Month
[1,] 1 81 1
[2,] 1 23 2
[3,] 1 72 3
[4,] 1 36 4
[5,] 2 22 1
[6,] 2 13 2
[7,] 2 50 3
[8,] 2 40 4
[9,] 3 74 1
[10,] 3 83 2
[11,] 3 4 3
[12,] 3 3 4
所以这里我们有3个帐户和4个月,每个帐户/月组合,我们有一个V1。因此,找到每个帐户的最大V1,我会执行以下操作:
setkey(DT, AccountID)
DT <- DT[, list(maxV1=max(V1)), by="AccountID"][DT]
DT[maxV1==V1]
AccountID maxV1 V1 Month
[1,] 1 81 81 1
[2,] 2 50 50 3
[3,] 3 83 83 2
这有点难以理解,所以让我尝试解释一下:我将AccountID设置为DT的关键。现在,我基本上在DT[, list(maxV1=max(V1)), by="AccountID"][DT]
中执行了两个步骤。首先,我计算每个帐户(DT[, list(maxV1=max(V1)), by="AccountID"]
)的最大V1值,然后在其后面调用[DT]
,我将新列maxV1
添加到旧DT
。显然,那时我只需要获取maxV1==V1
所持有的所有行。
将此解决方案应用于Nico的更高级示例,并向您展示如何将data.frame
转换为data.table
:
library(data.table)
DT <- as.data.table(m)
#Note that this line is only necessary if there are more than one rows per Month/AccountID combination
DT <- DT[, sum(V1), by="Month,AccountID"]
setkey(DT, AccountID)
DT <- DT[, list(maxV1=max(V1)), by="AccountID"][DT]
DT[maxV1==V1]
AccountID maxV1 Month V1
1 24660 1 24660
2 22643 2 22643
3 23642 3 23642
4 22766 5 22766
5 22445 12 22445
...
这恰好提供了50行。
编辑:
这是一个基础R解决方案:
df <- data.frame(AccountID = rep(1:3, each=4),
V1 = sample(1:100, 12, replace=FALSE),
Month = rep(1:4, times=3))
df$maxV1 <- ave(df$V1, df$AccountID, FUN = max)
df[df$maxV1==df$V1, ]
我的灵感来自here。
答案 2 :(得分:1)
我没有看到对这种算法进行矢量化的方法(如果其他人这样做,我很想知道如何)。
以下是我将如何编码(p.s:请在将来包含自包含的代码。看看?dput也是为了帮助):
make.data <- function(n = 100) # 250000
{
# Generate some random data
AccountID <- sample(1:50, n, replace=T)
V1 <- sample(1:100, n, replace=T)
Month <- sample(1:12, n, replace=T)
m <- data.frame(AccountID, V1, Month)
m
}
fo <- function(X)
{
unique_ID <- unique(X$AccountID)
M_max <- numeric(length(unique_ID ))
for(i in seq_along(unique_ID))
{
ss <- X$AccountID == unique_ID[i]
M_max [i] <- X[ss,"Month"][which.max(X[ss,"V1"])]
}
# results:
# M_max
data.frame(unique_ID , M_max)
}
X <- make.data(1000000)
system.time(fo(X))
# user system elapsed
# 2.32 0.33 2.70
我怀疑其中一些功能可能比您使用的功能更快(但值得测试时间)。
编辑: R的新JIT可能对您有所帮助(您可以在此处阅读更多相关信息:Speed up your R code using a just-in-time (JIT) compiler)我也尝试使用JIT,它没有加快速度。
将循环并行化可能也是值得的(但我现在不会进入)。
如果时机不切实际,可能需要使用data.table包(但我没有使用它的经验),甚至可以使用SQL ...
祝你好运,TalUPDATE :我使用了nico的示例,并将解决方案包装在函数中。时机非常好,不需要更先进的解决方案......
答案 3 :(得分:1)
这在我的笔记本电脑上使用250000行(加上它更干净)几乎是即时的
# Generate some random data
AccountID <- sample(1:50, 250000, replace=T)
V1 <- sample(1:100, 250000, replace=T)
Month <- sample(1:12, 250000, replace=T)
m <- data.frame(AccountID, V1, Month)
# Aggregate the data by month
V1.per.month <- aggregate(m$V1, sum, by=list(Month = m$Month))
编辑:重新阅读我意识到我忘记考虑帐户(双关语)的问题
但这应该是
V1.per.month <- aggregate(m$V1, sum,
by=list(Month = m$Month, Account= m$AccountID))
时序图(误差条为SD)。正如你所看到的那样,每100万行需要大约2.5s,这是非常可以接受的,我认为。
答案 4 :(得分:1)
我认为基本上这与Tal的相同解决方案
我通过以下循环获得合理的时间
# Generate some random data
AccountID <- sample(1:50, 250000, replace=T)
V1 <- sample(1:100, 250000, replace=T)
Month <- sample(1:12, 250000, replace=T)
m <- data.frame(AccountID, V1, Month)
# Aggregate the data by month
ac = as.numeric(levels(as.factor(m$AccountID)))
active.month = rep(NA, length(ac))
names(active.month) = ac
system.time(
{
for(i in ac)
{
subm = subset(m, AccountID == i)
active.month[i] = subm[which.max(subm[,"V1"]),"Month"]
}
})
User System verstrichen
0.78 0.14 0.92