R:`split`保持因子的自然顺序

时间:2013-07-12 09:29:59

标签: r split

split将始终按字典顺序排序分组。在某些情况下,人们宁愿保留自然秩序。一个人总是可以实现手动滚动功能,但有一个基本的R解决方案吗?

可重复的例子:

输入:

  Date.of.Inclusion Securities.Included Securities.Excluded yearmon
1        2013-04-01          INDUSINDBK             SIEMENS  4 2013
2        2013-04-01                NMDC               WIPRO  4 2013
3        2012-09-28               LUPIN                SAIL  9 2012
4        2012-09-28          ULTRACEMCO                STER  9 2012
5        2012-04-27          ASIANPAINT                RCOM  4 2012
6        2012-04-27          BANKBARODA              RPOWER  4 2012

split输出:

R> split(nifty.dat, nifty.dat$yearmon)
$`4 2012`
  Date.of.Inclusion Securities.Included Securities.Excluded yearmon
5        2012-04-27          ASIANPAINT                RCOM  4 2012
6        2012-04-27          BANKBARODA              RPOWER  4 2012

$`4 2013`
  Date.of.Inclusion Securities.Included Securities.Excluded yearmon
1        2013-04-01          INDUSINDBK             SIEMENS  4 2013
2        2013-04-01                NMDC               WIPRO  4 2013

$`9 2012`
  Date.of.Inclusion Securities.Included Securities.Excluded yearmon
3        2012-09-28               LUPIN                SAIL  9 2012
4        2012-09-28          ULTRACEMCO                STER  9 2012

请注意yearmon已经按照我想要的特定顺序排序。这可以视为给定,因为如果这个问题不成立,则问题会略微错误指定。

期望的输出:

$`4 2013`
  Date.of.Inclusion Securities.Included Securities.Excluded yearmon
1        2013-04-01          INDUSINDBK             SIEMENS  4 2013
2        2013-04-01                NMDC               WIPRO  4 2013

$`9 2012`
  Date.of.Inclusion Securities.Included Securities.Excluded yearmon
3        2012-09-28               LUPIN                SAIL  9 2012
4        2012-09-28          ULTRACEMCO                STER  9 2012

$`4 2012`
  Date.of.Inclusion Securities.Included Securities.Excluded yearmon
5        2012-04-27          ASIANPAINT                RCOM  4 2012
6        2012-04-27          BANKBARODA              RPOWER  4 2012

感谢。

PS:我知道有更好的方法来创建yearmon以保留该订单,但我正在寻找通用解决方案。

1 个答案:

答案 0 :(得分:18)

splitf(第二个)参数转换为因子,如果它不是一个。因此,如果您希望保留订单,请使用所需级别自行调整列。那就是:

df$yearmon <- factor(df$yearmon, levels=unique(df$yearmon))
# now split
split(df, df$yearmon)
# $`4_2013`
#   Date.of.Inclusion Securities.Included Securities.Excluded yearmon
# 1        2013-04-01          INDUSINDBK             SIEMENS  4_2013
# 2        2013-04-01                NMDC               WIPRO  4_2013

# $`9_2012`
#   Date.of.Inclusion Securities.Included Securities.Excluded yearmon
# 3        2012-09-28               LUPIN                SAIL  9_2012
# 4        2012-09-28          ULTRACEMCO                STER  9_2012

# $`4_2012`
#   Date.of.Inclusion Securities.Included Securities.Excluded yearmon
# 5        2012-04-27          ASIANPAINT                RCOM  4_2012
# 6        2012-04-27          BANKBARODA              RPOWER  4_2012

但请勿使用split。请改用data.table

然而,通常情况下,随着等级的增加,split往往非常慢。所以,我建议使用data.table子集到列表。我想这会快得多!

require(data.table)
dt <- data.table(df)
dt[, grp := .GRP, by = yearmon]
setkey(dt, grp)
o2 <- dt[, list(list(.SD)), by = grp]$V1

对大量数据进行基准测试:

set.seed(45)
dates <- seq(as.Date("1900-01-01"), as.Date("2013-12-31"), by = "days")
ym <- do.call(paste, c(expand.grid(1:500, 1900:2013), sep="_"))

df <- data.frame(x1 = sample(dates, 1e4, TRUE), 
                 x2 = sample(letters, 1e4, TRUE), 
                 x3 = sample(10, 1e4, TRUE), 
                 yearmon = sample(ym, 1e4, TRUE), 
      stringsAsFactors=FALSE)

require(data.table)
dt <- data.table(df)

f1 <- function(dt) {
    dt[, grp := .GRP, by = yearmon]
    setkey(dt, grp)

    o1 <- dt[, list(list(.SD)), by=grp]$V1
}

f2 <- function(df) {
    df$yearmon <- factor(df$yearmon, levels=unique(df$yearmon))
    o2 <- split(df, df$yearmon)
}

require(microbenchmark)
microbenchmark(o1 <- f1(dt), o2 <- f2(df), times = 10)

# Unit: milliseconds
         expr        min         lq     median        uq      max neval
#  o1 <- f1(dt)   43.72995   43.85035   45.20087  715.1292 1071.976    10
#  o2 <- f2(df) 4485.34205 4916.13633 5210.88376 5763.1667 6912.741    10

请注意,o1的解决方案将是未命名列表。但您只需执行names(o1) <- unique(dt$yearmon)

即可设置名称