在R中扩展csv数据

我有以下列标题的数据:

Sheet Number, Year, Term, Class, Debate #, Role in Debate, Gender of Evaluator, Person #1 Clarity, Person #1 Effort, Person #1 Gender, Person #1 Origin, Debater Number, Person #2 Clarity, Person #2 Effort, Person #2 Gender, Person #2 Origin, Debater Number, Person #3 Clarity, Person #3 Effort, Person #3 Gender, Person #3 Origin, Debater Number, Person #4 Clarity, Person #4 Effort, Person #4 Gender, Person #4 Origin, Debater Number, Person #5 Clarity, Person #5 Effort, Person #5 Gender, Person #5 Origin, Debater Number, Person #6 Clarity, Person #6 Effort, Person #6 Gender, Person #6 Origin, Debater Number, Person #7 Clarity, Person #7 Effort, Person #7 Gender, Person #7 Origin, Debater Number, Person #8 Clarity, Person #8 Effort, Person #8 Gender, Person #8 Origin, Debater Number, Learned from Team 1, Learned from Team 2, Who won?, Sheet all 10s?, Evaluator Class Year 

我想将其转换为具有以下列标题的格式:

 Sheet Number, Year, Term, Class, Debate #, Gender of Evaluator, Evaluator Class Year, Role in Debate, Debate Team Member #, Debater Number, Gender of Debate Team Member, Origin of Debate Team Member, Clarity of Debate Team Member, Effort of Debate Team Member, Learned from Team 1, Learned from Team 2, Who won?, Sheet all 10s?, =1 if Gender of Evaluator==Gender of Debater 

两者之间的主要区别在于,在第一种格式中,每个表格编号具有5-8个与其相关联的编号“人物”。 第二种格式,每个图纸编号都有一个与之关联的人员(因此每个图纸编号出现多次,数据“展开”)。

我怎么能在R中完成这个? 我一直在尝试使用'重塑'包。 谢谢!

(我借此机会了解一下tidyr ,我很高兴我做到了。)

正如@JamesKing所build议的,你提供的不是最好的MWE,所以我创build了一些结构相似的数据。 不过,我认为这一切都适用于你的例子,所以有一些解释,你应该能够把它翻译成你的数据。 话虽如此,由于看起来您是从Excel电子表格开始的,因此提出一个简化gatherseparate数据的命名约定是有益的。

我的资料:

 set.seed(1) n <- 5 dat <- data.frame( sheetNum = 1:n, year = sample(2000:2025, size = n), roleInDebate = sample(letters, size = n, replace = TRUE), Clarity.1 = sample(10, size = n, replace = TRUE), Effort.1 = sample(10, size = n, replace = TRUE), Clarity.2 = sample(10, size = n, replace = TRUE), Effort.2 = sample(10, size = n, replace = TRUE), Clarity.3 = sample(10, size = n, replace = TRUE), Effort.3 = sample(10, size = n, replace = TRUE)) dat # sheetNum year roleInDebate Clarity.1 Effort.1 Clarity.2 Effort.2 Clarity.3 # 1 1 2006 x 3 5 10 4 5 # 2 2 2009 y 2 8 3 1 6 # 3 3 2013 r 7 10 7 4 5 # 4 4 2020 q 4 4 2 9 2 # 5 5 2004 b 8 8 3 4 9 

数据types:

  • 静态列: sheetNumyearroleInDebate 。 这些数据不会在其他地方使用,并将被复制到每个人的每一行。 根据这些列gatherseparatespread任何内容。

  • 其余的是数据embedded在列名称中的列。 我的意思是,“ Clarity.11Clarity.1数据1 ,它需要巧妙地分离出来。 虽然我每人只有两列,但这很容易转化为更多。

底线,前面

(如果你不熟悉dplyrmagrittr%>% infix运算符,我鼓励你在其他地方研究它,这对于理解这个build议的解决scheme是非常方便和关键的。)

现在解决scheme很简单:

 library(tidyr) library(dplyr) dat %>% gather(var, val, -sheetNum, -year, -roleInDebate) %>% separate(var, c('skill', 'person'), '\\.') %>% spread(skill, val) # sheetNum year roleInDebate person Clarity Effort # 1 1 2006 x 1 3 5 # 2 1 2006 x 2 10 4 # 3 1 2006 x 3 5 7 # 4 2 2009 y 1 2 8 # 5 2 2009 y 2 3 1 # 6 2 2009 y 3 6 8 # 7 3 2013 r 1 7 10 # 8 3 2013 r 2 7 4 # 9 3 2013 r 3 5 2 # 10 4 2020 q 1 4 4 # 11 4 2020 q 2 2 9 # 12 4 2020 q 3 2 8 # 13 5 2004 b 1 8 8 # 14 5 2004 b 2 3 4 # 15 5 2004 b 3 9 5 

打破它

要看看发生了什么,让我们一步一步通过这个。 gather步骤简单地将未提及的列组合到列的键/值对中,如下所示:

 dat %>% gather(var, val, -sheetNum, -year, -roleInDebate) %>% head() # sheetNum year roleInDebate var val # 1 1 2006 x Clarity.1 3 # 2 2 2009 y Clarity.1 2 # 3 3 2013 r Clarity.1 7 # 4 4 2020 q Clarity.1 4 # 5 5 2004 b Clarity.1 8 # 6 1 2006 x Effort.1 5 

