【问题标题】:How to create vector in R when you don't know length ahead of time?当您提前不知道长度时如何在 R 中创建向量?
【发布时间】:2016-11-30 07:42:33
【问题描述】:

I have read 在 R 中附加到向量是不好的做法。那么,如果我想创建一个向量,但我提前不知道它的长度,我该怎么办?

我正在查看一个数据框,其中包含有关人们何时靠近特定位置的条目。每个条目都包含有关此人的信息以及附近的时间,但一个人可以有多个条目。

#    loc  id        time
# 1:   z   A       00:00
# 2:   z   A       00:01
# 3:   z   B       00:02
# 4:   z   A       00:02
# 5:   z   C       00:05
# 6:   z   C       00:07
# 7:   z   A       00:08
# 8:   z   A       00:09
# 9:   z   C       00:09
#10:   z   C       00:10

我想创建一个新的数据框,其中每个条目都是一个人的“访问”,整理来自一个人的任何时间接近的条目。

#    loc  id   starttime  endtime
# 1:   z   A       00:00   00:02
# 2:   z   C       00:05   00:07
# 3:   z   A       00:08   00:09
# 4:   z   C       00:09   00:10

它们可能是第一个数据框中单个人的 50 个条目,这些条目可能会被整理成新数据框中的 3 个“访问”。我不知道有多少“访问”。那么我应该如何创建这个数据框呢?

我知道 rbind,但在这种情况下,我会一一绑定每一行。这是个好主意吗?

另一种选择是遍历第一个数据框两次,一次是计算第二个数据框的大小,然后再次填充它,但这似乎效率更低。

【问题讨论】:

  • 你只是这样做,然后附加到它。动态语言就是这样做的。这在大型列表中更重要,因为需要复制的大型列表正在扩展。更多的小向量和值 R 已经在你背后过度分配了。
  • 这听起来有点像您只想meltfilter...虽然没有具体的数据示例,但很难知道。
  • 我建议你第 2 章,“Growing Objects”,这正是你所要求的 :) burns-stat.com/pages/Tutor/R_inferno.pdf 他建议的解决方案是 @Roland 提出的解决方案,但他也用系统分析了其他方法用于执行某些任务的时间(按块增长 vs. rbind vs. 下标)。你cn

标签: r vector dataframe append


【解决方案1】:

我不相信您需要这个(对于您描述不佳的实际问题可能有更好的解决方案),但我会在第一段中回答这个问题。如果您不知道结果向量需要多大,请将其初始化为合理的大小并根据需要将其增长为块。这限制了向量需要增长的时间。

set.seed(42)
vec <- numeric(100) #initialize a chunk
i <- 0

repeat {
  test <- rnorm(1)
  if (test > 3) break
  i <- i + 1
  #grow in chunks:
  if (length(vec) < i) vec <- c(vec, numeric(100)) 
  vec[i] <- test
}

#shorten to final length
vec <- vec[seq_len(i)]

您实际上在现实生活中会做类似的事情。如果你买了一个新书架,那么你买的书架就足够大,这样你就有足够的空间来购买未来的书了。当它满了,你买下一个(或更大的)。

【讨论】:

  • 我从未见过repeat 使用过——非常优雅!
【解决方案2】:

这并没有明确回答您的问题,而是演示了如何使用cut 创建您想要的数据来创建“访问”,然后计算唯一的访问次数。

library(data.table)
set.seed(1234)
dat <- data.table(visit_time = sample(20, 100, replace = TRUE), 
                  id = sample(LETTERS[1:5], 100, replace = TRUE))
dat[ , visit := cut(visit_time, breaks = seq(0, 20, 5))]
dat[ , list(nvisits = length(unique(visit))), by = id]
#    id nvisits
# 1:  A       4
# 2:  C       4
# 3:  B       4
# 4:  D       4
# 5:  E       4

运行以下显示他们在同一时间跨度/访问中在该位置的次数:

dat[ , .N, by = list(id, visit)]
#     id   visit N
# 1:   A   (0,5] 6
# 2:   C (10,15] 5
# 3:   B (10,15] 6
# 4:   A (15,20] 3
# 5:   A (10,15] 5
# 6:   D (10,15] 6
# 7:   E  (5,10] 7
# 8:   B  (5,10] 6
# 9:   E (15,20] 4
# 10:  D   (0,5] 6
# 11:  D  (5,10] 4
# 12:  E   (0,5] 9
# 13:  C   (0,5] 4
# 14:  B (15,20] 1
# 15:  C (15,20] 9
# 16:  B   (0,5] 6
# 17:  A  (5,10] 2
# 18:  C  (5,10] 5
# 19:  D (15,20] 2
# 20:  E (10,15] 4

编辑以显示剪切功能如何随时间工作:

我从this excellent answer 中获取了randTime 函数。

randTime <- function(N, st, et) {
  st <- as.POSIXct(st)
  et <- as.POSIXct(et)
  dt <- as.numeric(difftime(et,st,unit="sec"))
  ev <- sort(runif(N, 0, dt))
  rt <- st + ev
  rt
}

set.seed(1234)
st <- as.POSIXct("2012/01/01 12:00")
et <- as.POSIXct("2012/01/01 18:00")
dat2 <- data.table(visit_time = randTime(100, st, et), 
                  id = sample(LETTERS[1:5], 100, replace = TRUE))
dat2[ , visit := as.character(cut(visit_time, breaks = seq(st, et, "15 min")))]
dat2[ , length(unique(visit)), by = id]
#    id V1
# 1:  A 11
# 2:  C 13
# 3:  B 14
# 4:  D 14
# 5:  E 14

【讨论】:

  • 是的。我可以编辑答案给你看,但@Roland 确实回答了你提出的问题。
  • 即无论您是否使用我提供的解决方案,您都应该真正接受他的回答。
猜你喜欢
  • 1970-01-01
  • 2016-06-18
  • 2011-02-01
  • 1970-01-01
  • 2023-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多