R 清理和重新排序数据框中的 names/serial 个数字

R Cleaning and reordering names/serial numbers in data frame

假设我在 R 中有如下数据框:

 Data <- data.frame("SerialNum" = character(), "Year" = integer(), "Name" = character(), stringsAsFactors = F)
 Data[1,] <- c("983\n837\n424\n ", 2015, "Michael\nLewis\nPaul\n ")
 Data[2,] <- c("123\n456\n789\n136", 2014, "Elaine\nJerry\nGeorge\nKramer")
 Data[3,] <- c("987\n654\n321\n975\n ", 2010, "John\nPaul\nGeorge\nRingo\nNA")
 Data[4,] <- c("424\n983\n837", 2015, "Paul\nMichael\nLewis")
 Data[5,] <- c("456\n789\n123\n136", 2014, "Jerry\nGeorge\nElaine\nKramer")

我想做的是:

  1. 拆分每个名称字符串和每个序列号字符串,使它们成为自己的向量(或字符串向量列表)。
  2. 消除任一向量集中的任何字符 "NA""...\n " 表示的任何空格。
  3. 按字母顺序重新排列每个名称列表,并根据相同的排列重新排列相应的序列号。
  4. 以与原来相同的方式连接每个向量(我通常用 paste(., collapse = "\n") 这样做)。

我的问题是如何在不使用 for 循环的情况下执行此操作。什么是面向对象的方法来做到这一点?作为朝这个方向的第一次尝试,我最初通过命令 LIST <- strsplit(Data$Name, split = "\n") 制作了一个列表,从这里我需要一个 for 循环来找到名称的排列,这似乎是一个无法扩展的过程我的实际数据。此外,在列出 LIST 列表后,我不确定如何删除 NA 符号或空格。感谢您的帮助!

使用 lapply 我获取数据框的每一行并将其转换为一个新的数据框,每行一个名称。这将创建一个包含 5 个数据框的列表,原始数据框的每一行对应一个。

 seinfeld = lapply(1:nrow(Data), function(i) {

   # Turn strings into data frame with one name per row
   dat = data.frame(SerialNum=unlist(strsplit(Data[i,"SerialNum"], split="\n")), 
              Year=Data[i,"Year"],
              Name=unlist(strsplit(Data[i,"Name"], split="\n")))

   # Get rid of empty strings and NA values
   dat = dat[!(dat$Name %in% c(""," ","NA")), ]

   # Order alphabetically
   dat = dat[order(dat$Name), ]
 })

更新: 根据您的评论,如果这是您想要达到的结果,请告诉我:

seinfeld = lapply(1:nrow(Data), function(i) {

  # Turn strings into data frame with one name per row
  dat = data.frame(SerialNum=unlist(strsplit(Data[i,"SerialNum"], split="\n")), 
                   Name=unlist(strsplit(Data[i,"Name"], split="\n")))

  # Get rid of empty strings and NA values
  dat = dat[!(dat$Name %in% c(""," ","NA")), ]

  # Order alphabetically
  dat = dat[order(dat$Name), ]

  # Collapse back into a single row with the new sort order
  dat = data.frame(SerialNum=paste(dat[, "SerialNum"], collapse="\n"),
                   Year=Data[i, "Year"],
                   Name=paste(dat[, "Name"], collapse="\n"))

})

do.call(rbind, seinfeld)

           SerialNum Year                          Name
1      837\n983\n424 2015          Lewis\nMichael\nPaul
2 123\n789\n456\n136 2014 Elaine\nGeorge\nJerry\nKramer
3 321\n987\n654\n975 2010     George\nJohn\nPaul\nRingo
4      837\n983\n424 2015          Lewis\nMichael\nPaul
5 123\n789\n456\n136 2014 Elaine\nGeorge\nJerry\nKramer

