2012-11-02 50 views
2

所以我有一个包含四列的数据框:课程ID,用户ID,日期(一个整数)和累计积分接收。我想要做的是,对于每个用户课程对,请使用lowess来平滑课程中所有日期的累积分数。 lowess函数需要一个向量,应用平滑算法,然后返回两个向量xy ...我只对y向量感兴趣。在数据框上聚合lowess()

我的第一个想法是

aggregate(df$CumulativePointsReceived, 
      list(df$UserID, df$CourseID), 
      function(x) lowess(x)$y) 

但是,它返回一个基本不可用数据帧,其中第三列是这些向量的列表。我想要的是一个与输入df完全相同的数据框,但每个用户 - 课程日都有一列平滑点值。我确信有一个非循环的方式来做到这一点,但我似乎无法以正确的方式思考。在此先感谢...

这是df中的第一个用户 - 课程对的dput。我本可以投入更多,但是对于每个用户课程来说,它会变得很愚蠢,并且会有110天的时间。

structure(list(CourseID = c(6567146L, 6567146L, 6567146L, 6567146L, 
6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 
6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 
6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 
6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 
6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 
6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 
6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 
6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 
6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 
6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 
6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 
6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 
6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 
6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 
6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 6567146L, 
6567146L), UserID = c(4759679L, 4759679L, 4759679L, 4759679L, 
4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 
4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 
4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 
4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 
4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 
4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 
4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 
4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 
4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 
4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 
4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 
4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 
4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 
4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 
4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 4759679L, 
4759679L), DayInCourse = 1:110, CumulativePointsReceived = c(0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 47, 47, 47, 47, 47, 47, 47, 
47, 47, 47, 47, 47, 107, 107, 107, 107, 107, 107, 107, 107, 107, 
107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 
107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 
107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 
107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 
107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 
107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, 107)), .Names =  c("CourseID", 
"UserID", "DayInCourse", "CumulativePointsReceived"), row.names =  c(46085L, 
46118L, 46120L, 46133L, 46102L, 46086L, 46182L, 46184L, 46159L, 
46139L, 46088L, 46090L, 46144L, 46161L, 46187L, 46113L, 46177L, 
46193L, 46151L, 46143L, 46126L, 46121L, 46104L, 46170L, 46128L, 
46131L, 46167L, 46098L, 46127L, 46178L, 46101L, 46129L, 46152L, 
46175L, 46093L, 46122L, 46096L, 46136L, 46106L, 46116L, 46148L, 
46173L, 46189L, 46117L, 46172L, 46162L, 46164L, 46108L, 46091L, 
46112L, 46135L, 46181L, 46190L, 46171L, 46169L, 46100L, 46141L, 
46103L, 46168L, 46110L, 46107L, 46089L, 46154L, 46165L, 46125L, 
46163L, 46147L, 46166L, 46183L, 46160L, 46150L, 46097L, 46115L, 
46157L, 46194L, 46138L, 46188L, 46153L, 46155L, 46179L, 46180L, 
46191L, 46095L, 46176L, 46111L, 46105L, 46142L, 46087L, 46109L, 
46158L, 46145L, 46114L, 46192L, 46140L, 46146L, 46174L, 46094L, 
46124L, 46149L, 46119L, 46186L, 46130L, 46134L, 46156L, 46185L, 
46099L, 46123L, 46137L, 46132L, 46092L), class = "data.frame") 

回答

3

你可以用基本的R函数来做到这一点。例如。

lapply(split(df, list(df$UserID, df$CourseID)), 
     function(x) with(x, lowess(DayInCourse, CumulativePointsReceived))$y) 

返回:

$`4759679.6567146` 
    [1] 40.92152 42.50447 44.08898 45.67481 47.26167 48.84919 
    [7] 50.43697 52.02450 53.61120 55.19639 56.77928 58.35896 
