根据开始和结束索引(保存在两个向量中)并使用条件将 data.frame 拆分为较小的 data.frame

Posted

技术标签:

【中文标题】根据开始和结束索引(保存在两个向量中)并使用条件将 data.frame 拆分为较小的 data.frame【英文标题】:Split a data.frame into smaller data.frames, based on the start and end indices (held in two vectors) and using a condition 【发布时间】:2020-06-08 10:30:39 【问题描述】:

我正在尝试编写一种算法,该算法允许将包含 100000 行的“ALL_DATA”数据帧划分为三列($Date、$Time、$Value),并根据标准划分为较小的 data.frames 和一系列开始和结束索引(包含在相同长度的“START”和“END”向量中(int [1: 500])。

例如,我用于计算的 data.frame 是:

ALL_DATA:
     $Date       $Time       $Value
[1]  01/01/14    0:10:00     0.45
[2]  01/01/14    0:20:00     1.00
[3]  01/01/14    0:30:00     1.20
[4]  01/01/14    0:40:00     0.10
[5]  01/01/14    0:50:00     1.00
[6]  01/01/14    1:00:00     0.21
[7]  01/01/14    1:10:00     0.18
[8]  01/01/14    1:20:00     0.19
[9]  01/01/14    1:30:00     1.12
[10] 01/01/14    1:40:00     0.50
[11] 01/01/14    1:50:00     0.62
[12] 01/01/14    2:00:00     0.55
[13] 01/01/14    2:10:00     0.80
[14] 01/01/14    2:20:00     1.12
[15] 01/01/14    2:30:00     2.15
 ... 

虽然我的两个向量包含引用 data.frame 的索引,例如这些:

START:
[1] 2
[2] 5
[3] 9
[4] 12
...

END:
[1] 3
[2] 8
[3] 11
[4] 15
...

此时,我想应用两个 START 和 END 索引之间的 $Value 元素之和为 >= 2 的条件,然后创建以下 data.frames:

SPLIT_DATA_FRAME "001": (the sum of the values is infact 2.20 > 2)
     $Date       $Time       $Value
[2]  01/01/14    0:20:00     1.00
[3]  01/01/14    0:30:00     1.20

SPLIT_DATA_FRAME "002": (the sum of the values is infact 2.24 > 2)
     $Date       $Time       $Value
[9]  01/01/14    1:30:00     1.12
[10] 01/01/14    1:40:00     0.50
[11] 01/01/14    1:50:00     0.62

SPLIT_DATA_FRAME "003": (the sum of the values is infact 4.62 > 2)
     $Date       $Time       $Value
[12] 01/01/14    2:00:00     0.55
[13] 01/01/14    2:10:00     0.80
[14] 01/01/14    2:20:00     1.12
[15] 01/01/14    2:30:00     2.15

编辑:正如@Roland 所建议的那样,我尝试通过以下方式使用剪切和拆分功能:

split(ALL_DATA, cut(ALL_DATA$Value, breaks = unique(c(rbind(START, END)))))
cond <- sapply(split_ALL_DATA, function(DF) sum(DF$ALL_DATA$Value) >= 2)
split_ALL_DATA <- split_ALL_DATA[cond]

但我得到的结果是一组 data.frames 划分但不符合我的 START 和 END 索引向量,并且具有与原始 data.frame 相同的结构但内部没有值:

$`(2,3]`
[1] Date  Time    Value
<0 rows> (or 0-length row.names)

$`(3,5]`
[1] Date  Time    Value
<0 rows> (or 0-length row.names)

$`(5,8]`
[1] Date  Time    Value
<0 rows> (or 0-length row.names)

你能告诉我哪里错了吗?这是否取决于我的数据结构不仅包含数字数据,还包含日期和时间这一事实?非常感谢。

【问题讨论】:

使用cut根据您的开始和结束索引将值分配给组,然后使用split 非常感谢您的帮助,所以在 for 循环中使用 cut 命令我会得到:for (i in 1: length (START)) check [i] &lt;- cut (ALL_DATA $ ValUE, breaks = START [i]: END [i]) if (sum (check [i]&gt; 4)) "create data.frame and save in .dat file" 我的程序是否正确?然后我在哪里插入拆分?再次感谢 根据上面的 ALL_DATA 输出和示例子集中的日期/时间变量,索引应该是行索引还是值本身的括号索引?前者与您的示例子集匹配。如果是后者,那么我最初的想法为什么您的子集数据框为空是因为您的Values 不够高,因此范围为空。 感谢您的评论,索引是行索引,它定义了一系列值以剪切主 data.frame,我手动检查了是否有足够高的值来满足条件。不幸的是,我无法得到任何结果,您能给我一些建议吗?非常感谢编辑:但是,我刚刚意识到我在上面的示例中插入了错误的条件,我立即更正,谢谢 因此,第一次拆分时,上面的第 2 行和第 3 行的值分别为 0.20 和 0.01。但是您的第一个条件拆分列出了 1.00 和 1.20 的值。除非我遗漏了什么,否则哪一个是正确的? 【参考方案1】:

根据对行索引的初始评论的回答,并使用类似的 3 部分方法(如 @Roland),以下应该是您想要的。

这将创建一个通用函数来返回从“开始”到“结束”的所有行(假设提供的元素是整数)

split_data <- function( start, end, dfr )
  dfr[start:end,]

这将创建所有可用拆分的列表。

split.frames <- mapply(split_data,START,END,MoreArgs=list(dfr=ALL_DATA))

如果ith 拆分满足所需条件,这将返回一个逻辑向量,其中ith 元素等于TRUE。

cond <- sapply( split.frames, function(x)sum(x$Value)>=2 )

这仅返回满足条件的拆分。

split.frames <- split.frames[cond]

编辑#1

根据关于保存拆分的评论,最好使用 R 包 stringr 中的 str_pad() 函数来创建文件名,但这里是一个基本的 R 实现这应该适合你。

nchars <- nchar( length(split.frames) )

print.expr <- paste0("%0",nchars,"d")

for( i in 1:seq_along(split.frames) )
  file.i <- paste0( sprintf(print.expr,i), ".dat" )
  write.table( split.frames[[i]], file=file.i, sep="\t", row.names=FALSE )

不确定是否要在保存的输出中使用列名和/或行名,但我认为它们分别是 YES 和 NO。

【讨论】:

它有效,只是这样我得到一个唯一的列表,其中每三行(对应于日期、时间和值)我想创建一个单独的 data.frame 以便我可以将其保存在文本文件中。你能帮我把这个“大”列表变成data.frames列表吗?非常感谢您的帮助! 我认为我可以使用 for 循环构建 data.frame 并在每个循环中将其保存在一个文件中。这就是想法,尽管我必须了解如何一次考虑“split.frames”列表的三行,并且我仍然必须了解如何保存在文件中。 FRAME=NULL for (k in 1:length(split.frames)) DATA &lt;- split.frames[[k]] ORA &lt;- split.frames[[k+1]] VALUE &lt;- split.frames[[k+2]] FRAME &lt;- rbind(FRAME, data.frame(DATA,ORA,VALUE)) "save FRAME in .txt" 虽然它没有包含在 OP 中,但您希望文件如何命名以及文件类型是什么? 我真的很感谢你的帮助@statsetw,我需要按数字顺序(001、002、003、004,...)调用 data.frames 并将它们保存在.dat 文件以制表符分隔。再次感谢。 我检查了您的答案是否正确,即使我错过了保存数据帧的最后一部分,即使我的问题中没有明确要求。再次感谢您的帮助。【参考方案2】:

我以iris 数据集(来自数据集包)为例。

head(iris)
#  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#1          5.1         3.5          1.4         0.2  setosa
#2          4.9         3.0          1.4         0.2  setosa
#3          4.7         3.2          1.3         0.2  setosa
#4          4.6         3.1          1.5         0.2  setosa
#5          5.0         3.6          1.4         0.2  setosa
#6          5.4         3.9          1.7         0.4  setosa

假设我们要为 [-Inf, 5], (5, 6], (6, 7], (7, Inf] 中的每个萼片长度间隔创建一个 data.frame。那么我们可以这样做:

split_iris <- split(iris, cut(iris$Sepal.Length, breaks = c(-Inf, 5:7, Inf)))

然后你可以检查条件:

cond <- sapply(split_iris, function(DF) sum(DF$Petal.Width) > 20)

并将其用于子集:

split_iris <- split_iris[cond]

然后您可以遍历列表以导出到文件(使用for 循环或lapply 循环)。

【讨论】:

再次感谢您的帮助,我正在尝试使其适应我的情况,但我想请您澄清一下,我是否必须插入您在循环中指示的步骤?例如“split_iris [i]”并用作中断 = START [i]: END [i]?非常感谢 我问你这个问题,因为我有两个 500 元素的 START 和 END 向量,它们分别包含要剪切数据的开始和结束索引。非常感谢你的帮助,如果我问你很多事情,我很抱歉。 我试着像这样按照你的指示进行操作:split_ALL_DATA &lt;- split(ALL_DATA, cut(ALL_DATA$Value, breaks = START:END)) 问题是我必须插入包含索引的两个 START 和 END 向量,只有这两个向量的前两个索引在我希望为所有索引对剪切 data.frame 时使用,因此我认为我必须在 for 循环中插入您的指示。你能帮助我吗?非常感谢您,给您带来的不便深表歉意。 我不知道STARTEND 包含什么。也许这个? split(ALL_DATA, cut(ALL_DATA$Value, breaks =unique(c(rbind(START, END)))) 我已经修改了我最初的问题以使其更清晰,并包含了我的示例向量,希望能帮助您理解问题。感谢您的热心帮助。

以上是关于根据开始和结束索引(保存在两个向量中)并使用条件将 data.frame 拆分为较小的 data.frame的主要内容,如果未能解决你的问题,请参考以下文章

向量和Ifelse逻辑 - 不会填充向量

SQL根据开始和结束时间对满足条件的时间序列进行分组

返回二维向量的结束索引

Mongoose / typegoose 根据开始和结束索引获取数组

数组(向量)中大于某个值的元素的起始索引和结束索引

文本搜索功能