簡體   English   中英

根據特定值過濾data.frame的每一列

[英]Filter each column of a data.frame based on a specific value

請考慮以下數據框:

df <- data.frame(replicate(5,sample(1:10,10,rep=TRUE)))

#   X1 X2 X3 X4 X5
#1   7  9  8  4 10
#2   2  4  9  4  9
#3   2  7  8  8  6
#4   8  9  6  6  4
#5   5  2  1  4  6
#6   8  2  2  1  7
#7   3  8  6  1  6
#8   3  8  5  9  8
#9   6  2  3 10  7
#10  2  7  4  2  9

使用dplyr ,如何對每個大於2的值篩選每個列(不隱式命名它們)。

一些模仿假設的filter_each(funs(. >= 2))

現在我正在做:

df %>% filter(X1 >= 2, X2 >= 2, X3 >= 2, X4 >= 2, X5 >= 2)

這相當於:

df %>% filter(!rowSums(. < 2))

注意:假設我想只過濾前4列,我會這樣做:

df %>% filter(X1 >= 2, X2 >= 2, X3 >= 2, X4 >= 2) 

要么

df %>% filter(!rowSums(.[-5] < 2))

會有更有效的替代方案嗎?

編輯:子問題

如何指定列名並模仿假設的filter_each(funs(. >= 2), -X5)

基准子問題

由於我必須在大型數據集上運行此操作,因此我對這些建議進行了基准測試。

df <- data.frame(replicate(5,sample(1:10,10e6,rep=TRUE)))

mbm <- microbenchmark(
Marat = df %>% filter(!rowSums(.[,!colnames(.) %in% "X5", drop = FALSE] < 2)),
Richard = filter_(df, .dots = lapply(names(df)[names(df) != "X5"], function(x, y) { call(">=", as.name(x), y) }, 2)),
Docendo = df %>% slice(which(!rowSums(select(., -matches("X5")) < 2L))),
times = 50
)

結果如下:

#Unit: milliseconds
#    expr       min        lq      mean    median       uq      max neval
#   Marat 1209.1235 1320.3233 1358.7994 1362.0590 1390.342 1448.458    50
# Richard 1151.7691 1196.3060 1222.9900 1216.3936 1256.191 1266.669    50
# Docendo  874.0247  933.1399  983.5435  985.3697 1026.901 1053.407    50

在此輸入圖像描述

這是一個讓選擇名稱變得相當簡單的想法。 您可以設置要發送到filter_().dots參數的調用列表。 首先是創建未評估調用的函數。

Call <- function(x, value, fun = ">=") call(fun, as.name(x), value)

現在我們使用filter_()通過調用到列表.dots參數使用lapply()選擇您想要的任何名稱和值。

nm <- names(df) != "X5"
filter_(df, .dots = lapply(names(df)[nm], Call, 2L))
#   X1 X2 X3 X4 X5
# 1  6  5  7  3  1
# 2  8 10  3  6  5
# 3  5  7 10  2  5
# 4  3  4  2  9  9
# 5  8  3  5  6  2
# 6  9  3  4 10  9
# 7  2  9  7  9  8

您可以查看Call()創建的未評估調用,例如X4X5

lapply(names(df)[4:5], Call, 2L)
# [[1]]
# X4 >= 2L
#
# [[2]]
# X5 >= 2L

所以,如果你調整names()X的參數lapply()你應該罰款。

如何指定列名並模仿假設的filter_each(funs(。> = 2), - X5)?

它可能不是最優雅的解決方案,但它完成了工作:

df %>% filter(!rowSums(.[,!colnames(.)%in%'X5',drop=F] < 2))

如果有多個排除列(例如X3,X5),可以使用:

df %>% filter(!rowSums(.[,!colnames(.)%in%c('X3','X5'),drop=F] < 2))

這是slice的另一個選項,在這種情況下可以類似地用於filter 主要區別在於您為slice提供整數向量,而filter則采用邏輯向量。

df %>% slice(which(!rowSums(select(., -matches("X5")) < 2L)))

我喜歡這種方法的原因是因為我們在rowSums使用select ,你可以使用所有select耗材的特殊函數,例如matches


讓我們看看它與其他答案的比較:

df <- data.frame(replicate(5,sample(1:10,10e6,rep=TRUE)))

mbm <- microbenchmark(
    Marat = df %>% filter(!rowSums(.[,!colnames(.) %in% "X5", drop = FALSE] < 2)),
    Richard = filter_(df, .dots = lapply(names(df)[names(df) != "X5"], function(x, y) { call(">=", as.name(x), y) }, 2)),
    dd_slice = df %>% slice(which(!rowSums(select(., -matches("X5")) < 2L))),
    times = 50L,
    unit = "relative"
)

#Unit: relative
#     expr      min       lq   median       uq      max neval
#    Marat 1.304216 1.290695 1.290127 1.288473 1.290609    50
#  Richard 1.139796 1.146942 1.124295 1.159715 1.160689    50
# dd_slice 1.000000 1.000000 1.000000 1.000000 1.000000    50

PIC

編輯注釋:使用更可靠的基准更新,重復50次(時間= 50L)。


根據評論,基礎R將具有與slice方法相同的速度(沒有指定基本R方法的確切含義),我決定使用與我的答案中幾乎相同的方法與基礎R進行比較來更新我的答案。 對於使用的基本RI:

base = df[!rowSums(df[-5L] < 2L), ],
base_which = df[which(!rowSums(df[-5L] < 2L)), ]

基准測試:

df <- data.frame(replicate(5,sample(1:10,10e6,rep=TRUE)))

mbm <- microbenchmark(
  Marat = df %>% filter(!rowSums(.[,!colnames(.) %in% "X5", drop = FALSE] < 2)),
  Richard = filter_(df, .dots = lapply(names(df)[names(df) != "X5"], function(x, y) { call(">=", as.name(x), y) }, 2)),
  dd_slice = df %>% slice(which(!rowSums(select(., -matches("X5")) < 2L))),
  base = df[!rowSums(df[-5L] < 2L), ],
  base_which = df[which(!rowSums(df[-5L] < 2L)), ],
  times = 50L,
  unit = "relative"
)

#Unit: relative
#       expr      min       lq   median       uq      max neval
#      Marat 1.265692 1.279057 1.298513 1.279167 1.203794    50
#    Richard 1.124045 1.160075 1.163240 1.169573 1.076267    50
#   dd_slice 1.000000 1.000000 1.000000 1.000000 1.000000    50
#       base 2.784058 2.769062 2.710305 2.669699 2.576825    50
# base_which 1.458339 1.477679 1.451617 1.419686 1.412090    50

PIC2

與這兩種基本R方法相比,沒有任何更好或相當的性能。

編輯注釋#2:添加基准R選項的基准。

如果您只想過濾前四列,請執行以下操作:

df %>% filter(X1 >= 2, X2 >= 2, X3 >= 2, X4 >= 2) 

...嘗試這個:

df %>% 
  filter_at(vars(X1:X4), #<Select columns to filter
  all_vars(.>=2) )       #<Scope with all_vars (or any_vars)

另一種方法是排除您要過濾的列,如下所示:

df %>% 
  filter_at(vars(-X5)), #<Exclude column X5
  all_vars(.>=2) )

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM