尝试使用用户定义的函数填充数据框中的新列。出了什么问题?

时间:2011-10-17 22:11:31

标签: r dataframe subset replace

超短版:我正在尝试使用用户定义的函数使用以下命令填充数据框中的新列:

TestDF$ELN<-EmployeeLocationNumber(TestDF$Location)

但是,当我运行命令时,它似乎只是将EmployeeLocationNumber应用于第一行的Location值,而不是使用每一行的值来单独确定该行的新列值。

请注意:我正在尝试理解R,而不仅仅是执行此特定任务。我实际上能够使用Apply()函数获取我正在寻找的输出,但这无关紧要。我的理解是上面的行应该逐行进行,但事实并非如此。

以下是测试的具体内容:

TestDF<-data.frame(Employee=c(1,1,1,1,2,2,3,3,3), 
                   Month=c(1,5,6,11,4,10,1,5,10), 
                   Location=c(1,5,6,7,10,3,4,2,8))

此testDF会跟踪多个地点的3名员工在一年中的每个位置。

(您可以将“位置”视为每个员工的唯一ID ...它实际上是该行的唯一ID。)

EmployeeLocationNumber函数获取一个位置并输出一个数字,表示员工访问该位置的顺序。例如EmployeeLocationNumber(8) = 2,因为它是访问它的员工访问的第二个位置。

EmployeeLocationNumber <- function(Site){
  CurrentEmployee <- subset(TestDF,Location==Site,select=Employee, drop = TRUE)[[1]]
  LocationDate<- subset(TestDF,Location==Site,select=Month, drop = TRUE)[[1]]
  LocationNumber <- length(subset(TestDF,Employee==CurrentEmployee & Month<=LocationDate,select=Month)[[1]])
  return(LocationNumber)
}

我意识到我可能已将所有这些打包到一个子集命令中,但我不知道在其他子集命令中使用子集命令时引用是如何工作的。

所以,请记住,我真的想要了解如何在R中工作,我有几个问题:

  1. 为什么TestDF$ELN<-EmployeeLocationNumber(TestDF$Location)不会像其他作业语句一样逐行工作?

  2. 是否有更简单的方法根据另一个值的值引用数据框中的特定值?也许一个不返回数据框/列表然后必须展平并从中提取?

  3. 我确定我正在使用的功能是可笑的非R-like ...我应该做些什么来基本上模拟INNER Join类型查询?

4 个答案:

答案 0 :(得分:2)

A)TestDF$Location是一个向量。你的函数没有设置为返回一个向量,所以给它一个向量可能会失败。

B)在什么意义上是位置:8“访问过的第二个位置”?

C)如果你想要在团体订购中,那么你需要将员工分成的数据框传递给计算结果的函数。

D)data.frame的条件访问通常涉及逻辑索引和/或使用()

如果您只想要员工的访问顺序,请尝试以下方法: (将第一个参数更改为Month,因为这决定了位置的顺序)

 with(TestDF, ave(Location, Employee, FUN=seq))
[1] 1 2 3 4 2 1 2 1 3
 TestDF$LocOrder <-  with(TestDF, ave(Month, Employee, FUN=seq))

如果你想要EE:3的第二个位置,那就是:

subset(TestDF, LocOrder==2 & Employee==3, select= Location)
#   Location
# 8        2

答案 1 :(得分:2)

使用逻辑索引,您的函数的简化单行替换是:

EmployeeLocationNumber <- function(Site){
    with(TestDF[do.call(order, TestDF), ], which(Location[Employee==Employee[which(Location==Site)]] == Site))
}

当然这不是最易读的方式,但它在R中演示了逻辑索引和which()的原则。然后,就像其他人所说的那样,只需用向量化的* ply函数进行包装即可这在您的数据集中。

答案 2 :(得分:1)

您的EmployeeLocationNumber函数接受一个向量并返回单个值。 因此,创建新data.frame列的分配只获得一个值:

EmployeeLocationNumber(TestDF$Location) # returns 1

TestDF$ELN<-1 # Creates a new column with the single value 1 everywhere
  1. 作业没有做那样的魔术。它需要一个值并把它放在某个地方。在这种情况下,值1。如果值是一个与行数相同长度的向量,它将按您的意愿工作。
  2. 我会回复你:)
  3. DITO。
  4. 更新:我终于制定了一些代码来实现它,但到那时@DWin有一个更好的解决方案:(

    TestDF$ELN <- unlist(lapply(split(TestDF, TestDF$Employee), function(x) rank(x$Month)))
    

    ...我想ave函数的功能与上面的代码完全相同。但是为了记录:

    首先我split将data.frame分成子框架,每个员工一个。然后我rank几个月(以防万一你的月份不合适)。您也可以使用order,但rank可以更好地处理关系。最后,我将所有结果合并到一个向量中,并将其放入新列ELN

    再次更新关于问题2,“在数据框中引用值的最佳方法是什么?”:

    这取决于具体问题,但是如果你有一个值,比如Employee=3并且想要找到data.frame中与之匹配的所有行,那么只需:

    TestDF$Employee == 3 # Returns logical vector with TRUE for all rows with Employee == 3
    which(TestDF$Employee == 3) # Returns a vector of indices instead
    TestDF[which(TestDF$Employee == 3), ] # Subsets the data.frame on Employee == 3
    

答案 3 :(得分:0)

R(也称为逐行)的向量化特性不是通过使用参数的每个下一个值重复调用函数,而是通过一次传递整个向量并一次对所有向量进行操作。但是在EmployeeLocationNumber中,您只返回一个值,因此该值会重复整个数据集。

另外,EmployeeLocationNumber的示例与您的描述不符。

> EmployeeLocationNumber(8)
[1] 3

现在,以您正在思考的方式(对每个值重复调用)对函数进行矢量化的一种方法是将其传递给Vectorize()

TestDF$ELN<-Vectorize(EmployeeLocationNumber)(TestDF$Location)

给出了

> TestDF
  Employee Month Location ELN
1        1     1        1   1
2        1     5        5   2
3        1     6        6   3
4        1    11        7   4
5        2     4       10   1
6        2    10        3   2
7        3     1        4   1
8        3     5        2   2
9        3    10        8   3

关于你的其他问题,我只想把它写成

TestDF$ELN<-ave(TestDF$Month, TestDF$Employee, FUN=rank)

逻辑是花几个月的时间,分别按雇员的数量查看几个月的小组,并给我几个月的排名顺序(它们按顺序排列)。