繁体   English   中英

如何用“重复发生”的列重塑数据框?

[英]How to reshape a dataframe with “reoccurring” columns?

我是使用R进行数据分析的新手。我最近得到了一个预先格式化的环境观察模型数据集,其示例子集如下所示:

date                   site      obs    mod      site           obs    mod
2000-09-01 00:00:00    campus    NA     61.63    city centre    66     56.69
2000-09-01 01:00:00    campus    52     62.55    city centre    NA     54.75
2000-09-01 02:00:00    campus    52     63.52    city centre    56     54.65

基本上,数据包括每小时在“重复发生的列”中各个位置(即位置-obs-mod)处观察到的污染物浓度和建模浓度的时间序列(在示例中,我仅显示了总共75个位置中的2个)。 我以数据框的形式读取了这个“宽”数据集,并希望将其重塑为“窄”格式,如下所示:

date                   site           obs    mod
2000-09-01 00:00:00    campus         NA     61.63
2000-09-01 01:00:00    campus         52     62.55
2000-09-01 02:00:00    campus         52     63.52
2000-09-01 00:00:00    city centre    66     56.69
2000-09-01 01:00:00    city centre    NA     54.75
2000-09-01 02:00:00    city centre    56     54.65

我相信我应该使用包“ reshape2”来做到这一点。 首先,我尝试融合然后将数据集抛弃:

test.melt <- melt(test.data, id.vars = "date", measure.vars = c("site", "obs", "mod"))

但是,它仅返回了一半的数据,即,第一个站点(“校园”)之后的站点(“城市中心”)的记录全部被切断:

date                   variable    value
2001-01-01 00:00:00    site        campus
2001-01-01 01:00:00    site        campus
2001-01-01 02:00:00    site        campus
2001-01-01 00:00:00    obs         NA
2001-01-01 01:00:00    obs         52
2001-01-01 02:00:00    obs         52
2001-01-01 00:00:00    mod         61.63
2001-01-01 01:00:00    mod         62.55
2001-01-01 02:00:00    mod         63.52

然后,我尝试重铸:

test.recast <- recast(test.data, date ~ site + obs + mod)

但是,它返回了错误消息:

Error in eval(expr, envir, enclos) : object 'site' not found

我试图搜索以前的问题,但没有找到类似的情况(如果我错了,请纠正我)。 有人可以帮我吗?

提前谢谢了!

在执行一些变量名清除后,使用base R重塑可能会更好。

这是您的数据。