请注意,我所包含的列的前缀是如何保持逐字的。 接下来,我们需要分离(或separatevar列:

 dat %>% gather(var, val, -sheetNum, -year, -roleInDebate) %>% separate(var, c('skill', 'person'), '\\.') %>% head() # sheetNum year roleInDebate skill person val # 1 1 2006 x Clarity 1 3 # 2 2 2009 y Clarity 1 2 # 3 3 2013 r Clarity 1 7 # 4 4 2020 q Clarity 1 4 # 5 5 2004 b Clarity 1 8 # 6 1 2006 x Effort 1 5 

这里没有太多的事情发生,但是对于下一个步骤来说非常重要:扩大数据,或者从一个不同的键/值对列(现在使用skillval )中spread ,创build一个名为ClarityEffort新列,我们在上面的解决scheme中看到。

希望这可以帮助。

顺便说一句:对于一个好的MWE,通常build议提供来自dput(dat)的输出,其中dat是一个而有代表性的数据结构,有助于我们理解起点和预期输出。 在这里,两个小数据data.frame都是合适的。

假设@ r2evans的样本数据有点代表你的问题,这里有一些其他的select需要考虑。


选项1 :Base R的reshape

奇怪的是,在您描述的任务之后恰恰命名的function – reshape()在重塑工具方面似乎是“黑羊”。 如果它是你的常规工具包的一部分,但并不是很难得到它的挂钩。

对于这个问题,一个方法可能是:

 reshape(dat, direction = "long", idvar = 1:3, varying = 4:ncol(dat), sep = ".") 

我对reshape()关注不是它的语法,但是(1)它不能处理不均衡的广泛到长期的转换(例如,如果你有3个“Clarity”列但只有2个“Effort”列)和2)当你开始处理大量的行或者许多要重构的列时,可能会非常缓慢。 就这样,我写了merged.stack


选项2merged.stack

我写了merged.stack作为我的“splitstackshape”包的一部分来处理类似的重塑任务,以reshape(., direction = "long", ...)将会做的事情(例如,不同于“重塑” “/”reshape2“(随后从”tidyr“ gather ))。 我也想通过识别variables“桩”(在这种情况下,“清晰”和“努力”)来简化select感兴趣variables的过程。

如前所述, merged.stack也被devise得很快。

 library(splitstackshape) merged.stack(dat, var.stubs = c("Clarity", "Effort"), sep = ".") # sheetNum year roleInDebate .time_1 Clarity Effort # 1: 1 2006 x 1 3 5 # 2: 1 2006 x 2 10 4 # 3: 1 2006 x 3 5 7 # 4: 2 2009 y 1 2 8 # 5: 2 2009 y 2 3 1 # 6: 2 2009 y 3 6 8 # 7: 3 2013 r 1 7 10 # 8: 3 2013 r 2 7 4 # 9: 3 2013 r 3 5 2 # 10: 4 2020 q 1 4 4 # 11: 4 2020 q 2 2 9 # 12: 4 2020 q 3 2 8 # 13: 5 2004 b 1 8 8 # 14: 5 2004 b 2 3 4 # 15: 5 2004 b 3 9 5 

选项3 :等待从“data.table”版本1.9.8 melt

好。 那么,这可能不是一个真正的select(但“data.table”开发人员工作很快,所以谁知道),但在“data.table”版本1.9.8中,您将能够melt指定列的列表。 看到这个问题的更多细节 。 或者,如果您更冒险,请安装1.9.8分支并立即尝试:-)

最终,这可能会导致merged.stack冗余,因为它将具有类似的function,但速度更快(从我已经完成的几次试验中可以看出)。


更新 – 基准

我刚刚testing过merged.stack和@ r2evan的方法。 我没有testingreshape()因为我害怕它会减慢我的系统爬行。 我没有从“data.table”中testingmelt ,因为等待产品发布会更好。

以下是一些示例数据:

 set.seed(1) n <- 100000 r <- 6 dat <- data.frame( sheetNum = 1:n, year = sample(2000:2025, size = n, TRUE), roleInDebate = sample(letters, size = n, replace = TRUE), matrix(sample(10, n * r * 2, TRUE), nrow = n, dimnames = list(NULL, paste(c("Clarity", "Effort"), rep(seq_len(r), each = 2), sep = ".")))) 

以下是testing的两个function:

 r2evans <- function() { dat %>% gather(var, val, -sheetNum, -year, -roleInDebate) %>% separate(var, c('skill', 'person'), '\\.') %>% spread(skill, val) } ananda <- function() { merged.stack(dat, var.stubs = c("Clarity", "Effort"), sep = ".") } 

以下是10次运行的结果:

 library(microbenchmark) microbenchmark(r2evans(), ananda(), times = 10) # Unit: milliseconds # expr min lq mean median uq max neval # r2evans() 3514.0961 3603.7102 3839.6097 3713.6705 3959.5320 4380.4601 10 # ananda() 320.5602 336.2396 363.7165 367.3344 386.3064 417.7994 10 

和一些validation,输出是一样的:

 out1 <- r2evans() out2 <- ananda() library(compare) compare(out1, out2, allowAll = TRUE) # TRUE # renamed # dropped names # dropped attributes