Sur*_*ali 13 r subset dataframe
我有下表:
FN LN LN1 LN2 LN3 LN4 LN5
a b b x x x x
a c b d e NA NA
a d c a b x x
a e b c d x e
Run Code Online (Sandbox Code Playgroud)
我正在过滤LN1到LN5中存在LN的记录.
我用过的代码:
testFilter = filter(test, LN %in% c(LN1, LN2, LN3, LN4, LN5))
Run Code Online (Sandbox Code Playgroud)
结果不是我所期望的:
ï..FN LN LN1 LN2 LN3 LN4 LN5
1 a b b x x x x
2 a c b d e <NA> <NA>
3 a d c a b x x
4 a e b c d x e
Run Code Online (Sandbox Code Playgroud)
我明白这c(LN1, LN2, LN3, LN4, LN5)给了:"b" "b" "c" "b" "x" "d" "a" "c" "x" "e" "b" "d" "x" NA "x" "x" "x" NA "x" "e"并且知道这就是错误所在.
理想情况下,我只想返回第1和第4条记录.
FN LN LN1 LN2 LN3 LN4 LN5
a b b x x x x
a e b c d x e
Run Code Online (Sandbox Code Playgroud)
我想只使用列名过滤它们.这只是5.4M记录的一个子集.
使用申请:
# data
df1 <- read.table(text = "
FN LN LN1 LN2 LN3 LN4 LN5
a b b x x x x
a c b d e NA NA
a d c a b x x
a e b c d x e", header = TRUE, stringsAsFactors = FALSE)
df1[ apply(df1, 1, function(i) i[2] %in% i[3:7]), ]
# FN LN LN1 LN2 LN3 LN4 LN5
# 1 a b b x x x x
# 4 a e b c d x e
Run Code Online (Sandbox Code Playgroud)
注意:考虑使用下面的其他解决方案来处理大数据集,这可能比此应用解决方案快60倍.
有一种替代方法使用data.table和Reduce():
library(data.table)
cols <- paste0("LN", 1:5)
setDT(test)[test[, .I[Reduce(`|`, lapply(.SD, function(x) !is.na(x) & LN == x))],
.SDcols = cols]]
Run Code Online (Sandbox Code Playgroud)
Run Code Online (Sandbox Code Playgroud)FN LN LN1 LN2 LN3 LN4 LN5 1: a b b x x x x 2: a e b c d x e
library(data.table)
test <- fread(
"FN LN LN1 LN2 LN3 LN4 LN5
a b b x x x x
a c b d e NA NA
a d c a b x x
a e b c d x e")
Run Code Online (Sandbox Code Playgroud)
library(data.table)
library(dplyr)
n_row <- 1e6L
set.seed(123L)
DT <- data.table(
FN = "a",
LN = sample(letters, n_row, TRUE))
cols <- paste0("LN", 1:5)
DT[, (cols) := lapply(1:5, function(x) sample(c(letters, NA), n_row, TRUE))]
DT
df1 <- as.data.frame(DT)
bm <- microbenchmark::microbenchmark(
zx8754 = {
df1[ apply(df1, 1, function(i) i[2] %in% i[3:7]), ]
},
eric = {
df1[ which(df1$LN == df1$LN1 |
df1$LN == df1$LN2 |
df1$LN == df1$LN3 |
df1$LN == df1$LN4 |
df1$LN == df1$LN5), ]
},
uwe = {
DT[DT[, .I[Reduce(`|`, lapply(.SD, function(x) !is.na(x) & LN == x))],
.SDcols = cols]]
},
axe = {
filter_at(df1, vars(num_range("LN", 1:5)), any_vars(. == LN))
},
jaap = {df1[!!rowSums(df1$LN == df1[, 3:7], na.rm = TRUE),]},
times = 50L
)
print(bm, "ms")
Run Code Online (Sandbox Code Playgroud)
Run Code Online (Sandbox Code Playgroud)Unit: milliseconds expr min lq mean median uq max neval cld zx8754 3120.68925 3330.12289 3508.03001 3460.83459 3589.10255 4552.9070 50 c eric 69.74435 79.11995 101.80188 83.78996 98.24054 309.3864 50 a uwe 93.26621 115.30266 130.91483 121.64281 131.75704 292.8094 50 a axe 69.82137 79.54149 96.70102 81.98631 95.77107 315.3111 50 a jaap 362.39318 489.86989 543.39510 544.13079 570.10874 1110.1317 50 b
对于1M行,硬编码子集最快,其次是data.table/ Reduce()和dplyr/ filter_at方法.使用apply()速度慢60倍.
ggplot(bm, aes(expr, time)) + geom_violin() + scale_y_log10() + stat_summary(fun.data = mean_cl_boot)
Run Code Online (Sandbox Code Playgroud)
不是最简单的代码,而是
df1[ which(df1$LN == df1$LN1 |
df1$LN == df1$LN2 |
df1$LN == df1$LN3 |
df1$LN == df1$LN4 |
df1$LN == df1$LN5), ]
#> FN LN LN1 LN2 LN3 LN4 LN5
#> 1 a b b x x x x
#> 4 a e b c d x e
Run Code Online (Sandbox Code Playgroud)
一个快速而简单的dplyr解决方案:
filter_at(df1, vars(num_range("LN", 1:5)), any_vars(. == LN))
Run Code Online (Sandbox Code Playgroud)
这与@EricFail的硬编码答案在性能上非常相似,因为这只是在内部将调用扩展为:
filter(df1, (LN1 == LN) | (LN2 == LN) | (LN3 == LN) | (LN4 == LN) | (LN5 == LN))
Run Code Online (Sandbox Code Playgroud)
可以在其中使用而不是num_range任何其他select帮助程序,vars以根据其名称轻松选择许多变量.或者可以直接给出列位置.
你也可以使用rowSums:
df1[!!rowSums(df1$LN == df1[, 3:7], na.rm = TRUE),]
Run Code Online (Sandbox Code Playgroud)
这使:
Run Code Online (Sandbox Code Playgroud)FN LN LN1 LN2 LN3 LN4 LN5 1 a b b x x x x 4 a e b c d x e