首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >R重塑性能

R重塑性能
EN

Stack Overflow用户
提问于 2015-02-17 21:15:10
回答 6查看 212关注 0票数 5

编辑:在创建一个简单的示例data.frame时,我对两个日期列使用了相同的日期,但事实并非如此,这使得这个问题变得更加困难。

而不是这个数据帧:

代码语言:javascript
运行
复制
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

我有这个数据帧:

代码语言:javascript
运行
复制
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

我想将其重塑为以下内容:

代码语言:javascript
运行
复制
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...

目前我有以下几点。

代码语言:javascript
运行
复制
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左右的变量。

我在考虑写一个循环,有什么想法吗?请参阅下面的示例数据。

提前谢谢你,

罗伯特

代码语言:javascript
运行
复制
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))
EN

回答 6

Stack Overflow用户

发布于 2015-02-17 22:42:29

如果您的列的名称与您在示例中提供的名称相同,那么您可以尝试使用我的“merged.stack”包中的splitstackshape。请注意,"ID“列中的值必须是惟一的,才能正常工作(就像它们在示例数据中一样)。

用法很简单:指定变量的“存根”(这里是"Date“和"Balance")。设置sep = "var.stubs"只会去掉列名的其余部分。[, .time_1 := NULL]只是删除在整形过程中创建的时间列。

代码语言:javascript
运行
复制
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应该已经能够处理您当前的情况。

票数 3
EN

Stack Overflow用户

发布于 2015-02-17 21:26:44

通过组合这两个列集来构建新的data.frame不是最简单的解决方案吗?这可以在没有reshape的情况下完成

代码语言:javascript
运行
复制
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

你能让我知道这对你的数据是否表现良好吗?

对于排序:

代码语言:javascript
运行
复制
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的方法:

代码语言:javascript
运行
复制
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

现在,您可以使用以下命令实现所需的整形:

代码语言:javascript
运行
复制
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

和订购:

代码语言:javascript
运行
复制
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功能;它不需要依赖于任何附加包。我总是尽量避免使用附加包,因为随着理解代码所需的知识广度增加,这往往会在以后增加复杂性和维护难度。

票数 2
EN

Stack Overflow用户

发布于 2015-02-17 21:42:10

如果您关心顺序,那么最快的方法可能来自data.table answers。但是如果不这样做,那么可以使用rbind将前三列的行与前两列和后两列绑定。这将是非常快速和简单的,但没有你想要的顺序。您可以使用ID上的order函数进行重新排序。

或者,您可以生成两个矩阵,转置,然后将其作为向量绑定在一起。这将是非常快的,因为您只是制作了一些副本和选择,并且重新排序是通过以不同的方式识别数据来完成的,而不是依赖于排序算法。

代码语言:javascript
运行
复制
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上测试这两个版本的速度。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28562413

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档