我在下面给出了一个data.frame。我试图将它从长格式转移到宽格式。使用传播列为日期。使用tidyr
包中的扩散函数会出现两个问题:
那我怎么去
30-Apr-2015 632.95
28-May-2015 532.95
25-Jun-2015 232.95
到
30-Apr-2015 28-May-2015 25-Jun-2015
632.95 532.95 232.95
相反,我最终在
30-Apr-2015 25-Jun-2015 28-May-2015
632.95 NA 232.95
NA 232.95 NA
NA NA 532.95
实际日期并不重要,但是它们的相对排序事项,即最近的月份数据应该按顺序进入第一列,然后是其他两个月的数据。这是必要的,因为我在结果
上使用rbind
我试过的代码
data = tidyr::spread(data, key = EXPIRY_DT, value = CHG_IN_OI)
colnames(data)[3:5] = c('Month1', 'Month2', 'Month3')
data.frame如下所示:
data = structure(list(SYMBOL = c("A", "A", "A", "B", "B", "B", "C",
"C", "C", "D", "D", "D"), EXPIRY_DT = c("30-Apr-2015", "28-May-2015",
"25-Jun-2015", "30-Apr-2015", "28-May-2015", "25-Jun-2015", "30-Apr-2015",
"28-May-2015", "25-Jun-2015", "30-Apr-2015", "28-May-2015", "25-Jun-2015"
), OPEN = c(1750, 1789, 0, 1627.5, 1653.3, 0, 632.95, 644.1,
0, 317.8, 319.5, 0), HIGH = c(1788.05, 1795, 0, 1656.5, 1653.3,
0, 646.4, 650.5, 0, 324.6, 326.65, 0), LOW = c(1746, 1760, 0,
1627.5, 1645.45, 0, 629.65, 635, 0, 315.85, 318.4, 0), CLOSE = c(1782.3,
1791.85, 1695.1, 1642.95, 1646.75, 1613.9, 640.85, 644.35, 614.6,
320.55, 322.35, 310.85), SETTLE_PR = c(1782.3, 1791.85, 1804.8,
1642.95, 1653.85, 1664.35, 640.85, 644.35, 649.1, 320.55, 322.35,
325.35), CONTRACTS = c(1469L, 78L, 0L, 2638L, 14L, 0L, 4964L,
181L, 0L, 3416L, 82L, 0L), VALUE = c(6496.96, 347.91, 0, 10830.05,
57.68, 0, 15869.41, 583.38, 0, 10969.31, 264.93, 0), OPEN_INT = c(1353750L,
8500L, 0L, 1377250L, 17000L, 0L, 6264000L, 98000L, 0L, 8228000L,
216000L, 0L), CHG_IN_OI = c(15250L, 1250L, 0L, -21000L, 1500L,
0L, 73500L, 6000L, 0L, -192000L, 13000L, 0L), TIMESTAMP = c("10-APR-2015",
"10-APR-2015", "10-APR-2015", "10-APR-2015", "10-APR-2015", "10-APR-2015",
"10-APR-2015", "10-APR-2015", "10-APR-2015", "10-APR-2015", "10-APR-2015",
"10-APR-2015")), .Names = c("SYMBOL", "EXPIRY_DT", "OPEN", "HIGH",
"LOW", "CLOSE", "SETTLE_PR", "CONTRACTS", "VALUE", "OPEN_INT",
"CHG_IN_OI", "TIMESTAMP"), row.names = 40:51, class = "data.frame")
感谢阅读。
修改
来自@akrun的评论添加了预期的输出。因为每个日期的值不同,即需要一个接一个地放置每个月的数据,列名称附加字符串' Month1 / 2/3'而不是实际的日期。希望有所帮助。
output = structure(list(SYMBOL = c("A", "B", "C", "D"), TIMESTAMP = c("10-Apr-15",
"10-Apr-15", "10-Apr-15", "10-Apr-15"), OPEN.Month1 = c(1750,
1627.5, 632.95, 317.8), HIGH.Month1 = c(1788.05, 1656.5, 646.4,
324.6), LOW.Month1 = c(1746, 1627.5, 629.65, 315.85), CLOSE.Month1 = c(1782.3,
1642.95, 640.85, 320.55), SETTLE_PR.Month1 = c(1782.3, 1642.95,
640.85, 320.55), CONTRACTS.Month1 = c(1469L, 2638L, 4964L, 3416L
), VALUE.Month1 = c(6496.96, 10830.05, 15869.41, 10969.31), OPEN_INT.Month1 = c(1353750L,
1377250L, 6264000L, 8228000L), CHG_IN_OI.Month1 = c(15250L, -21000L,
73500L, -192000L), OPEN.Month2 = c(1789, 1653.3, 644.1, 319.5
), HIGH.Month2 = c(1795, 1653.3, 650.5, 326.65), LOW.Month2 = c(1760,
1645.45, 635, 318.4), CLOSE.Month2 = c(1791.85, 1646.75, 644.35,
322.35), SETTLE_PR.Month2 = c(1791.85, 1653.85, 644.35, 322.35
), CONTRACTS.Month2 = c(78L, 14L, 181L, 82L), VALUE.Month2 = c(347.91,
57.68, 583.38, 264.93), OPEN_INT.Month2 = c(8500L, 17000L, 98000L,
216000L), CHG_IN_OI.Month2 = c(1250L, 1500L, 6000L, 13000L),
OPEN.Month3 = c(0L, 0L, 0L, 0L), HIGH.Month3 = c(0L, 0L,
0L, 0L), LOW.Month3 = c(0L, 0L, 0L, 0L), CLOSE.Month3 = c(1695.1,
1613.9, 614.6, 310.85), SETTLE_PR.Month3 = c(1804.8, 1664.35,
649.1, 325.35), CONTRACTS.Month3 = c(0L, 0L, 0L, 0L), VALUE.Month3 = c(0L,
0L, 0L, 0L), OPEN_INT.Month3 = c(0L, 0L, 0L, 0L), CHG_IN_OI.Month3 = c(0L,
0L, 0L, 0L)), .Names = c("SYMBOL", "TIMESTAMP", "OPEN.Month1",
"HIGH.Month1", "LOW.Month1", "CLOSE.Month1", "SETTLE_PR.Month1",
"CONTRACTS.Month1", "VALUE.Month1", "OPEN_INT.Month1", "CHG_IN_OI.Month1",
"OPEN.Month2", "HIGH.Month2", "LOW.Month2", "CLOSE.Month2", "SETTLE_PR.Month2",
"CONTRACTS.Month2", "VALUE.Month2", "OPEN_INT.Month2", "CHG_IN_OI.Month2",
"OPEN.Month3", "HIGH.Month3", "LOW.Month3", "CLOSE.Month3", "SETTLE_PR.Month3",
"CONTRACTS.Month3", "VALUE.Month3", "OPEN_INT.Month3", "CHG_IN_OI.Month3"
), class = "data.frame", row.names = c(NA, -4L))
答案 0 :(得分:4)
我们可以使用devel
的{{1}}版本即ie。 ' v1.9.5'这可能需要多个" value.vars"。安装devel版本的说明是here
。
更改' data.frame'到' data.table' (data.table
)。创建一个"月"通过粘贴“月份”列来填充每个" SYMBOL"的行号。然后,我们可以使用setDT(data)
,将dcast
指定为列' 3:11'。
value.var
如果我们需要将列名更改为'输出'中的特定格式,请使用library(data.table)
res <- dcast(setDT(data)[, Month:=paste0('Month', 1:.N), by=SYMBOL],
SYMBOL+TIMESTAMP~Month, value.var=names(data)[3:11])
。我按照预期结果(&#39;输出&#39;)重新排列了列的顺序,并将data.table更改为data.frame(setnames
)
setDF
&#39;输出中的setnames(res, sub('([^_]+)_(.*)', '\\2.\\1', colnames(res)))
res1 <- setDF(res[,names(output), with=FALSE])
res1
# SYMBOL TIMESTAMP OPEN.Month1 HIGH.Month1 LOW.Month1 CLOSE.Month1
#1 A 10-APR-2015 1750.00 1788.05 1746.00 1782.30
#2 B 10-APR-2015 1627.50 1656.50 1627.50 1642.95
#3 C 10-APR-2015 632.95 646.40 629.65 640.85
#4 D 10-APR-2015 317.80 324.60 315.85 320.55
# SETTLE_PR.Month1 CONTRACTS.Month1 VALUE.Month1 OPEN_INT.Month1
#1 1782.30 1469 6496.96 1353750
#2 1642.95 2638 10830.05 1377250
#3 640.85 4964 15869.41 6264000
#4 320.55 3416 10969.31 8228000
# CHG_IN_OI.Month1 OPEN.Month2 HIGH.Month2 LOW.Month2 CLOSE.Month2
#1 15250 1789.0 1795.00 1760.00 1791.85
#2 -21000 1653.3 1653.30 1645.45 1646.75
#3 73500 644.1 650.50 635.00 644.35
#4 -192000 319.5 326.65 318.40 322.35
# SETTLE_PR.Month2 CONTRACTS.Month2 VALUE.Month2 OPEN_INT.Month2
#1 1791.85 78 347.91 8500
#2 1653.85 14 57.68 17000
#3 644.35 181 583.38 98000
#4 322.35 82 264.93 216000
# CHG_IN_OI.Month2 OPEN.Month3 HIGH.Month3 LOW.Month3 CLOSE.Month3
#1 1250 0 0 0 1695.10
#2 1500 0 0 0 1613.90
#3 6000 0 0 0 614.60
#4 13000 0 0 0 310.85
# SETTLE_PR.Month3 CONTRACTS.Month3 VALUE.Month3 OPEN_INT.Month3
#1 1804.80 0 0 0
#2 1664.35 0 0 0
#3 649.10 0 0 0
#4 325.35 0 0 0
# CHG_IN_OI.Month3
#1 0
#2 0
#3 0
#4 0
列&#39;是格式不同。更改了&#39; res1&#39;中的格式它与预期的输出相同。
TIMESTAMP
或者我们可以使用res1$TIMESTAMP <- format(as.Date(res1$TIMESTAMP, '%d-%b-%Y'), '%d-%b-%y')
all.equal(output, res1)
#[1] TRUE
中的reshape
,它会带有多个值列。就像我们之前创建的序列一样,我们可以使用base R
来创建“MONTH&#39;列,并将其用作ave
中的timevar
。
reshape
答案 1 :(得分:2)
非常棘手的问题。我设计了一个非常接近您的样本输出的解决方案;你应该能够清理之后的小差异(请参阅我的答案的末尾以获得差异摘要)。
首先,让我从我的假设开始:
data
已根据EXPIRY_DT
正确排序(每个SYMBOL
独立)。您的样本输入满足此假设。现在,作为一般建议,您应该尝试始终使用ISO 8601作为日期格式,这自然按字典顺序排序,并且自然允许您在R中强制为Date
格式。鉴于您的输入日期格式,如果您想保证正确的订单,则必须致电as.Date()
并传递输入格式,然后拨打order()
。我没有在我的代码中包含这些内容,而是假设数据已经被订购。TIMESTAMP
的{{1}}的所有值,因此我假设这两列包含数据的多列主键。如果这不正确,您只需将我在代码中定义的SYMBOL
变量更改为不包含keys
即可。但如果是这种情况,那么您将在输出中获得额外的TIMESTAMP
列(如果需要,可以在之后删除)。TIMESTAMP.Month{mnum}
正如您所看到的,我的解决方案的核心是将输入数据按月份分成单独的data.frames,这样可以为每个拆分独立地为所有非键列添加后缀,然后重复调用{{3将它们合并在一起。
keys <- c('SYMBOL','TIMESTAMP');
mnum <- ave(1:nrow(data), data[,keys], FUN=seq_along );
mnum;
## [1] 1 2 3 1 2 3 1 2 3 1 2 3
mdata <- lapply(1:max(mnum), function(x) setNames(data[mnum==x,],ifelse(names(data)%in%keys,names(data),paste0(names(data),'.Month',x))) );
mdata;
## [[1]]
## SYMBOL EXPIRY_DT.Month1 OPEN.Month1 HIGH.Month1 LOW.Month1 CLOSE.Month1 SETTLE_PR.Month1 CONTRACTS.Month1 VALUE.Month1 OPEN_INT.Month1 CHG_IN_OI.Month1 TIMESTAMP
## 40 A 30-Apr-2015 1750.00 1788.05 1746.00 1782.30 1782.30 1469 6496.96 1353750 15250 10-APR-2015
## 43 B 30-Apr-2015 1627.50 1656.50 1627.50 1642.95 1642.95 2638 10830.05 1377250 -21000 10-APR-2015
## 46 C 30-Apr-2015 632.95 646.40 629.65 640.85 640.85 4964 15869.41 6264000 73500 10-APR-2015
## 49 D 30-Apr-2015 317.80 324.60 315.85 320.55 320.55 3416 10969.31 8228000 -192000 10-APR-2015
##
## [[2]]
## SYMBOL EXPIRY_DT.Month2 OPEN.Month2 HIGH.Month2 LOW.Month2 CLOSE.Month2 SETTLE_PR.Month2 CONTRACTS.Month2 VALUE.Month2 OPEN_INT.Month2 CHG_IN_OI.Month2 TIMESTAMP
## 41 A 28-May-2015 1789.0 1795.00 1760.00 1791.85 1791.85 78 347.91 8500 1250 10-APR-2015
## 44 B 28-May-2015 1653.3 1653.30 1645.45 1646.75 1653.85 14 57.68 17000 1500 10-APR-2015
## 47 C 28-May-2015 644.1 650.50 635.00 644.35 644.35 181 583.38 98000 6000 10-APR-2015
## 50 D 28-May-2015 319.5 326.65 318.40 322.35 322.35 82 264.93 216000 13000 10-APR-2015
##
## [[3]]
## SYMBOL EXPIRY_DT.Month3 OPEN.Month3 HIGH.Month3 LOW.Month3 CLOSE.Month3 SETTLE_PR.Month3 CONTRACTS.Month3 VALUE.Month3 OPEN_INT.Month3 CHG_IN_OI.Month3 TIMESTAMP
## 42 A 25-Jun-2015 0 0 0 1695.10 1804.80 0 0 0 0 10-APR-2015
## 45 B 25-Jun-2015 0 0 0 1613.90 1664.35 0 0 0 0 10-APR-2015
## 48 C 25-Jun-2015 0 0 0 614.60 649.10 0 0 0 0 10-APR-2015
## 51 D 25-Jun-2015 0 0 0 310.85 325.35 0 0 0 0 10-APR-2015
##
res <- Reduce(function(x,y) merge(x,y,by=keys,all=T), mdata );
res;
## SYMBOL TIMESTAMP EXPIRY_DT.Month1 OPEN.Month1 HIGH.Month1 LOW.Month1 CLOSE.Month1 SETTLE_PR.Month1 CONTRACTS.Month1 VALUE.Month1 OPEN_INT.Month1 CHG_IN_OI.Month1 EXPIRY_DT.Month2 OPEN.Month2 HIGH.Month2 LOW.Month2 CLOSE.Month2 SETTLE_PR.Month2 CONTRACTS.Month2 VALUE.Month2 OPEN_INT.Month2 CHG_IN_OI.Month2 EXPIRY_DT.Month3 OPEN.Month3 HIGH.Month3 LOW.Month3 CLOSE.Month3 SETTLE_PR.Month3 CONTRACTS.Month3 VALUE.Month3 OPEN_INT.Month3 CHG_IN_OI.Month3
## 1 A 10-APR-2015 30-Apr-2015 1750.00 1788.05 1746.00 1782.30 1782.30 1469 6496.96 1353750 15250 28-May-2015 1789.0 1795.00 1760.00 1791.85 1791.85 78 347.91 8500 1250 25-Jun-2015 0 0 0 1695.10 1804.80 0 0 0 0
## 2 B 10-APR-2015 30-Apr-2015 1627.50 1656.50 1627.50 1642.95 1642.95 2638 10830.05 1377250 -21000 28-May-2015 1653.3 1653.30 1645.45 1646.75 1653.85 14 57.68 17000 1500 25-Jun-2015 0 0 0 1613.90 1664.35 0 0 0 0
## 3 C 10-APR-2015 30-Apr-2015 632.95 646.40 629.65 640.85 640.85 4964 15869.41 6264000 73500 28-May-2015 644.1 650.50 635.00 644.35 644.35 181 583.38 98000 6000 25-Jun-2015 0 0 0 614.60 649.10 0 0 0 0
## 4 D 10-APR-2015 30-Apr-2015 317.80 324.60 315.85 320.55 320.55 3416 10969.31 8228000 -192000 28-May-2015 319.5 326.65 318.40 322.35 322.35 82 264.93 216000 13000 25-Jun-2015 0 0 0 310.85 325.35 0 0 0 0
向量代表&#34;月号&#34;。你可以认为它是一种&#34;分离的&#34;输入mnum
对象的列;它表示data
中每行所属的主键组中的月份编号。我使用merge()
为每个组调用ave()
一次,生成一个长度等于组大小的顺序整数向量(即组中的行数),data
映射回来到原始ave()
对象中组行的位置。
data
对象是data.frames列表,其中每个组件代表一个月的数字。使用特定月份编号实际提取行是通过简单的逻辑索引操作完成的:
mdata
其中data[mnum==x,]
是x
元素,seq_along()
遍历mnum
。使用lapply()
完成非键列名称的后缀,派生替换列名称如下:
1:max(mnum)
上面保留了关键列的名称,但是将ifelse(names(data)%in%keys,names(data),paste0(names(data),'.Month',x))
附加到所有非关键列的名称。
最后,所有月份号码拆分必须合并回一个data.frame。我以为我能够使用'.Month{mnum}'
的单个调用(可能需要setNames()
的一点帮助)才能做到这一点,但是却发现它只需要两个参数才能合并,merge()
和x
(另见do.call()
)。因此,我需要调用Simultaneously merge multiple data.frames in a list来实现重复调用。如果您的不同符号具有不同的到期日数,则y
参数将非常重要;然后&#34;短&#34;如果all=T
未通过,则符号不会在最终合并的RHS上表示,因此将被删除。
我的输出与您的样本输出完全匹配。以下是不一致之处:
答案 2 :(得分:1)
记得aggregate()
有一个data.frames的重载,可以用来实现这个要求。列名称和顺序不会完全符合您的要求,但它们肯定是合乎逻辑且可用的(并且可以在之后进行调整):
keys <- c('SYMBOL','TIMESTAMP');
aggregate(data[,!(names(data)%in%keys)],data[,names(data)%in%keys],identity);
## SYMBOL TIMESTAMP EXPIRY_DT.1 EXPIRY_DT.2 EXPIRY_DT.3 OPEN.1 OPEN.2 OPEN.3 HIGH.1 HIGH.2 HIGH.3 LOW.1 LOW.2 LOW.3 CLOSE.1 CLOSE.2 CLOSE.3 SETTLE_PR.1 SETTLE_PR.2 SETTLE_PR.3 CONTRACTS.1 CONTRACTS.2 CONTRACTS.3 VALUE.1 VALUE.2 VALUE.3 OPEN_INT.1 OPEN_INT.2 OPEN_INT.3 CHG_IN_OI.1 CHG_IN_OI.2 CHG_IN_OI.3
## 1 A 10-APR-2015 30-Apr-2015 28-May-2015 25-Jun-2015 1750.00 1789.00 0.00 1788.05 1795.00 0.00 1746.00 1760.00 0.00 1782.30 1791.85 1695.10 1782.30 1791.85 1804.80 1469 78 0 6496.96 347.91 0.00 1353750 8500 0 15250 1250 0
## 2 B 10-APR-2015 30-Apr-2015 28-May-2015 25-Jun-2015 1627.50 1653.30 0.00 1656.50 1653.30 0.00 1627.50 1645.45 0.00 1642.95 1646.75 1613.90 1642.95 1653.85 1664.35 2638 14 0 10830.05 57.68 0.00 1377250 17000 0 -21000 1500 0
## 3 C 10-APR-2015 30-Apr-2015 28-May-2015 25-Jun-2015 632.95 644.10 0.00 646.40 650.50 0.00 629.65 635.00 0.00 640.85 644.35 614.60 640.85 644.35 649.10 4964 181 0 15869.41 583.38 0.00 6264000 98000 0 73500 6000 0
## 4 D 10-APR-2015 30-Apr-2015 28-May-2015 25-Jun-2015 317.80 319.50 0.00 324.60 326.65 0.00 315.85 318.40 0.00 320.55 322.35 310.85 320.55 322.35 325.35 3416 82 0 10969.31 264.93 0.00 8228000 216000 0 -192000 13000 0
基础R的简洁解决方案!
修改:感谢@Frash指出上述&#34;解决方案&#34;中的怪癖。可以通过如下包装aggregate()
来纠正这种情况:
do.call(data.frame,...);
这是因为data.frame()
会自动将矩阵扩展为结果data.frame中的独立列(类#34; model.matrix&#34;以及I()
保护的矩阵除外)。