【问题标题】:how to avoid loops如何避免循环
【发布时间】:2011-03-14 14:58:53
【问题描述】:

大家好, 我是 R 新手。

我有两个面板数据文件,列“id”、“date”和“ret”

文件 A 的数据比文件 B 多得多, 但我主要处理的是文件 B 数据。

“id”和“date”的组合是唯一标识符。

是否有一种优雅的方式来查找 B 中的每个 (id, date),我需要从文件 A 中获取过去 10 天的 ret,并将它们存储回 B 中?

我的天真做法是循环 B 中的所有行,

for i in 1:length(B) {
    B$past10d[i] <- prod(1+A$ret[which(A$id == B$id[i] & A$date > B$date[i]-10 & A$date < B$date[i])])-1
}

但循环需要永远。

非常感谢您的想法。

非常感谢。

【问题讨论】:

  • K:供将来参考:如果您的代码与您提出的问题的文本匹配,这对于试图帮助您的人来说非常有用。
  • 对不起。感谢您的帮助
  • 它是否匹配都可以解释。代码只是一个说明......像往常一样。

标签: r loops vectorization


【解决方案1】:

这样更快吗? (我假设 B$id 和 B$date 的组合是一个唯一标识符,不会在任何地方复制 - 您的代码暗示)

B$idDate <- factor(B$id):factor(B$date)
B$past10 <- sapply(B$idDate, function(x){with(B[B$idDate == x,], 
    prod(1+A$ret[A$id == id & A$date > date-10 & A$date < date])-1)})

【讨论】:

  • 感谢您的回复约翰。它看起来非常整洁。但是当我尝试数据时,我得到了:错误:无法为 B$past10 的家伙分配大小为 6.8 Mb 的向量太多工作?
  • 这不应该比你的方法占用更多的内存。尝试使用 ls() 和 rm() 命令清除内存中的一些内容。或者,尝试重新启动 R 并再次执行。
  • tks!我会尝试的..但我忘了提到我的天真方法从未完成循环..:S
  • 如果 A 非常大,有很多日期,尤其是 id,不在 B 中,那么您可能想看看我的其他答案。
【解决方案2】:

您尝试过 ?merge 吗?

“通过共同的列名或行名合并两个数据框,或者做其他版本的数据库连接操作。”

此外,如果您经常使用复合 PK 或其他任何唯一标识符,我建议使用一个小的本地 MySQL / PostgreSQL (RMySQL / RPostgreSQL) 数据库。对我来说,对数据进行 SQL 重新排列,然后从视图中使用 data.frames 比循环容易得多。

【讨论】:

  • RMySQL 更容易上手……
【解决方案3】:

如果您没有在 A 和 B 中复制的数据,那么rbind 是最简单的解决方案。

#Sample data
A <- data.frame(
  id = rep(letters[1:3], each = 13),
  date = Sys.Date() + -12:0,
  ret = runif(39)
)

B <- data.frame(
  id = rep(letters[5:6], each = 5),
  date = Sys.Date() + -4:0,
  ret = runif(10)
)

#Only take the last ten days from A
A_past_10_days <- A[A$date > Sys.Date() - 10,]

#Bind by rows
rbind(A_past_10_days, B)

【讨论】:

  • 您从今天开始计算过去 10 天,但他的代码从 B 中每个可能的日期开始计算过去 10 天,该日期的标识符限定。
  • @John:很好看。我似乎已经回答了问题的文字,但没有回答隐藏在代码中的问题。 叹息
【解决方案4】:

一般来说,你应该避免在 R 中循环。如果你的代码对向量进行操作会更快。

我会按照 ran2 的建议使用合并。您可以设置all.x = T(或all.yall)从一个(或其他或两者)数据帧中获取所有行。这很快,通常会自行确定要匹配的字段。否则,您需要指定by.x(和by.yby)作为查找字段。根据它的声音,您可能需要自己创建这个字段(根据 John 的评论)。

然后您可以按日期过滤。

【讨论】:

  • tks! B 实际上是 A 的一个小得多的子集,所以如果我正确理解“合并”,那么 A 将是合并的结果。
  • 这会比你原来的解决方案占用更多的内存。鉴于您的建议达到了内存限制,这不太可能奏效。此外,所有这一切都是为了让您将数据集中到一个地方。它不能解决您将过去 10 天转换为单个值的问题...您的代码表明这是必要的。
  • 啊!我的错。我以为您正在从 A 到 B 获取一组行。我没有意识到您实际上想要总结产品。您可能还对聚合(sapply 的便利功能)感兴趣。如果您遇到记忆问题,那么您可能希望抽取一个小样本进行子集化,直到您确定代码有效为止(另请参阅:r-bloggers.com/memory-management-in-r-a-few-tips-and-tricks)。
