扩展R中的csv数据

时间:2015-01-03 01:49:35

标签: r excel csv reshape

我的数据包含以下列标题:

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中实现这一目标?我一直在尝试使用'reshape'包。谢谢!

2 个答案:

答案 0 :(得分:5)

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

正如@JamesKing建议的那样,你提供的并不是最好的MWE,所以我创造了一些结构相似的数据。我认为这一切都适用于您的示例,因此,通过一些解释,您应该能够将其转换为您的数据。话虽如此,由于您似乎开始使用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

数据类型:

  • 静态列:sheetNumyearroleInDebate。此数据不会在其他任何地方使用,并会复制到每个人的每一行。根据这些列,gatherseparate d或spread没有任何内容。

  • 其余部分是列中包含数据的列。我的意思是Clarity.1在其中存储了基准1,它需要巧妙地分离出来。虽然我每人只有两列,但这很容易转化为更多。

底线,前方

(如果您不熟悉来自%>%dplyr的{​​{1}}中缀运算符,我建议您在其他地方进行研究。这对于理解这一点非常方便和关键建议的解决方案。)

现在解决方案非常简单:

magrittr

打破它

要了解正在发生的事情,请逐步完成此操作。 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 前面添加的列是如何逐字保存的。接下来,我们需要拆分(或-separate列:

var

这里没有发生太多事情,但它对下一步非常重要:扩大数据,或者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 来自不同的键/值对列(现在使用{{1} }和spread),它创建了名为skillval的新列,正如我们在上面的解决方案中看到的那样。

希望这有帮助。

BTW:对于一个好的MWE,通常建议提供Clarity的输出,其中Effort 但有代表性的数据有助于我们理解起点和预期输出的结构。在这里,两者的dput(dat)都是合适的。

答案 1 :(得分:3)

假设@ r2evans的样本数据在某种程度上代表了您的问题,可以考虑以下几个其他选项。


选项1 :基础R reshape

奇怪的是,在你描述的任务 - reshape()之后精确命名的函数 - 在重塑工具方面似乎是“黑羊”。如果它是你的常规工具包的一部分,那么要解决这个问题并不困难。

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

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

我对reshape()的关注不是它的语法,而是(1)它无法处理不平衡的从长到长的转换(例如,如果你有3个“Clarity”列但只有2个“Effort” ())当你开始处理要重新整形的大量行或大量列时,它可能会非常缓慢。因此,我写了merged.stack


选项2 merged.stack

我写了merged.stack作为我的“splitstackshape”包的一部分来处理类似的重塑任务reshape(., direction = "long", ...)会做什么(例如,与melt来自“重塑”不同“/”reshape2“(以及随后,gather,来自”tidyr“)。我还想通过识别变量“stubs”(在本例中为“Clarity”和“Effort”)来简化选择感兴趣变量的过程。

如前所述,merged.stack也设计得很快。

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

行。好吧,这可能不是一个真正的选择(但“data.table”开发人员工作得很快,所以谁知道)但是在“data.table”版本1.9.8中你可以melt指定列表列。 See this issue for more details。或者,如果您更喜欢冒险,请安装the 1.9.8 branch并立即试用:-)

最终,这可能会使merged.stack变得多余,因为它具有相似的功能,但速度更快(从我在一些试验中看到的情况来看)。


更新 - 基准

我在下面测试了merged.stack和@ r2evan的方法。我没有测试reshape(),因为我害怕它会让我的系统变慢。我没有从“data.table”测试melt因为最好等待生产版本。

以下是一些示例数据:

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 = "."))))

以下是测试的两个功能:

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

有些验证输出是相同的:

out1 <- r2evans()
out2 <- ananda()

library(compare)
compare(out1, out2, allowAll = TRUE)
# TRUE
#   renamed
#   dropped names
#   dropped attributes