编辑:在创建一个简单的示例data.frame时,我对两个日期列使用了相同的日期,但事实并非如此,这使得这个问题变得更加困难。
而不是这个数据帧:
ID Date Balance Date2 Balance2
1 01-01-2014 10000 01-02-2014 5000
2 01-01-2014 50000 01-02-2014 30000
3 01-01-2014 30000 01-02-2014 15000
4 01-01-2014 5000 01-02-2014 3500
我有这个数据帧:
ID Date Balance Date2 Balance2
1 01-01-2014 10000 01-02-2017 5000
2 01-01-2015 50000 01-02-2016 30000
3 01-08-2014 30000 01-02-2015 15000
4 01-02-2016 5000 01-02-2018 3500
我想将其重塑为以下内容:
ID Date Balance
1 01-01-2014 10000
1 02-02-2017 5000
2 01-01-2015 50000
2 01-02-2016 30000
3 ... ... And so on...
目前我有以下几点。
Dates = a character containing all the columns with Dates (Date, Date2, Date3...)
Balances = a character containing all the columns with Balances (Balance1, Balance2...)
df <- reshape(df,
varying = Balances,
v.names = "Balance"
timevar = "Date"
times = Dates,
direction = "long")
当我更改我的示例data.frame / data.table时,您提出的优秀方法的结果并不能得到结果。
主要的问题是,我在dates列中有不同的日期,我无法更改它。Date1 - Date2 - Date3总是按时间顺序排列的。
我需要一种方法,让R知道它需要获取Date列和Balance列,将其放入一个新的DF中,然后获取Date2和Balance2,将它们与第一个DF重新绑定,然后是Date3、Balance3,依此类推,直到我得到700左右的变量。
我在考虑写一个循环,有什么想法吗?请参阅下面的示例数据。
提前谢谢你,
罗伯特
df <- data.frame(ID=seq(1:4),
Date= c("01-01-2014","01-01-2015","01-08-2014","01-02-2016"),
Balance = c(10000,50000,30000,5000),
Date2= c("01-02-2017","01-02-2016","01-02-2015","01-02-2018"),
Balance2 = c(5000,30000,15000,3500))
发布于 2015-02-17 22:42:29
如果您的列的名称与您在示例中提供的名称相同,那么您可以尝试使用我的“merged.stack
”包中的splitstackshape。请注意,"ID“列中的值必须是惟一的,才能正常工作(就像它们在示例数据中一样)。
用法很简单:指定变量的“存根”(这里是"Date“和"Balance")。设置sep = "var.stubs"
只会去掉列名的其余部分。[, .time_1 := NULL]
只是删除在整形过程中创建的时间列。
library(splitstackshape)
merged.stack(mydf, var.stubs = c("Date", "Balance"),
sep = "var.stubs")[, .time_1 := NULL][]
# ID Date Balance
# 1: 1 01-01-2014 10000
# 2: 1 01-02-2014 5000
# 3: 2 01-01-2014 50000
# 4: 2 01-02-2014 30000
# 5: 3 01-01-2014 30000
# 6: 3 01-02-2014 15000
# 7: 4 01-01-2014 5000
# 8: 4 01-02-2014 3500
很快(“data.table”的1.9.8版本) melt
将能够处理到半长表单的转换,就像您正在尝试实现的那样。这将比目前的merged.stack
更快,但merged.stack
应该已经能够处理您当前的情况。
发布于 2015-02-17 21:26:44
通过组合这两个列集来构建新的data.frame不是最简单的解决方案吗?这可以在没有reshape
的情况下完成
r> x <- data.frame(ID=1:4, Date=as.POSIXct(c('2014-01-01','2014-01-01','2014-01-01','2014-01-01')), Balance=c(10000,50000,30000,5000), Date2=as.POSIXct(c('2014-01-02','2014-01-02','2014-01-02','2014-01-02')), Balance2=c(5000,30000,15000,3500) );
r> y <- data.frame(ID=c(x$ID,x$ID), Date=c(x$Date,x$Date2), Balance=c(x$Balance,x$Balance2) );
r> y;
ID Date Balance
1 1 2014-01-01 10000
2 2 2014-01-01 50000
3 3 2014-01-01 30000
4 4 2014-01-01 5000
5 1 2014-01-02 5000
6 2 2014-01-02 30000
7 3 2014-01-02 15000
8 4 2014-01-02 3500
你能让我知道这对你的数据是否表现良好吗?
对于排序:
r> z <- y[order(y$ID,y$Date),]; rownames(z) <- 1:nrow(z);
r> z;
ID Date Balance
1 1 2014-01-01 10000
2 1 2014-01-02 5000
3 2 2014-01-01 50000
4 2 2014-01-02 30000
5 3 2014-01-01 30000
6 3 2014-01-02 15000
7 4 2014-01-01 5000
8 4 2014-01-02 3500
编辑:考虑到您有这么多列,在每个日期和余额列上手动调用c()
是不切实际的。但是,在尝试了一下之后,我意识到您可以结合使用names()
、grep()
、do.call()
和c()
函数来自动提取和组合您想要的数据。您还需要使用unname()
删除不需要的元素名称,使用replicate()
将ID列复制足够多的次数。
首先,我想出了一种生成用于测试的随机输入data.frame的方法:
r> randDate <- function() as.Date('2014-01-01')+as.integer(runif(1,max=30));
r> randBalance <- function() 5000+as.integer(runif(1,max=18))*5000;
r> n <- 700;
r> x <- setNames(do.call(data.frame, c(list(1:4), replicate(n, list(do.call(c, replicate(4, randDate(), simplify=F ) ), do.call(c, replicate(4, randBalance(), simplify=F ) ) ), simplify=F ) ) ), c('ID', sapply(1:n, function(x) c(paste0('Date',x), paste0('Balance',x) ) ) ) );
r> x;
ID Date1 Balance1 Date2 Balance2 Date3 Balance3 ... Balance698 Date699 Balance699 Date700 Balance700
1 1 2014-01-29 10000 2014-01-08 50000 2014-01-05 40000 ... 30000 2014-01-23 35000 2014-01-08 45000
2 2 2014-01-30 65000 2014-01-15 10000 2014-01-11 45000 ... 75000 2014-01-29 25000 2014-01-04 50000
3 3 2014-01-11 75000 2014-01-14 70000 2014-01-24 45000 ... 50000 2014-01-02 10000 2014-01-01 50000
4 4 2014-01-11 25000 2014-01-11 20000 2014-01-24 20000 ... 50000 2014-01-08 70000 2014-01-11 75000
现在,您可以使用以下命令实现所需的整形:
r> y <- data.frame(ID=do.call(c, replicate((ncol(x)-1)/2, x$ID, simplify=F ) ), Date=unname(do.call(c, x[,grep('^Date[0-9]+$', names(x) )] )), Balance=unname(do.call(c, x[,grep('^Balance[0-9]+$', names(x) )] )) );
r> y;
ID Date Balance
1 1 2014-01-29 10000
2 2 2014-01-30 65000
3 3 2014-01-11 75000
4 4 2014-01-11 25000
5 1 2014-01-08 50000
6 2 2014-01-15 10000
...
2795 3 2014-01-02 10000
2796 4 2014-01-08 70000
2797 1 2014-01-08 45000
2798 2 2014-01-04 50000
2799 3 2014-01-01 50000
2800 4 2014-01-11 75000
和订购:
r> z <- y[order(y$ID,y$Date),]; rownames(z) <- 1:nrow(z);
r> z;
ID Date Balance
1 1 2014-01-01 55000
2 1 2014-01-01 20000
3 1 2014-01-01 15000
4 1 2014-01-01 75000
5 1 2014-01-01 40000
6 1 2014-01-01 85000
...
2795 4 2014-01-30 15000
2796 4 2014-01-30 65000
2797 4 2014-01-30 5000
2798 4 2014-01-30 70000
2799 4 2014-01-30 35000
2800 4 2014-01-30 30000
这段代码基本上是即时运行的。速度的关键在于,它通过下标data.frame同时提取目标输出列的每个输入列(例如,所有日期列的x[,grep('^Date[0-9]+$', names(x) )]
),并通过对c()
的单个调用运行所有这些列(通过对do.call
的单个调用),这会忽略参数的data.frame类,并将其视为基础列表。最终结果是,您获得了输出列的c()
-combined向量,其形式几乎可以附加到输出data.frame (只需使用unname()
删除不需要的元素名称)。您需要对Date列和Balance列分别执行此操作(通过x[,grep('^Balance[0-9]+$', names(x) )]
下标的Balance列),并将它们打包到一个新的data.frame构造调用中。这个难题的另一个部分是复制输入ID列足够多的次数((ncol(x)-1)/2
),以生成与日期和平衡输出向量对应的正确输出ID列。
这个解决方案是完全矢量化的,没有显式或隐藏的循环。此外,它只使用内置的R功能;它不需要依赖于任何附加包。我总是尽量避免使用附加包,因为随着理解代码所需的知识广度增加,这往往会在以后增加复杂性和维护难度。
发布于 2015-02-17 21:42:10
如果您关心顺序,那么最快的方法可能来自data.table
answers。但是如果不这样做,那么可以使用rbind
将前三列的行与前两列和后两列绑定。这将是非常快速和简单的,但没有你想要的顺序。您可以使用ID上的order
函数进行重新排序。
或者,您可以生成两个矩阵,转置,然后将其作为向量绑定在一起。这将是非常快的,因为您只是制作了一些副本和选择,并且重新排序是通过以不同的方式识别数据来完成的,而不是依赖于排序算法。
dateMat <- as.matrix(df[, c(2, 4)])
balMat <- as.matrix(df[, c(3, 5)])
dates <- as.vector( t(dateMat) )
balances <- as.vector( t(balMat) )
dfl <- data.frame(ID = rep(df$ID, each = 2), Date = dates, Balance = balances)
您可以在大型data.frame
上测试这两个版本的速度。
https://stackoverflow.com/questions/28562413
复制相似问题