使用函数将结果保存在嵌套循环中

时间:2020-06-03 20:26:48

标签: r function nested-loops

我正在尝试使用一个函数在嵌套循环(4)中获得结果,打印正确,但无法在数据框中获得结果。我有这个:

for (t in 1:5)
  for (s in 1:5)
    for (j in 1:4)
      for (i in 1:2)
        print(c (t,s,j,i,function(j,t,s,i)))

可以得到正确的结果,但是我无法存储,所以我尝试过:

c=c()
for (h in 1:200)
for (t in 1:5)
  for (s in 1:5)
    for (j in 1:4)
      for (i in 1:2)
        c[[h]] = c (t,s,j,i,function(j,t,s,i))
         return(c)

但是行不通,谢谢。

3 个答案:

答案 0 :(得分:1)

当我们使用NULL向量进行初始化时,我们可以追加而不是建立索引

c1 <- c()
for (h in 1:200){
   for (t in 1:5) {
      for (s in 1:5) {
        for (j in 1:4) {
           for (i in 1:2){
                 c1 <- c(c1, c(t,s,j,i,function(j,t,s,i)))
           }
          }
         }
         }
       }

注意:c是一个函数名,最好使用不同的对象名('c1')初始化

答案 1 :(得分:1)

OP的原始尝试

original attempt几乎就在那儿:您只需要独立地推进嵌套循环的索引h

在评估函数的最内层循环的每次迭代中,h都会加1,以将该迭代的结果存储在下一个可用的存储位置:

c1 <- c()
h <- 0L
for (t in 1:5)
  for (s in 1:5)
    for (j in 1:4)
      for (i in 1:2) {
        h <- h + 1L
        c1[[h]] = c(t, s, j, i, your_function(j, t, s, i))
      }

这将返回c1中的向量列表。该列表包含200 = 5 * 5 * 4 * 2个条目。

