R数据帧的高效过滤(用户定义的不同过滤器)

R数据帧的高效过滤(用户定义的不同过滤器),r,filtering,performance,R,Filtering,Performance,我希望在R中过滤一个数据框,original.data。这个数据框可以有大约100-200万个观察值。dataframe有几个字段,名称可能会有所不同。用户可以选择要根据哪些字段进行筛选。这些字段名存储在名称(all.filters)中,其中all.filters是可变长度的列表。然后,用户可以选择名称(all.filters)中每个字段的级别。例如,此列表可能类似于: > all.filters $Period [1] "2010-12-31" "2011-03-31" "2011-06

我希望在R中过滤一个数据框,original.data。这个数据框可以有大约100-200万个观察值。dataframe有几个字段,名称可能会有所不同。用户可以选择要根据哪些字段进行筛选。这些字段名存储在名称(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,]
附加(原始数据)

filter.names看起来您想要的是与任何筛选器选项匹配的数据?比如说,不管时间长短,是“L”还是“VL”

在这种情况下,我只想:

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

Filtered.Data一种稍微通用的方法,它不依赖于对字段名称进行硬编码:假设您的
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"))
最后,我们提取所有过滤器匹配的
foo
行:

foo[apply(bar,1,all),]

您可以使用带有适当设置键的
data.table
。这将节省内存

然后,您可以将过滤器的
列表
传递给
[.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
你也可以这样做

 setkeyv(DT, names(all_filters))

 DT[do.call(CJ,all_filters)]

干杯,这里的%in%运算符很有用。但是,您可能会注意到,在我的代码中,我没有直接提到句点、大小和数字。这是因为all.filter是由另一个用户定义的。在他们更新过滤器后,代码应该自动应用它们,也就是说,无需显式键入变量的名称。我可以执行以下操作:以下内容:粘贴(“子集(原始.data)”,获取(过滤器),%in%all.filters[[filter]]),sep=“”)然而,我仍然需要做一个循环来应用每个可变数量的过滤器,然后找到结果数据集的交集。干得好!我需要做的唯一更改是:filtered.data+1-你提到的假设不是一个:只要做
foo@flodel:如果我错了,请纠正我,但是
rowSums(bar)>0
将选择至少一个筛选器命中的行,而不是所有筛选器(相当于
任何
),对吗?我现在已经很晚了……但无论如何,谢谢你的支持!你是对的,我编辑了我的评论。我还想说,
rowSums
将比
apply
@flodel:对我担心的警告做了很好的补充,+1感谢你的帮助。起初我认为这是完美的-高效的和优雅的-然而,存在一个问题。在列表
所有\u过滤器
中,
期间
大小
都有一个级别。当
所有\u过滤器的两个或多个元素都有多个级别时(例如,如果
所有\u过滤器
与上面相同,除了
大小=c(“L”,“XL”)
),此代码不起作用。这种过滤方法速度非常快,所以我将尝试解决这个问题。希望我能解决如何在没有循环的情况下进行过滤。谢谢。我假设有某种方法可以获得交叉连接而不是相等连接(这是正确的术语吗?)。这很有效!
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)]