如何在R中的一个向量中累积添加值
Posted
技术标签:
【中文标题】如何在R中的一个向量中累积添加值【英文标题】:how to cumulatively add values in one vector in R 【发布时间】:2014-02-20 15:52:08 【问题描述】:我有一个看起来像这样的数据集
id name year job job2
1 Jane 1980 Worker 0
1 Jane 1981 Manager 1
1 Jane 1982 Manager 1
1 Jane 1983 Manager 1
1 Jane 1984 Manager 1
1 Jane 1985 Manager 1
1 Jane 1986 Boss 0
1 Jane 1987 Boss 0
2 Bob 1985 Worker 0
2 Bob 1986 Worker 0
2 Bob 1987 Manager 1
2 Bob 1988 Boss 0
2 Bob 1989 Boss 0
2 Bob 1990 Boss 0
2 Bob 1991 Boss 0
2 Bob 1992 Boss 0
这里,job2
表示一个虚拟变量,表示一个人在该年是否为Manager
。我想对这个数据集做两件事:首先,我只想保留该人第一次成为Boss
时的行。其次,我想查看一个人作为Manager
的累积年数,并将此信息存储在变量cumu_job2
中。因此我想拥有:
id name year job job2 cumu_job2
1 Jane 1980 Worker 0 0
1 Jane 1981 Manager 1 1
1 Jane 1982 Manager 1 2
1 Jane 1983 Manager 1 3
1 Jane 1984 Manager 1 4
1 Jane 1985 Manager 1 5
1 Jane 1986 Boss 0 0
2 Bob 1985 Worker 0 0
2 Bob 1986 Worker 0 0
2 Bob 1987 Manager 1 1
2 Bob 1988 Boss 0 0
我已经更改了我的示例并将 Worker 职位包括在内,因为这更多地反映了我想要对原始数据集执行的操作。此线程中的答案仅在数据集中只有 Managers 和 Boss 时才有效 - 所以任何有关使这项工作的建议都会很棒。将不胜感激!!
【问题讨论】:
【参考方案1】:这是针对同一问题的简洁 dplyr
解决方案。
注意:在读取数据时确保stringsAsFactors = FALSE
。
library(dplyr)
dat %>%
group_by(name, job) %>%
filter(job != "Boss" | year == min(year)) %>%
mutate(cumu_job2 = cumsum(job2))
输出:
id name year job job2 cumu_job2
1 1 Jane 1980 Worker 0 0
2 1 Jane 1981 Manager 1 1
3 1 Jane 1982 Manager 1 2
4 1 Jane 1983 Manager 1 3
5 1 Jane 1984 Manager 1 4
6 1 Jane 1985 Manager 1 5
7 1 Jane 1986 Boss 0 0
8 2 Bob 1985 Worker 0 0
9 2 Bob 1986 Worker 0 0
10 2 Bob 1987 Manager 1 1
11 2 Bob 1988 Boss 0 0
解释
-
获取数据集
按姓名和工作分组
根据条件过滤每个组
添加
cumu_job2
列。
【讨论】:
与data.table
可以使用相同的过滤思想:dt[, list(cum_job2=cumsum(job2[job!="Boss" | year==min(year)])), by=c('name', 'job')]
@Ramnath 我想知道为什么这对我不起作用——我无法安装 dplyr 并且 %.% 不是函数。
%.%
是dplyr
中的一个函数。 dplyr
在 CRAN 上,所以使用 install_packages
安装应该很简单。
@Ramnath install.packages 中的警告:包“dplyr”不可用(对于 R 版本 3.0.0)您正在使用哪个 R 版本?
@Ramnath 我也试过 devtools::install_github("hadley/dplyr") 但它说客户端错误:404 未找到【参考方案2】:
这是使用within
和ave
的基本解决方案。我们假设输入是DF
,并且数据按照问题排序。
DF2 <- within(DF,
seq = ave(id, id, job, FUN = seq_along)
job2 = (job == "Manager") + 0
cumu_job2 = ave(job2, id, job, FUN = cumsum)
)
subset(DF2, job != 'Boss' | seq == 1, select = - seq)
REVISION:现在使用within
。
【讨论】:
【参考方案3】:我认为这可以满足您的需求,尽管数据必须按照您提供的方式进行排序。
my.df <- read.table(text = '
id name year job job2
1 Jane 1980 Worker 0
1 Jane 1981 Manager 1
1 Jane 1982 Manager 1
1 Jane 1983 Manager 1
1 Jane 1984 Manager 1
1 Jane 1985 Manager 1
1 Jane 1986 Boss 0
1 Jane 1987 Boss 0
2 Bob 1985 Worker 0
2 Bob 1986 Worker 0
2 Bob 1987 Manager 1
2 Bob 1988 Boss 0
2 Bob 1989 Boss 0
2 Bob 1990 Boss 0
2 Bob 1991 Boss 0
2 Bob 1992 Boss 0
', header = TRUE, stringsAsFactors = FALSE)
my.seq <- data.frame(rle(my.df$job)$lengths)
my.df$cumu_job2 <- as.vector(unlist(apply(my.seq, 1, function(x) seq(1,x))))
my.df2 <- my.df[!(my.df$job=='Boss' & my.df$cumu_job2 != 1),]
my.df2$cumu_job2[my.df2$job != 'Manager'] <- 0
id name year job job2 cumu_job2
1 1 Jane 1980 Worker 0 0
2 1 Jane 1981 Manager 1 1
3 1 Jane 1982 Manager 1 2
4 1 Jane 1983 Manager 1 3
5 1 Jane 1984 Manager 1 4
6 1 Jane 1985 Manager 1 5
7 1 Jane 1986 Boss 0 0
9 2 Bob 1985 Worker 0 0
10 2 Bob 1986 Worker 0 0
11 2 Bob 1987 Manager 1 1
12 2 Bob 1988 Boss 0 0
【讨论】:
【参考方案4】:由马修·道尔供稿:
dt[, .SD[job != "Boss" | year == min(year)][, cumjob := cumsum(job2)],
by = list(name, job)]
解释
-
获取数据集
运行过滤器并在 Data (
.SD
) 的每个 S子集内添加一列
按姓名和工作分组
旧版本:
您在这里有两个不同的拆分应用组合。一个获取累积职位,另一个获取第一行boss状态。这是data.table
中的一个实现,我们基本上分别进行每个分析(嗯,有点),然后使用rbind
在一个地方收集所有内容。主要需要注意的是 by=id
部分,这基本上意味着针对数据中的每个 id
分组评估其他表达式,这是您正确指出的尝试中缺少的内容。
library(data.table)
dt <- as.data.table(df)
dt[, cumujob:=0L] # add column, set to zero
dt[job2==1, cumujob:=cumsum(job2), by=id] # cumsum for manager time by person
rbind(
dt[job2==1], # this is just the manager portion of the data
dt[job2==0, head(.SD, 1), by=id] # get first bossdom row
)[order(id, year)] # order by id, year
# id name year job job2 cumujob
# 1: 1 Jane 1980 Manager 1 1
# 2: 1 Jane 1981 Manager 1 2
# 3: 1 Jane 1982 Manager 1 3
# 4: 1 Jane 1983 Manager 1 4
# 5: 1 Jane 1984 Manager 1 5
# 6: 1 Jane 1985 Manager 1 6
# 7: 1 Jane 1986 Boss 0 0
# 8: 2 Bob 1985 Manager 1 1
# 9: 2 Bob 1986 Manager 1 2
# 10: 2 Bob 1987 Manager 1 3
# 11: 2 Bob 1988 Boss 0 0
请注意,这假定表格在每个 id
中按年份排序,但如果它不够容易修复。
或者,您也可以通过以下方式实现相同的目的:
ans <- dt[, .I[job != "Boss" | year == min(year)], by=list(name, job)]
ans <- dt[ans$V1]
ans[, cumujob := cumsum(job2), by=list(name,job)]
这个想法基本上是获取条件匹配的行号(使用.I
- 内部变量),然后在这些行号上子集dt
($v1
部分),然后只执行累积和。
【讨论】:
非常感谢!我有一个错误 - RHS 类型('integer')必须匹配 LHS('double')。对于最快的情况,检查和强制会影响性能。要么更改目标列的类型,要么自己强制 := 的 RHS(例如,使用 1L 而不是 1) 这个我不太明白,因为我确实通过 as.integer 命令把 id 和 job2 向量变成了整数向量.... 我阅读了***.com/questions/16361225/… 并解决了问题 - 只是做了 cumujob:=as.numeric(cumsum(job2)) 代替。 就可读性而言,我会选择:dt[, .SD[job != "Boss" | year == min(year)][, cumjob := cumsum(job2)], by = list(name, job)]
@eddi 嗨——我还有一个问题!因此,如果我有除经理以外的其他职位,我将如何保存所有信息?我没有在我的问题中明确指定这部分,但我认为 dt[job2==1] 这样做会在我的数据集中丢弃大量信息。【参考方案5】:
@BrodieG 的更好:
数据
dat <- read.table(text="id name year job job2
1 Jane 1980 Manager 1
1 Jane 1981 Manager 1
1 Jane 1982 Manager 1
1 Jane 1983 Manager 1
1 Jane 1984 Manager 1
1 Jane 1985 Manager 1
1 Jane 1986 Boss 0
1 Jane 1987 Boss 0
2 Bob 1985 Manager 1
2 Bob 1986 Manager 1
2 Bob 1987 Manager 1
2 Bob 1988 Boss 0
2 Bob 1989 Boss 0
2 Bob 1990 Boss 0
2 Bob 1991 Boss 0
2 Bob 1992 Boss 0", header=TRUE)
#代码:
inds1 <- rle(dat$job2)
inds2 <- cumsum(inds1[[1]])[inds1[[2]] == 1] + 1
ends <- cumsum(inds1[[1]])
starts <- c(1, head(ends + 1, -1))
inds3 <- mapply(":", starts, ends)
dat$id <- rep(1:length(inds3), sapply(inds3, length))
dat <- do.call(rbind, lapply(split(dat[, 1:5], dat$id ), function(x)
if(x$job2[1] == 0)
x$cumu_job2 <- rep(0, nrow(x))
else
x$cumu_job2 <- 1:nrow(x)
x
))
keeps <- dat$job2 > 0
keeps[inds2] <- TRUE
dat2 <- data.frame(dat[keeps, ], row.names = NULL)
dat2
## id name year job job2 cumu_job2
## 1 1 Jane 1980 Manager 1 1
## 2 1 Jane 1981 Manager 1 2
## 3 1 Jane 1982 Manager 1 3
## 4 1 Jane 1983 Manager 1 4
## 5 1 Jane 1984 Manager 1 5
## 6 1 Jane 1985 Manager 1 6
## 7 2 Jane 1986 Boss 0 0
## 8 3 Bob 1985 Manager 1 1
## 9 3 Bob 1986 Manager 1 2
## 10 3 Bob 1987 Manager 1 3
## 11 4 Bob 1988 Boss 0 0
【讨论】:
猜你反对简洁? @pssguy 我猜你对礼仪有偏见?不过你的回答很简洁。以上是关于如何在R中的一个向量中累积添加值的主要内容,如果未能解决你的问题,请参考以下文章