test <- read.table(header = TRUE, stringsAsFactors=FALSE,
text = "date             site  obs    mod    site             obs    mod
'2000-09-01 00:00:00'  campus   NA  61.63    'city centre'    66     56.69
'2000-09-01 01:00:00'  campus   52  62.55    'city centre'    NA     54.75
'2000-09-01 02:00:00'  campus   52  63.52    'city centre'    56     54.65")
test
#                  date   site obs   mod      site.1 obs.1 mod.1
# 1 2000-09-01 00:00:00 campus  NA 61.63 city centre    66 56.69
# 2 2000-09-01 01:00:00 campus  52 62.55 city centre    NA 54.75
# 3 2000-09-01 02:00:00 campus  52 63.52 city centre    56 54.65

如果正确执行此操作, 则应获得与我类似的名称:正如@chase 在他的回答中提到的那样 ,“重复的列名有点奇怪,并且不是正常的R行为”-因此我们必须对其进行修复。

注意: 这两个选项均会生成一个“时间”变量,您可以继续进行拖放。 您可能需要保留它,以防万一您想重新变宽为宽格式。

  • 选项1: 如果您的名字和我一样 (应该有),解决方案很简单。 对于第一个站点,只需在站点名称后附加“ 0”,然后使用基数R重塑:

     names(test)[2:4] <- paste(names(test)[2:4], "0", sep=".") test <- reshape(test, direction = "long", idvar = "date", varying = 2:ncol(test)) rownames(test) <- NULL # reshape makes UGLY rownames test # date time site obs mod # 1 2000-09-01 00:00:00 0 campus NA 61.63 # 2 2000-09-01 01:00:00 0 campus 52 62.55 # 3 2000-09-01 02:00:00 0 campus 52 63.52 # 4 2000-09-01 00:00:00 1 city centre 66 56.69 # 5 2000-09-01 01:00:00 1 city centre NA 54.75 # 6 2000-09-01 02:00:00 1 city centre 56 54.65 
  • 选项2: 如果确实有重复的列名 ,则修复仍然很容易,并且遵循相同的逻辑。 首先,创建更好的列名(使用rep()容易做到),然后如上所述使用reshape()

     names(test)[-1] <- paste(names(test)[-1], rep(1:((ncol(test)-1)/3), each = 3), sep = ".") test <- reshape(test, direction = "long", idvar = "date", varying = 2:ncol(test)) rownames(test) <- NULL ### Or, more convenient: # names(test) <- make.unique(names(test)) # names(test)[2:4] <- paste(names(test)[2:4], "0", sep=".") # test <- reshape(test, direction = "long", # idvar = "date", varying = 2:ncol(test)) # rownames(test) <- NULL 
  • 可选步骤:这种形式的数据仍然不是完全“长”的。 如果需要,则仅需再执行一个步骤:

     require(reshape2) melt(test, id.vars = c("date", "site", "time")) # date site time variable value # 1 2000-09-01 00:00:00 campus 0 obs NA # 2 2000-09-01 01:00:00 campus 0 obs 52.00 # 3 2000-09-01 02:00:00 campus 0 obs 52.00 # 4 2000-09-01 00:00:00 city centre 1 obs 66.00 # 5 2000-09-01 01:00:00 city centre 1 obs NA # 6 2000-09-01 02:00:00 city centre 1 obs 56.00 # 7 2000-09-01 00:00:00 campus 0 mod 61.63 # 8 2000-09-01 01:00:00 campus 0 mod 62.55 # 9 2000-09-01 02:00:00 campus 0 mod 63.52 # 10 2000-09-01 00:00:00 city centre 1 mod 56.69 # 11 2000-09-01 01:00:00 city centre 1 mod 54.75 # 12 2000-09-01 02:00:00 city centre 1 mod 54.65 

更新(尝试解决评论中的一些问题)

  1. reshape()文档非常混乱。 最好通过几个示例来理解它的工作原理。 具体来说,“时间”不必指时间(问题中的“日期”),而对于面板数据来说更多,例如,在同一时间在不同时间收集同一ID的记录。 对于您而言,原始数据中唯一的“ id”是“ date”列。 另一个可能的“ id”是站点,但不是组织数据的方式。

    试想一下,如果您的数据如下所示:

     test1 <- structure(list(date = structure(1:3, .Label = c("2000-09-01 00:00:00", "2000-09-01 01:00:00", "2000-09-01 02:00:00"), class = "factor"), obs.campus = c(NA, 52L, 52L), mod.campus = c(61.63, 62.55, 63.52), obs.cityCentre = c(66L, NA, 56L), mod.cityCentre = c(56.69, 54.75, 54.65)), .Names = c("date", "obs.campus", "mod.campus", "obs.cityCentre", "mod.cityCentre"), class = "data.frame", row.names = c(NA, -3L)) test1 # date obs.campus mod.campus obs.cityCentre mod.cityCentre # 1 2000-09-01 00:00:00 NA 61.63 66 56.69 # 2 2000-09-01 01:00:00 52 62.55 NA 54.75 # 3 2000-09-01 02:00:00 52 63.52 56 54.65 

    现在尝试reshape(test1, direction = "long", idvar = "date", varying = 2:ncol(test1)) 您会看到reshape()将站点名称视为“时间”(可以通过在reshape命令中添加“ timevar = "site" “来覆盖它)。

    direction = "long" ,必须指定哪些列随“ time”改变。 在您的情况下,那是除第一列以外的所有列,因此我将2:ncol(test)用于“ varying ”。

  2. test2 哪里是?

  3. @Chase的答案下的问题:我认为您误解了melt()应该如何工作。 基本上,它试图使您的数据“最纤细”。 在这种情况下,最肤浅的形式将是上述的“可选步骤”,因为date + site是构成唯一ID变量所需的最小值。 (我会说“ time ”可以放心地删除。)

    一旦您的数据采用“可选步骤”中所述的格式(我们假设输出已存储为“ test.melt ”,您就可以随时轻松以不同的方式来旋转表。意思是,请尝试以下操作并查看其作用。

     dcast(test.melt, date + site ~ variable) dcast(test.melt, date ~ variable + site) dcast(test.melt, variable + site ~ date) dcast(test.melt, variable + date ~ site) 

    如果您在“选项1”或“选项2”处停下来,要获得这样的灵活性并不容易。


更新(几年后)

melt从“data.table”现在能“融化”多列在以类似的方式reshape一样。 列名称是否重复都应该起作用。

您可以尝试以下方法:

measure <- c("site", "obs", "mod")
melt(as.data.table(test), measure.vars = patterns(measure), value.name = measure)
#                   date variable        site obs   mod
# 1: 2000-09-01 00:00:00        1      campus  NA 61.63
# 2: 2000-09-01 01:00:00        1      campus  52 62.55
# 3: 2000-09-01 02:00:00        1      campus  52 63.52
# 4: 2000-09-01 00:00:00        2 city centre  66 56.69
# 5: 2000-09-01 01:00:00        2 city centre  NA 54.75
# 6: 2000-09-01 02:00:00        2 city centre  56 54.65

您具有重复的列名的事实有点奇怪,并且不是正常的R行为。 大多数情况下,R通过make.names()函数强制您使用有效名称。 无论如何,我能够重复您的问题。 注意,由于您的示例不可复制,因此我做了自己的示例,但是逻辑是相同的。

#Do not force unique names
s <- data.frame(id = 1:3, x = runif(3), x = runif(3), check.names = FALSE)
#-----
  id         x         x
1  1 0.6845270 0.5218344
2  2 0.7662200 0.6179444
3  3 0.4110043 0.1104774

#Now try to melt, note that 1/2 of your x-values are missing!
melt(s, id.vars = 1)
#-----
  id variable     value
1  1        x 0.6845270
2  2        x 0.7662200
3  3        x 0.4110043

解决方案是使您的列名唯一。 正如我之前说过的,R在大多数情况下默认情况下会这样做。 但是,您可以在事后通过make.unique()

names(s) <- make.unique(names(s))
#-----
[1] "id"  "x"   "x.1"

请注意,x的第二列现在附加了1。 现在melt()可以按您期望的那样工作:

melt(s, id.vars = 1)
#-----
  id variable     value
1  1        x 0.6845270
2  2        x 0.7662200
3  3        x 0.4110043
4  1      x.1 0.5218344
5  2      x.1 0.6179444
6  3      x.1 0.1104774

在这一点上,如果您想将xx.1视为相同的变量,我认为可以使用一些gsub()或其他正则表达式函数来摆脱令人讨厌的字符。 这是我经常使用的工作流程。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM