使用 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
使用 dplyr
和 tidyr
包的另一种方式。
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
列。
我当前的数据集是这样的:
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
使用 dplyr
和 tidyr
包的另一种方式。
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
列。