(顺便说一句,OP's own answer创建了一个5行200列的矩阵。)

替代:无循环

R提供的功能可一次性获得结果,而无需循环。以下所有三个变体的基本思想是首先创建tsjieg <- expand.grid(t = 1:5, s = 1:5, j = 1:4, i = 1:2) eg[, "f"] <- do.call(mapply, c(your_function, eg)) 的所有组合,然后应用这些输入数据上的功能。

基本R

library(dplyr)
library(tidyr)
library(purrr)
crossing(t = 1:5, s = 1:5, j = 1:4, i = 1:2) %>% 
  mutate(f = pmap_dbl(., your_function))

这将返回一个5列200行的data.frame。

tidyverse

library(data.table)
CJ(t = 1:5, s = 1:5, j = 1:4, i = 1:2)[, .(t, s, j, i, f = your_function(j, t, s, i))]

这将返回5列200行的小标题(增强的data.frame)。

data.table

cbind()

这将返回具有5列200行的data.table(增强型data.frame)。

基准化

我很惊讶和失望地发现像 akrun 这样的高知名度SO用户是suggesting to use appending instead of indexing。例如,Patrick Burns' book The R Inferno第2章中讨论了增长对象被认为是效率低下和不当行为。

当然,追加所需的键入较少,但是最快的答案并不总是最好的。 可以通过对不同问题大小的不同方法进行基准测试来验证这一点。

下面的基准比较了执行时间和内存消耗

  • 通过
    • 使用rbind()附加到矩阵(列方式,如OP's own answer
    • 使用c()(逐行)追加到矩阵上
    • 使用mapply()附加到列表
  • 索引
    • 在预分配的矩阵中按列存储
    • 在预先分配的矩阵中按行存储
    • 存储在列表中(如OP's original approach中所述)
  • 无循环使用
    • pmap()(以R为底)
    • i(tidyverse)
    • data.table

为了对不同的问题大小进行基准测试,fct <- function(j, t, s, i) ((10*j + t)*10 + s)*10 + i 上最里面的循环的长度是变化的。

最后,我们需要定义一个廉价的函数:

bench

使用library(bench) library(ggplot2) library(dplyr) library(tidyr) library(purrr) library(data.table) bm <- press( ni = 2L * 10^(0:2), { nt <- 5L ns <- 5L nj <- 4L ntsji <- nt * ns * nj * ni cat(ntsji, "\n") mark( grow_cbind = { c1 <- c() for (t in seq(nt)) for (s in seq(ns)) for (j in seq(nj)) for (i in seq(ni)) { c1 <- cbind(c1, c(t, s, j, i, fct(j, t, s, i))) } }, idx_col = { c1 <- matrix(nrow = 5L, ncol = ntsji) icol <- 0L for (t in seq(nt)) for (s in seq(ns)) for (j in seq(nj)) for (i in seq(ni)) { icol <- icol + 1L c1[ , icol] <- c(t, s, j, i, fct(j, t, s, i)) } }, grow_rbind = { c1 <- c() for (t in seq(nt)) for (s in seq(ns)) for (j in seq(nj)) for (i in seq(ni)) { c1 <- rbind(c1, c(t, s, j, i, fct(j, t, s, i))) } }, idx_row = { c1 <- matrix(nrow = ntsji, ncol = 5L) irow <- 0L for (t in seq(nt)) for (s in seq(ns)) for (j in seq(nj)) for (i in seq(ni)) { irow <- irow + 1L c1[irow, ] <- c(t, s, j, i, fct(j, t, s, i)) } }, grow_list = { c1 <- list() for (t in seq(nt)) for (s in seq(ns)) for (j in seq(nj)) for (i in seq(ni)) { c1 <- c(c1, list(c(t, s, j, i, fct(j, t, s, i)))) } }, idx_list = { c1 <- list(ntsji) idx <- 0L for (t in seq(nt)) for (s in seq(ns)) for (j in seq(nj)) for (i in seq(ni)) { idx <- idx + 1L c1[[idx]] <- c(t, s, j, i, fct(j, t, s, i)) } }, nol_mapply = { eg <- expand.grid(t = seq(nt), s = seq(ns), j = seq(nj), i = seq(ni)) eg[, "f"] <- do.call(mapply, c(fct, eg)) }, nol_pmap = { crossing(t = seq(nt), s = seq(ns), j = seq(nj), i = seq(ni)) %>% mutate(f = pmap_dbl(., fct)) }, nol_dt = { CJ(t = seq(nt), s = seq(ns), j = seq(nj), i = seq(ni))[ , .(t, s, j, i, f = fct(j, t, s, i))] }, check = FALSE )} ) 软件包进行基准测试。由于不同方法的结果在类别和形状上有所不同,因此已关闭对结果的检查。

autoplot(bm)

时间是使用对数时间标度绘制的:

data.table

enter image description here

图表显示

    与其他方法相比,随着问题规模的增大,物体的生长变得越来越慢。对于最大的问题,速度要慢得多。
  • 创建输出对象的方式(列表,矩阵按列或矩阵按行)对执行速度没有太大影响。
  • mem_alloc方法是解决较大问题的最快方法。对于最大的问题,它比第二快的问题快一个数量级,比成长的对象快三个数量级(1000倍)。

内存消耗显示在下面的列print(bm, n = Inf) # A tibble: 27 x 14 expression ni min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc total_time <bch:expr> <dbl> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl> <int> <dbl> <bch:tm> 1 grow_cbind 2 1.58ms 1.88ms 484. 794.16KB 8.73 222 4 458.41ms 2 idx_col 2 1.44ms 1.76ms 489. 12.12KB 6.58 223 3 455.64ms 3 grow_rbind 2 1.7ms 2ms 445. 794.16KB 10.6 126 3 283.15ms 4 idx_row 2 1.41ms 1.59ms 548. 11.81KB 6.37 258 3 471.22ms 5 grow_list 2 1.34ms 1.59ms 574. 164.59KB 4.17 275 2 479.38ms 6 idx_list 2 1.38ms 1.62ms 561. 29.95KB 6.37 264 3 470.7ms 7 nol_mapply 2 714.4us 821.8us 1108. 15.64KB 8.52 520 4 469.3ms 8 nol_pmap 2 7.25ms 8.7ms 111. 22.77KB 4.28 52 2 467.69ms 9 nol_dt 2 1.58ms 1.99ms 460. 78.91KB 2.03 226 1 491.48ms 10 grow_cbind 20 21.86ms 23.24ms 38.0 76.42MB 69.7 6 11 157.74ms 11 idx_col 20 6.9ms 7.92ms 115. 117.59KB 6.53 53 3 459.29ms 12 grow_rbind 20 24.12ms 25.16ms 39.6 76.42MB 87.1 5 11 126.26ms 13 idx_row 20 6.67ms 7.77ms 118. 117.28KB 6.55 54 3 458.18ms 14 grow_list 20 21.36ms 23.13ms 42.7 15.36MB 7.11 18 3 421.72ms 15 idx_list 20 7.44ms 8.36ms 112. 333.92KB 6.59 51 3 455.35ms 16 nol_mapply 20 4.99ms 5.87ms 167. 142.55KB 9.02 74 4 443.29ms 17 nol_pmap 20 13.28ms 15.1ms 62.0 114.36KB 6.89 27 3 435.4ms 18 nol_dt 20 1.67ms 2.1ms 422. 198.44KB 2.05 206 1 488.67ms 19 grow_cbind 200 2.3s 2.3s 0.434 7.45GB 33.9 1 78 2.3s 20 idx_col 200 66.01ms 74.33ms 13.7 1.14MB 7.85 7 4 509.42ms 21 grow_rbind 200 2.43s 2.43s 0.412 7.45GB 32.1 1 78 2.43s 22 idx_row 200 65.9ms 75.92ms 13.6 1.14MB 7.78 7 4 514.05ms 23 grow_list 200 2.04s 2.04s 0.490 1.49GB 7.35 1 15 2.04s 24 idx_list 200 71.34ms 77.3ms 12.3 3.3MB 7.01 7 4 570.57ms 25 nol_mapply 200 52.63ms 64.19ms 14.8 1.48MB 9.26 8 5 540.07ms 26 nol_pmap 200 74.69ms 82.26ms 11.7 1.01MB 7.79 6 4 513.76ms 27 nol_dt 200 2.32ms 2.92ms 259. 1.36MB 1.99 130 1 501.78ms # ... with 4 more variables: result <list>, memory <list>, time <list>, gc <list> 中:

sessioninfo::session_info()

该表清楚地表明了

    与其他方法相比,增长的对象分配更多的内存。对于最大的问题,增长矩阵比索引或无循环方法分配的内存多7000倍。

会话信息

摘录

- Session info -------------------------------------------------------------------------
 setting  value                       
 version  R version 4.0.0 (2020-04-24)
 os       Windows 10 x64              
 system   x86_64, mingw32             
 ui       RStudio                     

- Packages -----------------------------------------------------------------------------
 package     * version date       lib source        
 bench       * 1.1.1   2020-01-13 [1] CRAN (R 4.0.0)
 data.table  * 1.12.9  2020-05-26 [1] local         
 dplyr       * 1.0.0   2020-05-29 [1] CRAN (R 4.0.0)
 ggplot2     * 3.3.1   2020-05-28 [1] CRAN (R 4.0.0)
 purrr       * 0.3.4   2020-04-17 [1] CRAN (R 4.0.0)
 tidyr       * 1.1.0   2020-05-20 [1] CRAN (R 4.0.0)
import matplotlib.pyplot as plt
import matplotlib.tri as tri
import pandas as pd
import numpy as np

dlel = pd.read_fwf('HSH_Calculation.LEL')
dlnd = pd.read_fwf('HSH_Calculation.LND')
dt = pd.read_fwf('HSH_Calculation.HTD')

xy = np.asarray(dlnd.iloc[:, 3:5])

x = xy[:, 0]
y = xy[:, 1]
triangles = np.asarray(dlel.iloc[:, 1:4])
# triang = tri.triangulation(x,y)
triang = tri.Triangulation(x, y)


plt.figure()
plt.gca().set_aspect('equal')
# plt.triplot(x, y, triangles, 'go-', lw=1.0)
plt.triplot(triang, 'go-', lw=1.0)
plt.title('triplot of user-specified triangulation')
plt.xlabel('Longitude (degrees)')
plt.ylabel('Latitude (degrees)')

plt.show()

答案 2 :(得分:0)

使用

setup_method

DevTools listening on ws://127.0.0.1:51558/devtools/browser/88bf2c58-10da-4b03-9697-eec415197e66
Testing

@akrun裂纹