R清理和重新排序数据框中的名称/序列号

时间:2015-10-03 01:57:22

标签: r text-processing string-parsing

假设我在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")执行此操作。)
  5. 我的问题是如何在不使用for循环的情况下执行此操作。什么是面向对象的方式来做到这一点?作为这个方向的第一次尝试,我最初通过命令LIST <- strsplit(Data$Name, split = "\n")创建了一个列表,从这里我需要一个for循环来查找名称的排列,这似乎是一个不会扩展的过程根据我的实际数据。此外,一旦我列出LIST列表,我就不确定如何删除NA符号或空格。任何帮助表示赞赏!

2 个答案:

答案 0 :(得分:1)

使用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

答案 1 :(得分:1)

eipi10提供了一个很好的答案。除此之外,我想留下我主要使用data.table的东西。首先,我将两列(即SerialNum and Name)与cSplit()分开,添加了一个带add_rownames()的索引,并按索引拆分数据。在第一个lapply()中,我使用了Stacked()包中的splitstackshape。我堆叠了SerialNum和Name;分开的SeriaNum和Name变为两列,正如您在temp2的一部分中看到的那样。在第二个lapply()中,我使用了data.table包中的合并。然后,我删除了带有NAs(lapply(na.omit))的行,将所有数据表(rbindlist)组合在一起,并按rowname更改了行的顺序,这是原始数据的行号)和{ {1}}(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