【解决方案5】:

我认为关键是矢量化并使用%in% 运算符对数据框A 进行子集化。而且,我知道,价格不是随机数,但我不想编写随机游走代码......我使用 paste 创建了一个股票日期指数,但我相信你可以使用来自 @987654324 的指数@ 在plm 库中,这是我找到的最好的面板数据。

A  <- data.frame(stock=rep(1:10, each=100), date=rep(Sys.Date()-99:0, 10), price=rnorm(1000))
B <- A[seq(from=100, to=1000, by=100), ]
A <- cbind(paste(A$stock, A$date, sep="-"), A)
B <- cbind(paste(B$stock, B$date, sep="-"), B)
colnames(A) <- colnames(B) <- c("index", "stock", "date", "price")
index <- which(A[, 1] %in% B[, 1])
returns <- (A$price[index] - A$price[index-10]) / A$price[index-10]
B <- cbind(B, returns)

【讨论】:

    【解决方案6】:

    考虑到您有记忆问题,也许首先减少 A 可能会有所帮助。首先,去掉无关的 id。

    A <- A[A$id %in% B$id,]
    

    完全减少 A 数据集仍然想要获取更多内存。不存储一些变量是不可能的。不过,我希望通过将每个日期都砍掉低于我们的绝对最小值和高于我们的绝对最大值,我们可以摆脱它。

    A <- A[A$date > (min(B$date) - 10) & A$date <= max(B$date),]
    

    当然,通过不通过 id 来限定它,我们还没有得到 A 的最小版本,但希望它足够小。

    现在运行我最初提出的代码,看看是否还有内存错误

    B$idDate <- factor(B$id):factor(B$date)
    B$past10 <- sapply(B$idDate, function(x){with(B[B$idDate == x,], 
        prod(1+A$ret[A$id == id & A$date > date-10 & A$date < date])-1)})
    

    【讨论】:

    • 感谢 John,这两行代码在 A 中帮助减少了很多。但是在我尝试了 500 行之后,它仍然给了我错误:无法分配大小为 6.8 Mb 的向量。我认为问题可能出在“应用”中。不知何故,它将 id 和 date 的东西作为向量,并尝试将 A$id 与 B$id 等进行比较,而不是按元素
    • 看起来是 sapply(B$idDate) 造成了问题,我将 B$idDate 更改为字符并且代码一直在运行............闻起来像一个循环
    • 尽量不要把past10放在B中(去掉第二行开头的B$)。是不是也有内存问题? sapply() 正在获取 B 的每个元素,然后使用它来选择 A 并获取产品......就像在您的原始代码中一样。我在我的代码上对其进行了演示,但在我的计算机上,6.8meg 的内存量是微不足道的(而且我只使用了大约 200 行)。如果它确实报告了错误消息,那么确切的消息是什么?
    • 所以在我将 B$idDate 更改为字符后,它可以使用 500 行。现在我正在运行完整的数据,它一直在运行......没有任何错误消息,但也没有停止的迹象。
    【解决方案7】:
    library(data.table)
    #create data
    A  <- data.table(id=rep(1:10, each=40000), date=rep(Sys.Date()-99:0,  4000), ret=rnorm(400000))
    B  <- data.table(id=rep(1:5,  each=10), date=rep(Sys.Date()-99:0),  ret=rnorm(50))
    
    #find dates to compare against
    n <- NROW(B)
    B_long <- B[,.(id = rep(id,each=10),date = rep(date,each=10))]
    s <- rep(-10:-1,n)
    B_long[,date:=date + s]
    
    #information in one column
    B_long$com <- as.numeric(paste0(B_long$id,as.numeric(B$date)))
    A$com <- as.numeric(paste0(A$id,as.numeric(A$date)))
    
    #compare
    setkey(A,com)
    X <- A[com %in% B_long$com,]
    

    此答案基于 Richards 的答案,但更针对问题。

    关键思想是建立一个 id 日期组合向量来进行比较。这发生在第二个代码块中。

    我的解决方案使用 data.table 包,但应该对 data.frame 进行一些语法更改。但是使用data.table包有keycolumns的优势。

    如果您仍然遇到问题,可以将此方法与 john 的第二个答案和第一个作物 A 结合使用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-05-01
      • 2021-12-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多