[13] 59.93435 61.50424 63.06724 64.62175 66.16596 67.69780 
[19] 69.21547 70.71909 72.20948 73.68773 75.15522 76.61367 
[25] 78.06516 79.51217 80.95767 82.40508 83.85843 85.32230 
[31] 86.80193 88.30315 89.83235 91.39619 93.00115 94.65248 
[37] 96.35240 98.75650 100.73124 102.31467 103.55841 104.51780 
[43] 105.24556 105.78855 106.18658 106.47246 106.67275 106.80862 
[49] 106.89685 106.95067 106.98051 106.99458 106.99936 107.00000 
[55] 107.00000 107.00000 107.00000 107.00000 107.00000 107.00000 
[61] 107.00000 107.00000 107.00000 107.00000 107.00000 107.00000 
[67] 107.00000 107.00000 107.00000 107.00000 107.00000 107.00000 
[73] 107.00000 107.00000 107.00000 107.00000 107.00000 107.00000 
[79] 107.00000 107.00000 107.00000 107.00000 107.00000 107.00000 
[85] 107.00000 107.00000 107.00000 107.00000 107.00000 107.00000 
[91] 107.00000 107.00000 107.00000 107.00000 107.00000 107.00000 
[97] 107.00000 107.00000 107.00000 107.00000 107.00000 107.00000 
[103] 107.00000 107.00000 107.00000 107.00000 107.00000 107.00000 
[109] 107.00000 107.00000 

我们可以修改此方法包括转换步骤:

out <- lapply(split(df, list(df$UserID, df$CourseID)), 
       function(x) transform(x, smooth = lowess(DayInCourse,   
            CumulativePointsReceived)$y)) 

> head(out[[1]]) 
     CourseID UserID DayInCourse CumulativePointsReceived smooth 
46085 6567146 4759679   1      0 40.92152 
46118 6567146 4759679   2      0 42.50447 
46120 6567146 4759679   3      0 44.08898 
46133 6567146 4759679   4      0 45.67481 
46102 6567146 4759679   5      0 47.26167 
46086 6567146 4759679   6      0 48.84919 

因为你只提供一个过程/用户组合,结果是一个列表只有一个组件。在现实世界的例子中,列表将包含更多的组件。在这种情况下做

final <- do.call(rbind, out) 

的原因aggregate()步骤失败是要传递lowess()一个数据帧,它需要两个向量xy。我不认为这是正确的做法。除非你想学习plyr,否则手工操作拆分应用组合将成为一种方法。

+0

我不得不将'drop = T'添加到'split'调用中,因为并不是每个用户都需要使用每一个课程,而'lowess'不能处理一个空的'x',但这对我很有用!非常感谢。 –

1

我认为这将是更容易plyr

df <- ddply(df, .(CourseID, UserID), transform, 
     smoothed = lowess(DayInCourse, CumulativePointsReceived)$y) 

plyr总体理念是 “分开申请,结合”。为ddply函数的语法(它接受一个数据帧,并返回一个数据帧 - 存在用于数组或列表等功能)是

ddply(dataframe, field-list, function, function-args) 

然后,该函数分割数据帧到行的块,其中所有的在​​指定的字段的值是相同的。然后这需要这些块中的每一个,并用然后的任何附加的function-args这些函数调用的结果被组合成一个单一的数据帧一起适用function

下面是一个例子:

ddply(mtcars, "cyl", colMeans) 

在这种情况下,colMeans是一个函数,它接受在一个数据帧中的每个列的平均值,所以该装置是分开的用于cyl每个值。

你也可以指定你自己的功能:

ddply(mtcars, "cyl", function(df) c(hp.mean=mean(df$hp), hp.sd=sd(df$hp))) 

我们解释transformtransform是用于添加新列的数据帧没有难看索引一个方便的功能。比较以下两个相同的调用:

Orange$score <- Orange$age * Orange$circumference^2 

Orange <- transform(Orange, score = age * circumference^2) 

第二个版本更易于阅读且不易出错。从此示例中可以看出,transform的语法是

tranform(dataframe, myname2 = some-value, myname2 = some-other-value) 

等等。在plyr函数调用中使用时

transform真正显示出它自己。在我上面给出的例子中,是smoothed = lowess(DayInCourse, CumulativePointsReceived)$y)简单地传递到变换一个额外的参数,所以对于每个块中的分割数据帧xddply applys transform作为

transform(x, smoothed = lowess(DayInCourse, CumulativePointsReceived)$y)) 

,然后组合的结果。

+0

我想我应该学习'plyr'的时候了。非常感谢解决方案!我仍然确定它是正确的,但目前看起来不错... –

+1

@seancormody如果你有第二个,你会介意解释“变换”的论点吗?我想这是作为.fun传递的,但我真的不知道平滑= lowess()部分是如何进入......有意义的... –

+1

我已经添加了更多的解释。希望它有道理。 – seancarmody