【问题标题】:transform one long row in data-frame to individual records将数据框中的一长行转换为单个记录
【发布时间】:2013-05-20 07:32:09
【问题描述】:

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

我的原始数据是这样的,

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 之间的任何数字。我尝试使用 reshape2melt 和 `t()1 但它变得复杂了。我想有一些我根本不知道的已知方法。

【问题讨论】:

    标签: r dataframe transform


    【解决方案1】:

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

    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>
    

    【讨论】:

    • 谢谢,感谢您使用我提到的工具。
    【解决方案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 列拆分为名称部分(nameemailphone)和我们称之为id 的数字后缀部分。最后,我们使用dcast 将其转换回宽格式,并像之前一样选择适当的列。

    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 行重塑为一个 n x 长度(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 来解决。和以前一样,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 
    

    【讨论】:

    • 谢谢,我喜欢你的回答,而且你只使用 base-R
    • reshape() 的可变参数似乎比相应的 reshape2 解决方案更适合该问题,但矩阵重塑和 tapply 也已添加的解决方案可能是其中最简单的解决方案。跨度>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-03-19
    • 1970-01-01
    • 2021-09-28
    • 2021-06-18
    • 1970-01-01
    • 2020-02-13
    • 1970-01-01
    相关资源
    最近更新 更多