R Example - ddply, ave, and merge

3

我写了一段代码,如果你们能提供更好的方法来完成我想做的事情,那就太好了。给出的dt如下:

   SIC FYEAR AU       AT
1    1  2003  6  212.748
2    1  2003  5 3987.884
3    1  2003  4  100.835
4    1  2003  4 1706.719
5    1  2003  5    9.159
6    1  2003  7   60.069
7    1  2003  5  100.696
8    1  2003  4  113.865
9    1  2003  6  431.552
10   1  2003  7  309.109 ...

我的工作是为给定的SIC和FYEAR创建一个新的列,该列将由具有最高AT百分比的AU和最高AT和次高AT之间的差异确定其值为1,否则为0。以下是我尝试完成上述工作的代码。

a <- ddply(dt,.(SIC,FYEAR),function(x){ddply(x,.(AU),function(x) sum(x$AT))});

   SIC FYEAR AU        V1
1    1  2003  4  3412.619
2    1  2003  5 13626.241
3    1  2003  6   644.300
4    1  2003  7  1478.633
5    1  2003  9     0.003
6    1  2004  4  3976.242
7    1  2004  5  9383.516
8    1  2004  6   457.023
9    1  2004  7   456.167
10   1  2004  9   238.282

V1代表特定SIC和FYEAR下给定AU的所有行的AT总和。接下来我做:

a$V1 <- ave(a$V1, a$SIC, a$FYEAR, FUN = function(x) x/sum(x));

   SIC FYEAR AU           V1
1    1  2003  4 1.780949e-01
2    1  2003  5 7.111150e-01
3    1  2003  6 3.362420e-02
4    1  2003  7 7.716568e-02
5    1  2003  9 1.565615e-07
6    1  2004  4 2.740114e-01
7    1  2004  5 6.466382e-01
8    1  2004  6 3.149444e-02
9    1  2004  7 3.143545e-02
10   1  2004  9 1.642052e-02

V1列现在代表了特定SIC和FYEAR下每个AU对AT贡献的百分比值。接下来,

a$V2 <- ave(a$V1, a$SIC, a$FYEAR, FUN = function(x) {t<-((sort(x, TRUE))[2]); 
                                                    ifelse((x-t)> 0.1,1,0)});

   SIC FYEAR AU           V1 V2
1    1  2003  4 1.780949e-01  0
2    1  2003  5 7.111150e-01  1
3    1  2003  6 3.362420e-02  0
4    1  2003  7 7.716568e-02  0
5    1  2003  9 1.565615e-07  0
6    1  2004  4 2.740114e-01  0
7    1  2004  5 6.466382e-01  1
8    1  2004  6 3.149444e-02  0
9    1  2004  7 3.143545e-02  0
10   1  2004  9 1.642052e-02  0

针对给定的SIC和FYEAR,找出对AT贡献最大的AU,如果其贡献比例差异大于10%,则该AU得到1,否则得到0。

然后将结果与原始数据dt合并。

dt <- merge(dt,a,key=c("SIC","FYEAR","AU"));

   SIC FYEAR AU       AT           V1 V2
1    1  2003  4 1706.719 1.780949e-01  0
2    1  2003  4  100.835 1.780949e-01  0
3    1  2003  4  113.865 1.780949e-01  0
4    1  2003  4 1491.200 1.780949e-01  0
5    1  2003  5 3987.884 7.111150e-01  1
6    1  2003  5  100.696 7.111150e-01  1
7    1  2003  5   67.502 7.111150e-01  1
8    1  2003  5 9461.000 7.111150e-01  1
9    1  2003  5    9.159 7.111150e-01  1
10   1  2003  6  212.748 3.362420e-02  0

我所做的很繁琐,有没有更好的方法来完成相同的工作?谢谢。


3
你使用软件包没问题,但不喜欢繁琐的语法。当有更好的选择时,你却不喜欢它,因为它使用了一个软件包?嗯?我认为您应该恢复您的答案,如果OP想出于任何原因返回到data.frame,我不明白有什么阻止他们这样做。 - eddi
1
我是一个R语言初学者。我想掌握基本的R语言知识,因此即使我的数据大小超过70k行40列,我也不想使用data table。我相信对于我的需求来说,data table会更快,但我会坚持使用data frame以便熟练掌握R语言。 - Sumit
你应该意识到ddply不是基本的R函数,对吧?无论如何,每个人都有自己的选择,只是想指出你的逻辑毫无意义。 - eddi
我必须说,我真的很努力地想理解你的意思。你的例子不完整,因为我没有看到所有的数据,也无法验证自己的代码。然而,似乎你的问题一旦被正确定义,就可以在基本的R语言中很简单地解决。但在我更好地了解你的目标、看到更充分的例子并且至少有点理解上下文之前,我无法确定并告诉你如何做。 - amit
2个回答

3
这里有一个使用 data.table 的版本:
require(data.table)
DT <- data.table(your_data_frame)
setkey(DT, SIC, FYEAR, AU)
DT[setkey(DT[, sum(AT), by=key(DT)][, V1 := V1/sum(V1), 
          by=list(SIC, FYEAR)])[, V2 := (V1 - V1[.N-1] > 0.1) * 1, 
          by=list(SIC, FYEAR)]]

这部分代码DT[, sum(AT), by=key(DT)][, V1 := V1/sum(V1), by=list(SIC, FYEAR)]首先对所有三列进行求和,然后通过引用将V1替换为SIC、FYEAR列的V1/sum(V1)。包裹此代码的setkey对所有四列进行排序。因此,倒数第二个值始终是第二高的值(在没有重复值的情况下)。使用这个方法,我们可以创建V2[, V2 := (V1 - V1[.N-1] > 0.1) * 1, by=list(SIC, FYEAR)]],通过引用实现。一旦我们有了这个,我们可以通过使用DT[.]进行join

希望这能帮到你。


我正在寻找一种不将数据框转换为数据表的解决方案。我对纯数据框解决方案感兴趣。谢谢。 - Sumit

3

我不确定被删除的回答是否与此相同,但您可以用几行代码有效地完成它。

# Simulate data
set.seed(1)
n<-1000
dt<-data.frame(SIC=sample(1:10,n,replace=TRUE),FYEAR=sample(2003:2007,n,replace=TRUE),
AU=sample(1:7,n,replace=TRUE),AT=abs(rnorm(n)))

# Cacluate proportion.
dt$prop<-ave(dt$AT,dt$SIC,dt$FYEAR,FUN=prop.table)
# Find AU with max proportion.
dt$au.with.max.prop<-
  ave(dt,dt$SIC,dt$FYEAR,FUN=function(x)x$AU[x$prop==max(x$prop)])[,1]

这完全依靠基础,并避免了使用merge,因此速度不会太慢。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接