eipi10 给出了很好的答案。除此之外,我想把我主要用 data.table 尝试过的东西留下来。首先,我用 cSplit() 拆分两列(即 SerialNum and Name),用 add_rownames() 添加索引,然后按索引拆分数据。在第一个 lapply() 中,我使用了 splitstackshape 包中的 Stacked()。我堆叠了 SerialNum 和 Name;分离的 SeriaNum 和 Name 成为两列,正如您在 temp2 的一部分中看到的那样。在第二个 lapply() 中,我使用了 data.table 包中的合并。然后,我删除了带有 NAs (lapply(na.omit)) 的行,合并了所有数据表 (rbindlist),并将行的顺序更改为 rowname,这是原始数据的行号) 和 Name (setorder(rowname, Name))

library(data.table)
library(splitstackshape)
library(dplyr)

cSplit(mydf, c("SerialNum", "Name"), direction = "wide",
       type.convert = FALSE, sep = "\n") %>%
add_rownames %>%
split(f = .$rowname) -> temp

#a part of temp
#$`1`
#Source: local data frame [1 x 12]
#
#rowname  Year SerialNum_1 SerialNum_2 SerialNum_3 SerialNum_4 SerialNum_5  Name_1 Name_2
#(chr) (dbl)       (chr)       (chr)       (chr)       (chr)       (chr)   (chr)  (chr)
#1       1  2015         983         837         424          NA          NA Michael  Lewis
#Variables not shown: Name_3 (chr), Name_4 (chr), Name_5 (chr)


lapply(temp, function(x){

    Stacked(x, var.stubs = c("SerialNum", "Name"), sep = "_")

}) -> temp2

# A part of temp2
#$`1`
#$`1`$SerialNum
#   rowname Year .time_1 SerialNum
#1:       1 2015       1       983
#2:       1 2015       2       837
#3:       1 2015       3       424
#4:       1 2015       4        NA
#5:       1 2015       5        NA
#
#$`1`$Name
#   rowname Year .time_1    Name
#1:       1 2015       1 Michael
#2:       1 2015       2   Lewis
#3:       1 2015       3    Paul
#4:       1 2015       4      NA
#5:       1 2015       5      NA

lapply(1:nrow(mydf), function(x){

    merge(temp2[[x]]$SerialNum, temp2[[x]]$Name, by = c("rowname", "Year", ".time_1"))

}) %>%

lapply(na.omit) %>%
rbindlist %>%
setorder(rowname, Name) -> out

print(out)

 #    rowname Year .time_1 SerialNum    Name
 # 1:       1 2015       2       837   Lewis
 # 2:       1 2015       1       983 Michael
 # 3:       1 2015       3       424    Paul
 # 4:       2 2014       1       123  Elaine
 # 5:       2 2014       3       789  George
 # 6:       2 2014       2       456   Jerry
 # 7:       2 2014       4       136  Kramer
 # 8:       3 2010       3       321  George
 # 9:       3 2010       1       987    John
 #10:       3 2010       2       654    Paul
 #11:       3 2010       4       975   Ringo
 #12:       4 2015       3       837   Lewis
 #13:       4 2015       2       983 Michael
 #14:       4 2015       1       424    Paul
 #15:       5 2014       3       123  Elaine
 #16:       5 2014       2       789  George
 #17:       5 2014       1       456   Jerry
 #18:       5 2014       4       136  Kramer

数据

mydf <- structure(list(SerialNum = c("983\n837\n424\n ", "123\n456\n789\n136", 
"987\n654\n321\n975\n ", "424\n983\n837", "456\n789\n123\n136"
), Year = c(2015, 2014, 2010, 2015, 2014), Name = c("Michael\nLewis\nPaul\n ", 
"Elaine\nJerry\nGeorge\nKramer", "John\nPaul\nGeorge\nRingo\nNA", 
"Paul\nMichael\nLewis", "Jerry\nGeorge\nElaine\nKramer")), .Names = c("SerialNum", 
"Year", "Name"), row.names = c(NA, -5L), class = "data.frame")