4

我希望在 R 中过滤一个数据框 original.data。该数据框可能有大约 1-2 百万个观察值。数据框有几个字段,名称可能会有所不同。用户可以选择要过滤的字段。这些字段名称存储在 names(all.filters) 中,其中 all.filters 是可变长度的列表。然后,用户可以为名称(all.filters)中的每个字段选择级别。例如,此列表可能类似于:

> all.filters
$Period
[1] "2010-12-31" "2011-03-31" "2011-06-30" "2011-09-30" "2011-12-31"
[6] "2012-03-31" "2012-06-30" "2012-09-30"

$Size
[1] "L"  "VL"

$Number
[1] "11" "21" "35" "42" "45" "47" "49" "52" "57"

我正在使用以下代码应用选择的过滤器:

attach(original.data)    
filter.names <- names(all.filters)
flag <- 1
for(filter in filter.names){
   flag <- flag*(is.element(get(filter),all.filters[[filter]]))
}
filtered.data <- original.data[flag==1,]

这行得通,但感觉有点慢。请注意,get(filter) 检索列名等于 filter 的 original.data 列。我不确定这是否是过滤数据的好方法,但 all.filters 的可变性质限制了我的选择 - 我想使用子集,但我不确定将什么作为选择参数。我想让这个过滤步骤尽可能快,以便当用户更新过滤器选择时,可以快速绘制数据。

过滤数据后,我使用 reshape2 对数据进行汇总,然后再使用 ggplot2 绘制数据。我认为如果可能的话,在这些步骤之一中应用过滤器可能会更有效。

任何建议将不胜感激。

4

3 回答 3

2

您可以将 adata.table与适当设置的键一起使用。这将节省内存。

然后你可以将你list的过滤器传递i[.data.table

.period <- seq(from = as.Date("2010/1/1", "%Y/%m/%d"), to = as.Date("2012/1/1", 
    "%Y/%m/%d"), by = "3 months")
.size <- c("XS", "S", "M", "L", "XL")
.number <- as.character(1:100)
DF <- expand.grid(Period = .period, Size = .size, Number = .number, stringsAsFactors = F)

DF$other <- rnorm(nrow(DF))

library(data.table)

DT <- as.data.table(DF)

DT[, `:=`(Period, as.IDate(.period))]


##           Period Size Number    other
##    1: 2010-01-01   XS      1  0.17947
##    2: 2010-04-01   XS      1  1.43252
##    3: 2010-07-01   XS      1 -0.97142
##    4: 2010-10-01   XS      1 -0.98021
##    5: 2011-01-01   XS      1 -0.62964
##   ---                                
## 4496: 2011-01-01   XL    100  0.65831
## 4497: 2011-04-01   XL    100 -0.45277
## 4498: 2011-07-01   XL    100 -0.14236
## 4499: 2011-10-01   XL    100 -0.02376
## 4500: 2012-01-01   XL    100 -0.11525

all_filters <- list(Period = as.IDate(as.Date("2010/1/1", format = "%Y/%m/%d")), 
    Size = "L", Number = c("11", "21", "35", "42", "45", "47", "49", "52", "57"))


setkeyv(DT, names(all_filters))

DT[all_filters]

##        Period Size Number   other
## 1: 2010-01-01    L     11  1.4122
## 2: 2010-01-01    L     21 -0.4923
## 3: 2010-01-01    L     35  1.1262
## 4: 2010-01-01    L     42  1.3527
## 5: 2010-01-01    L     45 -0.3758
## 6: 2010-01-01    L     47 -0.1847
## 7: 2010-01-01    L     49 -0.8503
## 8: 2010-01-01    L     52 -1.0645
## 9: 2010-01-01    L     57 -0.6092

我能看到的唯一问题是您每次都必须重置密钥以确保引用正确的列。此外,您需要确保过滤器标识符与 data.frame 中的列属于同一类 - 使用characterfactor列可能更容易

编辑

要在多个列上过滤多个级别,请使用CJ. CJ 是一个交叉连接,(data.table 等效于 expand.grid,带有键集)

all_filters <- list(Period = as.IDate(as.Date("2010/1/1", format = "%Y/%m/%d")), 
  Size = c("L",'XL'), Number = c("11", "21", "35", "42", "45", "47", "49", "52", "57"))




cj_filter <- do.call(CJ, all_filters)

# note you could avoid this `do.call` line by
# cj_filter <- CJ(Period = as.IDate(as.Date("2010/1/1", format = "%Y/%m/%d")), 
  Size = c("L",'XL'), Number = c("11", "21", "35", "42", "45", "47", "49", "52", "57"))

setkeyv(DT, names(cj_filter))

DT[cj_filter]
       Period Size Number       other
 1: 2010-01-01    L     11  0.36289104
 2: 2010-01-01    L     21  1.26356767
 3: 2010-01-01    L     35 -0.18629723
 4: 2010-01-01    L     42  0.92267902
 5: 2010-01-01    L     45  1.68796072
 6: 2010-01-01    L     47  1.75107447
 7: 2010-01-01    L     49  0.24048407
 8: 2010-01-01    L     52  0.06675221
 9: 2010-01-01    L     57  0.49665392
10: 2010-01-01   XL     11  0.33682495
11: 2010-01-01   XL     21  0.67642271
12: 2010-01-01   XL     35 -0.16412768
13: 2010-01-01   XL     42  0.72863394
14: 2010-01-01   XL     45 -0.55527588
15: 2010-01-01   XL     47  1.30850591
16: 2010-01-01   XL     49  1.08688166
17: 2010-01-01   XL     52 -0.31157250
18: 2010-01-01   XL     57  0.43626422

你也可以做

 setkeyv(DT, names(all_filters))

 DT[do.call(CJ,all_filters)]
于 2012-10-24T23:33:57.497 回答
1

一种稍微更通用的方法,它不依赖于对字段名称进行硬编码:假设您data.frame和您的过滤器以相同的顺序具有相同的列/字段:

foo <- data.frame(Period=sample(x=c("2010-12-31","2011-01-01"),size=100,replace=TRUE),
    Size=sample(x=c("S","L","VL"),size=100,replace=TRUE),
    Number=sample(x=c("9","11","21"),size=100,replace=TRUE))

all.filters <- list(
    Period=c("2010-12-31","2011-03-31"),
    Size=c("L","VL"),
    Number=c("11","21","35"))

然后我们需要针对第一个过滤器条目应用%in%到第一列,foo针对第二个条目应用第二列,依此类推:

bar <- mapply(FUN='%in%',foo,all.filters)

foo最后,我们提取所有过滤器匹配的那些行:

foo[apply(bar,1,all),]
于 2012-10-24T22:38:28.623 回答
0

看起来您想要的是与任何过滤器选项匹配的数据?所以"L"或者"VL",例如,无论时期如何?

在这种情况下,我会这样做:

Filtered.Data <- subset(original.data, Period %in% all.filters$Period | 
            Size %in% all.filters$Size | Number %in% all.filters$Number)

根本不应该花很长时间。如果您想要与所有这些值匹配的数据,请明显替换|&。如果你有很多过滤器类别,你可以做一个for循环和rbind废话。

于 2012-10-24T22:30:25.053 回答