使用 R 中重复的个人条目重组团队数据

Restructuring team data with duplicate individual entries in R

我当前的数据集是这样的:

Person  Team1     Team2     Team3   Team4   Team5
  1      10         11       NA      NA      NA  
  2      12         13       14      NA      NA
  3      15         16       NA      NA      NA
  1      11         14       NA      NA      NA

如您所见,有些人(即第 1 个人)在整个数据集中重复。给定人员的重复条目可能会提供有关团队隶属关系的新信息,也可能会重复旧信息。我想做的是创建一个数据集,其中每个人只占一行,并且他们的团队成员信息都包含在该行中,而不是多余的。例如:

Person  Team1   Team2   Team3   Team4   Team5
  1      10      11      14      NA      NA
  2      12      13      14      NA      NA
  3      15      16      NA      NA      NA

因此,即使第 1 个人在数据集中两次被列为团队 11,但它在最终版本中只出现了一次。仅供参考:在我的实际数据集中,团队变量一直到 16,而不是停在 5。

我不是很精通 R,所以这段代码可能草率,但我认为你最好的选择是创建一个包含团队成员列表的新列,例如:

data$teams = with(data, c(Team1, Team2, Team3, Team4[...]))

其中 [...] 是团队的其余部分。从那里,您可能可以聚合列,使用 Person 作为键,并做一些 unlisting/uniqueing 来消除重复项:

byperson = aggregate(data, by=list(data$Person), FUN=list)
byperson$teams = sapply(sapply(byperson$teams, unlist), unique)

它按人汇总列表,将重复的行(我在第一位中制作的列表)与 list 函数组合在一起,从而生成列表列表。然后第二行只是遍历 byperson$teams 中的每个列表列表,首先对它们运行 unlist 以将它们放入一个平面列表中,然后 unique 以摆脱欺骗。

可能有更好更优雅的方法来做到这一点,也许使用 melt 库,但这是您必须要做的基本想法 - 将 16 列重新格式化为列表的任一列,或将其扩展成行,每个 "membership" 根据我的经验,后一种格式是 R 倾向于 "prefer" 的格式,如下所示:

Person Team
     1   10
     1   11
     1   14
     2   12
     2   13
     2   14
     3   15
     3   16

但我不确定如何从您的数据中获取信息。列表列表可能是可行的,具体取决于您尝试对数据执行的操作,但是在您的示例中构建的数据将很难在 R 中执行任何有用的操作。

还有一种可能。将有优雅的方法来做到这一点。但这会给你你要求的结果。首先,我按 Person 拆分数据并为每个人创建一个向量。其次,我搜索了每个向量中的唯一元素并确定了长度 length(colnames(mydf))-1 。第三,我将列表转换为数据框。最后,我为 Person 添加了一列并更改了列名。

ana <- lapply(split(mydf, mydf$Person), function(x) c(as.matrix(x[,-1])))
bob <- lapply(lapply(ana, unique), function(y) y[1:length(colnames(mydf))-1])
cathy <- data.frame(do.call(rbind, bob))
dan <- cbind(unique(mydf$Person), cathy)
names(dan) <- names(mydf)

#  Person Team1 Team2 Team3 Team4 Team5
#1      1    10    11    14    NA    NA
#2      2    12    13    14    NA    NA
#3      3    15    16    NA    NA    NA

数据

mydf <- structure(list(Person = c(1L, 2L, 3L, 1L), Team1 = c(10L, 12L, 
15L, 11L), Team2 = c(11L, 13L, 16L, 14L), Team3 = c(NA, 14L, 
NA, NA), Team4 = c(NA, NA, NA, NA), Team5 = c(NA, NA, NA, NA)), .Names = c("Person", 
"Team1", "Team2", "Team3", "Team4", "Team5"), class = "data.frame", row.names = c(NA, 
-4L))

这是一个基于 reshape2

中的 cast/melt 函数的解决方案
library(reshape2)

# Make the data tidy
d.melt <- melt(mydf,id.vars = 'Person')

# Remove duplicates
d.uniq <- d.melt[!duplicated(d.melt[,c('Person','value')]),]

# renumber the teams
d.uniq$team <- ave(d.uniq$Person,d.uniq$Person,FUN=function(i) paste0('Team',seq_along(i)))

# cast into the desired 'wide' format
d.result <- dcast(d3,Person~team,value.var = 'value')

结果是:

#   Person Team1 Team2 Team3 Team4
# 1      1    10    11    14    NA
# 2      2    12    13    14    NA
# 3      3    15    16    NA    NA

如果结果必须有一定数量的列:

all.teams <- paste0('Team',1:16)
d.result[,all.teams[!all.teams %in% colnames(d.result)]]=NA

你可以试试 data.table。通过setDT将"data.frame"转换为"data.table",将"wide"转换为"long"形式,得到"Team"的unique值按 "Person"、(unique(unlist(.SD))) 的列,为每个 "Person" 创建序列列 ("V2"),然后按 [=16= 将其重塑回 "wide" ]

library(data.table)
dt1 <- dcast.data.table(setDT(df)[, unique(unlist(.SD)) ,
          by=Person][, V2:=paste0('Team', 1:.N), by=Person],
                                Person~V2, value.var='V1')
dt1 
 #  Person Team1 Team2 Team3 Team4
 #1:      1    10    11    14    NA
 #2:      2    12    13    14    NA
 #3:      3    15    16    NA    NA

如果需要 "Team5"、"Team6" 等,创建一个 "Team" 名称的向量,使用 setdiff,然后将结果向量分配给 NA

indx <- setdiff(paste0('Team', 1:5), colnames(dt1))
dt1[,(indx):=NA]
dt1
#   Person Team1 Team2 Team3 Team4 Team5
#1:      1    10    11    14    NA    NA
#2:      2    12    13    14    NA    NA
#3:      3    15    16    NA    NA    NA

使用 dplyrtidyr 包的另一种方式。

x <- read.table(text = "Person  Team1     Team2     Team3   Team4   Team5
  1      10         11       NA      NA      NA  
  2      12         13       14      NA      NA
  3      15         16       NA      NA      NA
  1      11         14       NA      NA      NA", header = TRUE)

library(dplyr)
library(tidyr)

x %>%
gather(meaningless_column, Team, -Person) %>%
select(-meaningless_column) %>%
filter(!is.na(Team)) %>%
distinct %>%
arrange(Person, Team) %>%
group_by(Person) %>%
mutate(Index = paste0("Team_", seq_along(Team))) %>%
ungroup %>%
spread(Index, Team)

结果:

  Person Team_1 Team_2 Team_3
1      1     10     11     14
2      2     12     13     14
3      3     15     16     NA

它将根据需要创建尽可能多的 Team_n 列。