我有一些像这样的数据集:
# date # value class
1984-04-01 95.32384 A
1984-04-01 39.86818 B
1984-07-01 43.57983 A
1984-07-01 10.83754 B
现在我想按数据对数据进行分组,并从A类中减去B类的值。 我研究了ddply,总结,融化和聚合,但不能完全得到我想要的东西。有办法轻松完成吗?请注意,我每个日期都有两个值,一个是A类,另一个是B类。我的意思是我可以将它重新排列成两个dfs,按日期和类顺序排列并再次合并,但我觉得还有更多的R方式去做吧。
答案 0 :(得分:7)
假设这个数据框(在Prasad的帖子中生成,但重现性为set.seed
):
set.seed(123)
DF <- data.frame( date = rep(seq(as.Date('1984-04-01'),
as.Date('1984-04-01') + 3, by=1),
1, each=2),
class = rep(c('A','B'), 4),
value = sample(1:8))
然后我们考虑七种解决方案:
1) zoo 可以为我们提供一行解决方案(不包括library
语句):
library(zoo)
z <- with(read.zoo(DF, split = 2), A - B)
提供此zoo
系列:
> z
1984-04-01 1984-04-02 1984-04-03 1984-04-04
-3 3 3 -5
另请注意,as.data.frame(z)
或data.frame(time = time(z), value = coredata(z))
会提供数据框;但是,您可能希望将其保留为动物园对象,因为它是一个时间序列,并且在此形式下可以更方便地对其进行其他操作,例如, plot(z)
2) sqldf 还可以提供一个语句解决方案(除library
调用外):
> library(sqldf)
> sqldf("select date, sum(((class = 'A') - (class = 'B')) * value) as value
+ from DF group by date")
date value
1 1984-04-01 -3
2 1984-04-02 3
3 1984-04-03 3
4 1984-04-04 -5
3) tapply 可以作为受sqldf解决方案启发的解决方案的基础:
> with(DF, tapply(((class =="A") - (class == "B")) * value, date, sum))
1984-04-01 1984-04-02 1984-04-03 1984-04-04
-3 3 3 -5
4)聚合可以与上面的sqldf
和tapply
相同的方式使用(尽管基于aggregate
的略有不同的解决方案已经出现):
> aggregate(((DF$class=="A") - (DF$class=="B")) * DF["value"], DF["date"], sum)
date value
1 1984-04-01 -3
2 1984-04-02 3
3 1984-04-03 3
4 1984-04-04 -5
5)doBy包中的 summaryBy 可以提供另一种解决方案,尽管它需要transform
来帮助它:
> library(doBy)
> summaryBy(value ~ date, transform(DF, value = ((class == "A") - (class == "B")) * value), FUN = sum, keep.names = TRUE)
date value
1 1984-04-01 -3
2 1984-04-02 3
3 1984-04-03 3
4 1984-04-04 -5
来自remix软件包的 remix 也可以使用transform
并且具有特别漂亮的输出:
> library(remix)
> remix(value ~ date, transform(DF, value = ((class == "A") - (class == "B")) * value), sum)
value ~ date
============
+------+------------+-------+-----+
| | sum |
+======+============+=======+=====+
| date | 1984-04-01 | value | -3 |
+ +------------+-------+-----+
| | 1984-04-02 | value | 3 |
+ +------------+-------+-----+
| | 1984-04-03 | value | 3 |
+ +------------+-------+-----+
| | 1984-04-04 | value | -5 |
+------+------------+-------+-----+
7)Hmisc包中的 summary.formula 也有很好的输出:
> library(Hmisc)
> summary(value ~ date, data = transform(DF, value = ((class == "A") - (class == "B")) * value), fun = sum, overall = FALSE)
value N=8
+----+----------+-+-----+
| | |N|value|
+----+----------+-+-----+
|date|1984-04-01|2|-3 |
| |1984-04-02|2| 3 |
| |1984-04-03|2| 3 |
| |1984-04-04|2|-5 |
+----+----------+-+-----+
答案 1 :(得分:6)
我能想到的最简单方法是使用dcast
包中的reshape2
来创建一个数据框,每行包含一个日期,列A
和{{1} },然后使用B
执行transform
:
A-B
答案 2 :(得分:5)
在基础R中,我会使用aggregate
和sum
来解决问题。这可以通过将B类的每个值转换为负值来实现:
(使用@PrasadChalasani提供的数据)
df <- within(df, value[class=="B"] <- -value[class=="B"])
aggregate(df$value, by=list(date=df$date), sum)
date x
1 1984-04-01 3
2 1984-04-02 2
3 1984-04-03 2
4 1984-04-04 1
答案 3 :(得分:4)
为了记录,我最喜欢重塑选项。这是一个使用摘要的plyr选项:
library(plyr)
ddply(df, "date", summarise
, A = value[class == "A"]
, B = value[class == "B"]
, A_B = value[class == "A"] - value[class == "B"]
)