R 如何为每行返回一系列列中的第一个非空值?第二个非空值呢?

R 如何为每行返回一系列列中的第一个非空值?第二个非空值呢?,r,hierarchy,hierarchical-data,R,Hierarchy,Hierarchical Data,我有以下组织数据: EmployeeID <- c(10:15) Job.Title <- c("Program Manager", "Development Manager", "Developer" , "Developer", "Developer", "Summer Intern") Level.1 <- c(1,1,1,1,1,1) Level.2 <- c(2,2,2,2,2,2) Level.3 <- c("",10,10,10,10,10) Leve

我有以下组织数据:

EmployeeID <- c(10:15)
Job.Title <- c("Program Manager", "Development Manager", "Developer" , "Developer", "Developer", "Summer Intern")
Level.1 <- c(1,1,1,1,1,1)
Level.2 <- c(2,2,2,2,2,2)
Level.3 <- c("",10,10,10,10,10)
Level.4 <- c("","",11,11,11,11)
Level.5 <- c("","","","","",12)
Level.6 <- c("","","","","","")
Pay.Type <- c("Salary", "Salary", "Salary", "Salary", "Salary", "Hourly")
acme = data.frame(EmployeeID, Job.Title, Level.1, Level.2, Level.3, Level.4, Level.5, Level.6, Pay.Type)

acme

  EmployeeID           Job.Title Level.1 Level.2 Level.3 Level.4 Level.5 Level.6 Pay.Type
1         10     Program Manager       1       2                                   Salary
2         11 Development Manager       1       2      10                           Salary
3         12           Developer       1       2      10      11                   Salary
4         13           Developer       1       2      10      11                   Salary
5         14           Developer       1       2      10      11                   Salary
6         15       Summer Intern       1       2      10      11      12           Hourly

我们可以使用
max.col
来实现这一点。查找“Level”列的索引(“i1”),将基于“i1”的“acme”子集转换为
矩阵(
!=”
),应用
max.col
,并获取
最后一个
真值的列索引,减去1得到第二个最后一个真值(“i3”),使用行/列索引提取元素并创建“主管”和“经理”列

i1 <- grep("Level\\.\\d+", names(acme))
i2 <- max.col(acme[i1]!="", "last")
i3 <- i2-1
acme$Supervisor <- acme[i1][cbind(1:nrow(acme), i2)]
acme$Manager <-  acme[i1][cbind(1:nrow(acme), i3)]
acme
#  EmployeeID           Job.Title Level.1 Level.2 Level.3 Level.4 Level.5 Level.6 Pay.Type Supervisor Manager
#1         10     Program Manager       1       2                                   Salary          2       1
#2         11 Development Manager       1       2      10                           Salary         10       2
#3         12           Developer       1       2      10      11                   Salary         11      10
#4         13           Developer       1       2      10      11                   Salary         11      10
#5         14           Developer       1       2      10      11                   Salary         11      10
#6         15       Summer Intern       1       2      10      11      12           Hourly         12      11

i1我们可以使用
apply
行方式获得所有不为空的索引,并选择第一个和第二个值分别获得两列

acme[, c("Supervisor", "Manager")] <- t(apply(acme[, 8:3], 1, 
                      function(x) c(x[which(x != "")[1]], x[which(x != "")[2]])))

acme

#  EmployeeID           Job.Title Level.1 Level.2 Level.3 Level.4 Level.5 Level.6 Pay.Type Supervisor Manager
#1         10     Program Manager       1       2                                   Salary          2       1
#2         11 Development Manager       1       2      10                           Salary         10       2
#3         12           Developer       1       2      10      11                   Salary         11      10
#4         13           Developer       1       2      10      11                   Salary         11      10
#5         14           Developer       1       2      10      11                   Salary         11      10
#6         15       Summer Intern       1       2      10      11      12           Hourly         12      11
应该有用

如果我们只需要
主管
,我们可以忽略第二部分

acme[, "Supervisor"] <- t(apply(acme[, maxcol:mincol], 1, 
                            function(x) x[which(x != "")[1]]))

acme[,“Supervisor”]这里是一个
数据表
“一行”:

工作原理

  • data.frame
    被强制为
    data.table
  • 它的形状从宽到长的顺序
  • 删除级别为
    的所有行。
  • 现在,数据按级别编号排序(隐式表示为
    级别1
    级别2
    ,等等)
  • 对于每个员工,提取最后一个值(主管)和最后第二个值(经理),创建由三列组成的中间结果
  • 最后,将中间结果连接到
    acme
    以附加新列
  • 印刷品
  • 注意:
    melt()
    将发出警告消息,指出并非所有级别列都具有相同的数据类型。这是由于在
    acme
    data.frame的定义中将整数值与字符(
    “”
    )混合在一起造成的。最好使用
    NA
    而不是
    ”。顺便说一句:在这种情况下,通过使用
    na.rm=FALSE
    melt()


    注意:步骤4中的简单物理排序最多适用于9个级别(
    Level.1
    Level.9
    )。如果有更多级别,则必须提取级别编号并强制为整数。

    一种解决方案,使用
    dplyr
    tidyr
    依赖于数据的重塑

    library(tidyverse)
    acme %>%
      gather('level', 'value', starts_with('Level.')) %>%
      group_by(EmployeeID) %>%
      filter(value != '') %>%
      summarise(Supervisor = last(value),
                Manager = nth(value, -2)) %>%
      left_join(acme)
    

    R有NA值。使用空字符串比使用空字符串要好得多。我的数据帧要大得多,并且是更大代码集的一部分,因此,如果以后添加或删除其他列,则按顺序号引用列可能会导致错误。我应该为这项任务创建一个单独的数据框并将其合并回去,还是有办法编辑“apply(acme[,8:3]”以使用列名?我尝试了:acme[,c(“主管”,“经理”)]另外,我如何只返回主管?@Ankie我已经更新了答案。如果您还有疑问,请告诉我。
    acme[, "Supervisor"] <- t(apply(acme[, maxcol:mincol], 1, 
                                function(x) x[which(x != "")[1]]))
    
    library(data.table)
    setDT(acme)[melt(acme, measure.vars = patterns("Level.\\d"))[value != ""][
      order(variable), .(Supervisor = value[.N], Manager = value[.N - 1]), by = EmployeeID], 
      on = "EmployeeID"][]
    
       EmployeeID           Job.Title Level.1 Level.2 Level.3 Level.4 Level.5 Level.6 Pay.Type Supervisor
    #1:         10     Program Manager       1       2                                   Salary          2
    #2:         11 Development Manager       1       2      10                           Salary         10
    #3:         12           Developer       1       2      10      11                   Salary         11
    #4:         13           Developer       1       2      10      11                   Salary         11
    #5:         14           Developer       1       2      10      11                   Salary         11
    #6:         15       Summer Intern       1       2      10      11      12           Hourly         12
       Manager
    #1:       1
    #2:       2
    #3:      10
    #4:      10
    #5:      10
    #6:      11
    
    library(tidyverse)
    acme %>%
      gather('level', 'value', starts_with('Level.')) %>%
      group_by(EmployeeID) %>%
      filter(value != '') %>%
      summarise(Supervisor = last(value),
                Manager = nth(value, -2)) %>%
      left_join(acme)