如何在 sapply 函数上放置一个循环来为多个参与者创建一个子集?

How to put a loop on an sapply function to create a subset for multiple participants?

对于以下问题,我将不胜感激:

我有多个巨大的日志文件(每个都超过 1.000.000 个条目),其中包含一些我特别感兴趣的行(行)。所以我想制作一个仅包含这些行的子集,但我想将结果写入包含不止一条 Logfile/Participant 的信息的矩阵中。所以我创建了一小段代码来 1. 创建子集和 2. 运行 它在一个循环中,不仅对一个日志文件,而且对所有日志文件都这样做。

  Result <- subset(df, df$columnOfInterest== "interestingCondition1" | df$columnOfInterest== "interestingCondition2" | df$columnOfInterest== "interestingCondition3")$columnOfInterest
  View(Result)

1
interestingCondition1
2
interestingCondition1
3
interestingCondition2
4
interestingCondition1
5
interestingCondition1
6
interestingCondition3
7
interestingCondition2
8
interestingCondition1
9
interestingCondition1
10
interestingCondition1

嵌入循环:

WrongResult <- matrix(data=NA,nrow=TrialNumber, ncol=length(ListOfFiles))
vpncount <- 1
for (v in ListOfFiles){

  df<- read.delim(v, header = TRUE, sep='\t')
  WrongResult[,vpncount] <- subset(df, df$columnOfInterest== "interestingCondition1" | df$columnOfInterest== "interestingCondition2" | df$columnOfInterest== "interestingCondition3")$columnOfInterest

vpncount <- vpncount+1

}

当运行在一个日志文件上使用代码时,我得到了我想要的结果,但是当运行通过循环使用它时,它创建了一个具有适当大小的矩阵,但只是填充用 "random" 数字代替我细分的条件。

有谁知道为什么会这样以及如何解决?非常感谢任何帮助!

编辑:

我尝试创建一个示例数据框。第一行代码(包括变量 Results)就像我想要的那样工作。它在我的 columnOfInterest 的行上过滤我的数据框,并将它们放入一个新矩阵中。但是,如果我尝试在一个循环中 运行 它处理多个数据帧,我就会 运行 出错:

df <- data.frame(
  X = sample(1:10),
  columnOfInterest= sample(c("interestingCondition1", "interestingCondition2", "interestingCondition3", "NotinterestingCondition1"), 10, replace = TRUE)
)

View(df)

Result <- subset(df, df$columnOfInterest== "interestingCondition1" | df$columnOfInterest== "interestingCondition2" | df$columnOfInterest== "interestingCondition3")$columnOfInterest
View(Result)

WrongResult <- matrix(data=NA,nrow=280, ncol=20)
vpncount <- 1
for (v in 1:20){

  df<- read.delim(v, header = TRUE, sep='\t')
  WrongResult[,vpncount] <- subset(df, df$columnOfInterest== "interestingCondition1" | df$columnOfInterest== "interestingCondition2" | df$columnOfInterest== "interestingCondition3")$columnOfInterest

  vpncount <- vpncount+1

}

View(WrongResult)

我不记得如何使用 data.frame 进行操作,所以我将尝试使用 data.table。你可能需要安装 data.table 包以防你没有它 install.packages("data.table")

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

然后你可以用下面的方式重写你的代码

subset..table <- function(dt){
    dt[columnOfInterest %in% c("interestingCondition1",
                               "interestingCondition2",
                               "interestingCondition3"),columnOfInterest]
}


myfun <- function(x){
### DD
    ## x interp string representing  file name

### Purpose
    ## read and subset

    dt <- fread(x,header=TRUE,sep="\t")
    subset..table(dt)

}

res..list <- lapply(ListOfFiles, myfun)

编辑

例如使用您的示例。

df <- data.frame(
  X = sample(1:10),
  columnOfInterest= sample(c("interestingCondition1",
    "interestingCondition2", "interestingCondition3", 
    "NotinterestingCondition1"), 10, replace = TRUE))


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

会产生

