2

我有一个可变的人员列表,作为数据框中的一长行,我有兴趣将这些记录重新组织成更有意义的格式。

我的原始数据看起来像这样,

df <- data.frame(name1 = "John Doe", email1 = "John@Doe.com", phone1 = "(444) 444-4444", name2 = "Jane Doe", email2 = "Jane@Doe.com", phone2 = "(444) 444-4445", name3 = "John Smith", email3 = "John@Smith.com", phone3 = "(444) 444-4446", name4 = NA, email4 = "Jane@Smith.com", phone4 = NA, name5 = NA, email5 = NA, phone5 = NA)
df
#     name1       email1         phone1    name2       email2         phone2
# 1 John Doe John@Doe.com (444) 444-4444 Jane Doe Jane@Doe.com (444) 444-4445
#       name3         email3         phone3 name4         email4 phone4 name5
# 1 John Smith John@Smith.com (444) 444-4446    NA Jane@Smith.com     NA    NA
#  email5 phone5
# 1     NA     NA    

我正在尝试将其弯曲成这样的格式,

df_transform <- structure(list(name = structure(c(2L, 1L, 3L, NA, NA), .Label = c("Jane Doe", 
"John Doe", "John Smith"), class = "factor"), email = structure(c(3L, 
1L, 4L, 2L, NA), .Label = c("Jane@Doe.com", "Jane@Smith.com", 
"John@Doe.com", "John@Smith.com"), class = "factor"), phone = structure(c(1L, 
2L, 3L, NA, NA), .Label = c("(444) 444-4444", "(444) 444-4445", 
"(444) 444-4446"), class = "factor")), .Names = c("name", "email", 
"phone"), class = "data.frame", row.names = c(NA, -5L))
 df_transform
#         name          email          phone
# 1   John Doe   John@Doe.com (444) 444-4444
# 2   Jane Doe   Jane@Doe.com (444) 444-4445
# 3 John Smith John@Smith.com (444) 444-4446
# 4       <NA> Jane@Smith.com           <NA>
# 5       <NA>           <NA>           <NA>

应该补充一点,它并不总是五个记录,它可以是 1 到 99 之间的任何数字。我尝试使用reshape2'smelt和 `t()1 但它变得复杂了。我想有一些我根本不知道的已知方法。

4

2 回答 2

3

你在正确的轨道上,试试这个:

library(reshape2)

# melt it down
df.melted = melt(t(df))
# get rid of the numbers at the end
df.melted$Var1 = sub('[0-9]+$', '', df.melted$Var1)

# cast it back
dcast(df.melted, (seq_len(nrow(df.melted)) - 1) %/% 3 ~ Var1)[,-1]
#           email       name          phone
#1   John@Doe.com   John Doe (444) 444-4444
#2   Jane@Doe.com   Jane Doe (444) 444-4445
#3 John@Smith.com John Smith (444) 444-4446
#4 Jane@Smith.com       <NA>           <NA>
#5           <NA>       <NA>           <NA>
于 2013-05-24T16:29:13.577 回答
2

1) reshape()首先我们从列名中去掉数字,给出减少的列名,names0. 然后我们将列分成产生的组g(其中具有对应于emailnamephone列组的三个组件)。然后使用reshape(从 R 的基础)执行从宽到长的转换,并从生成的长数据框中选择所需的列,以排除由 自动添加的列reshape。该选择向量 ,unique(names0)使得它以所需的方式对结果列进行重新排序。

names0 <- sub("\\d+$", "", names(df))
g <- split(names(df), names0)
reshape(df, dir = "long", varying = g, v.names = names(g))[unique(names0)]

最后一行给出了这个:

          name          email          phone
1.1   John Doe   John@Doe.com (444) 444-4444
1.2   Jane Doe   Jane@Doe.com (444) 444-4445
1.3 John Smith John@Smith.com (444) 444-4446
1.4       <NA> Jane@Smith.com           <NA>
1.5       <NA>           <NA>           <NA>

2) reshape2 包这里是一个使用reshape2 的解决方案。我们将一rowname列添加dfmelt长格式。然后我们将variable列拆分为名称部分 ( name, email, phone) 和我们称之为的数字后缀部分iddcast最后,我们像以前一样使用并选择适当的列将其转换回宽格式。

library(reshape2)

m <- melt(data.frame(rowname = 1:nrow(df), df), id = 1)
mt <- transform(m, 
   variable = sub("\\d+$", "", variable), 
   id = sub("^\\D+", "", variable)
)
dcast(mt, rowname + id ~ variable)[, unique(mt$variable)]

最后一行给出了这个:

        name          email          phone
1   John Doe   John@Doe.com (444) 444-4444
2   Jane Doe   Jane@Doe.com (444) 444-4445
3 John Smith John@Smith.com (444) 444-4446
4       <NA> Jane@Smith.com           <NA>
5       <NA>           <NA>           <NA>

3)简单的矩阵重塑。从列名中删除数字后缀并设置cn为唯一的剩余名称。(cn代表列名)。然后我们只是将行重塑df为一个 nx 长度(cn)矩阵添加列名。

cn <- unique(sub("\\d+$", "", names(df)))
matrix(as.matrix(df), nc = length(cn), byrow = TRUE, dimnames = list(NULL, cn))

     name         email            phone           
[1,] "John Doe"   "John@Doe.com"   "(444) 444-4444"
[2,] "Jane Doe"   "Jane@Doe.com"   "(444) 444-4445"
[3,] "John Smith" "John@Smith.com" "(444) 444-4446"
[4,] NA           "Jane@Smith.com" NA              
[5,] NA           NA               NA    

4) tapply这个问题也可以用一个简单的tapply. 和以前一样names0是没有数字后缀的列名。 names.suffix只是后缀。现在使用tapply

names0 <- sub("\\d+$", "", names(df))
names.suffix <- sub("^\\D+", "", names(df))
tapply(as.matrix(df), list(names.suffix, names0), c)[, unique(names0)]

最后一行给出:

  name         email            phone           
1 "John Doe"   "John@Doe.com"   "(444) 444-4444"
2 "Jane Doe"   "Jane@Doe.com"   "(444) 444-4445"
3 "John Smith" "John@Smith.com" "(444) 444-4446"
4 NA           "Jane@Smith.com" NA              
5 NA           NA               NA 
于 2013-05-24T17:25:33.543 回答