#[1] "interestingCondition2" "interestingCondition3" "interestingCondition1"
#[4] "interestingCondition2" "interestingCondition1" "interestingCondition2"
#[7] "interestingCondition3" "interestingCondition1" "interestingCondition3"

如果您对函数subset..满意table,那么您只需使用函数myfun即可得到您想要的。函数 fread 会自动给你一个 data.table

在 tidyverse 领域,当您处理单个数据帧时,您希望 filter() 然后 select() 您的原始数据,为了方便添加,使用 mutate() , 文件名。当有多个可能值时,一种很好的过滤方法是使用 %in%。所以

library(tidyverse)

process_1_df <- function(df, id, condition)
    select(df, columnOfInterest) %>%                 # only interesting column
        filter(columnOfInterest %in% condition) %>%  # specific rows
        mutate(id = id)                              # add identifier

condition <- paste0("interestingCondition", 1:3)
process_1_df(df, "id", condition)

id 是一个标识符——如果 data.frame 来自文件 'foo.txt',则使用 "foo.txt" 作为 id。最初的问题试图将来自多个文件的数据表示为矩阵,但假设每个文件都选择了相同数量的有趣行。这里的策略是创建一个数据框,其中包含有趣条件的来源文件以及有趣条件的值。此数据框在处理多个文件时很有用...

这适用于示例数据集:

> condition <- paste0("interestingCondition", 1:3)
> process_1_df(df, "id", condition)
       columnOfInterest id
1 interestingCondition2 id
2 interestingCondition2 id
3 interestingCondition3 id
4 interestingCondition1 id
5 interestingCondition3 id
6 interestingCondition1 id

您可以扩展它来处理文件

process_1_file <- function(file_name, condition)
    read_csv(file_name) %>%                   # better: input only columnOfInterest
        process_1_df(file_name, condition)

正如@DJJ 所建议的,process_1_file() 的data.table 实现可能非常紧凑和高效——fread(file_name)[columnOfInterest %in% condition, columnOfInterest]

要处理多个文件,请使用 purr 包

library(purrr)
process_files <- function(file_names, condition)
    map(file_names, process_1_file, condition) %>%
        bind_rows()

dir(pattern="*.csv") %>% process_files(condition)

最终结果是单个数据框,其中有一列是有趣的条件,另一列指示有趣的条件来自哪个日志文件。现在可以根据需要处理/汇总此 'long' 格式的数据框。

Does anyone knows why that happens?

你的循环是……不工作。原因有点复杂,但我已经使用简单的循环(没有 *apply 函数)在 base R 中做了一个工作示例,希望你能跟进,并希望它能充分代表你的问题。

先学会走路 运行。在学习如何使用 apply()lapply() 等更简洁地完成循环之前,先学习基本循环。在深入研究非标准评估(data.tabletidyversepurrr 等)

首先我们将创建一些数据框并将它们写入文件

owd <- getwd()
dir.create("sotest")
setwd("sotest")

set.seed(1)

flist <- c("dtf1.txt", "dtf2.txt", "dtf3.txt")

for (i in 1:length(flist)) {
    dtf <- data.frame(
      X=sample(1:10),
      coi=sample(c("ic1", "ic2", "ic3", "nic1"), 10, replace=TRUE)
    )
    write.table(dtf, flist[i], row.names=FALSE, sep="\t")
}

在 运行 之后,您应该有一个名为 "sotest" 的文件夹,其中包含三个制表符分隔的 txt 文件。

然后我们将获得可用文件的列表,并对其进行循环。

flist <- list.files(pattern=".txt")
WrongResult <- list()
interesting <- c("ic1", "ic2", "ic3")

for (v in 1:length(flist)) {

    dtf <- read.delim(flist[v], header=TRUE, sep="\t", stringsAsFactors=FALSE)
    WrongResult[[v]] <- dtf[dtf$coi %in% interesting, "coi"]

}

WrongResult

setwd(owd)

我将输出存储为列表而不是矩阵,因为在循环的每次迭代中生成的对象的长度都不相同。