缓慢的 data.frame 行分配

2024-04-28

我正在使用 RMongoDB,需要用查询的值填充空的 data.frame。结果相当长,大约有 200 万个文档(行)。

当我进行性能测试时,我发现将值写入行的时间随着数据帧的维度的增加而增加。也许这是一个众所周知的问题,而我是最后一个注意到它的人。

一些代码示例:

set.seed(20140430)
nreg <- 2e3
dfres <- as.data.frame(matrix(rep(NA,nreg*7),nrow=nreg,ncol=7))
system.time(dfres[1e3,] <-  c(1:5,"a","b"))
summary(replicate(10,system.time(dfres[sample(1:nreg,1),] <- c(1:5,"a","b"))[3]))

nreg <- 2e6
dfres <- as.data.frame(matrix(rep(NA,nreg*7),nrow=nreg,ncol=7))
system.time(dfres[1e3,] <-  c(1:5,"a","b"))
summary(replicate(10,system.time(dfres[sample(1:nreg,1),] <- c(1:5,"a","b"))[3]))

在我的机器上,200 万行 data.frame 的分配大约需要 0.4 秒。如果我想填充整个数据集,这需要很多时间。为了解决这个问题,我们进行了第二次模拟。

nreg <- seq(2e1,2e7,length.out=10)
te <- NULL 
for(i in nreg){
    dfres <- as.data.frame(matrix(rep(NA,i*7),nrow=i,ncol=7))
    te <- c(te,mean(replicate(10,{r <- sample(1:i,1); system.time(dfres[r,] <- c(1:5,"a","b"))[3]}) ) )
}
plot(nreg,te,xlab="Number of rows",ylab="Avg. time for 10 random assignments [sec]",type="o")
#rm(nreg,dfres,te)

Question: 为什么会出现这种情况?有没有更快的方法来填充内存中的 data.frame ?


让我们先从“列”开始,看看发生了什么,然后再回到行。

R 版本data.frame当你对它们进行操作时。例如:

## R v3.0.3
df <- data.frame(x=1:5, y=6:10)
dplyr:::changes(df, transform(df, z=11:15)) ## requires dplyr to be available

# Changed variables:
#           old            new           
# x         0x7ff9343fb4d0 0x7ff9326dfba8
# y         0x7ff9343fb488 0x7ff9326dfbf0
# z         <added>        0x7ff9326dfc38

# Changed attributes:
#           old            new           
# names     0x7ff934170c28 0x7ff934308808
# row.names 0x7ff934551b18 0x7ff934308970
# class     0x7ff9346c5278 0x7ff935d1d1f8

您可以看到添加“新”列已生成“旧”列的副本(地址不同)。属性也被复制。最令人印象深刻的是这些副本是深拷贝,相对于浅拷贝.

浅拷贝只复制列指针的向量,而不是整个数据,而深拷贝则复制所有内容(这里是不必要的)。

然而,在 R v3.1.0 中,出现了一些令人欢迎的变化,即“旧”列不再是deep复制的。所有功劳都归功于 R 核心开发团队。

## R v3.1.0
df <- data.frame(x=1:5, y=6:10)
dplyr:::changes(df, transform(df, z=11:15)) ## requires dplyr to be available

# Changed variables:
#           old     new           
# z         <added> 0x7f85d328dda8

# Changed attributes:
#           old            new           
# names     0x7f85d1459548 0x7f85d297bec8
# row.names 0x7f85d2c66cd8 0x7f85d2bfa928
# class     0x7f85d345cab8 0x7f85d2d6afb8

您可以看到这些列x and y根本没有改变(因此不存在于输出中changes函数调用)。这是一个巨大的(并且受欢迎的)改进!

到目前为止,我们研究了在 R


现在,回到你的问题:那么,“行”呢?我们首先考虑旧版本的 R,然后再回到 R v3.1.0。

## R v3.0.3
df <- data.frame(x=1:5, y=6:10)
df.old <- df
df$y[1L] <- -6L
dplyr:::changes(df.old, df)

# Changed variables:
#           old            new           
# x         0x7f968b423e50 0x7f968ac6ba40
# y         0x7f968b423e98 0x7f968ac6bad0
# 
# Changed attributes:
#           old            new           
# names     0x7f968ab88a28 0x7f968abca8e0
# row.names 0x7f968abb6438 0x7f968ab22bb0
# class     0x7f968ad73e08 0x7f968b580828

我们再次看到不断变化的专栏y导致复制列x在旧版本的 R 中也是如此。

## R v3.1.0
df <- data.frame(x=1:5, y=6:10)
df.old <- df
df$y[1L] <- -6L
dplyr:::changes(df.old, df)

# Changed variables:
#           old            new           
# y         0x7f85d3544090 0x7f85d2c9bbb8
# 
# Changed attributes:
#           old            new           
# row.names 0x7f85d35a69a8 0x7f85d35a6690

我们看到 R v3.1.0 中的出色改进,从而产生了just column y。 R v3.1.0 再次有了巨大的改进! R 的修改时复制变得更加明智。

但仍然使用data.table's 通过引用赋值语义上,我们可以做得更好——甚至不复制y列与 R v3.1.0 中的情况相同。

这个想法是:只要您分配给特定索引处的列的对象类型不改变(这里,列y是整数 - 所以只要你分配一个整数回y), we really无需通过修改复制即可做到in-place(引用)。

为什么?因为我们不必在这里分配/重新分配任何东西。例如,如果您分配了双精度/数字类型,则需要 8 字节的存储空间,而不是整数列的 4 字节存储空间y,然后我们要创建一个新列y并将值复制回来。

也就是说,我们可以通过引用进行子分配 using data.table。我们可以使用:= or set()去做这个。我将演示使用set() here.

现在,这是与基础 R 和data.table分别针对 R v3.0.3 和 v3.1.0,对包含 2,000 到 20,000,000 行(10 的倍数)的数据进行分析。您可以在这里找到代码 https://gist.github.com/arunsrinivasan/91b9951a57ce85090104.

与 R v3.0.3 的比较图:

Plot for comparison against R v3.1.0: 3.1.0 vs data.table

R v3.0.3、R v3.1.0 和 data.table 在 2000 万行(10 次复制)上的最小值、中值和最大值为:

      type    min  median    max
base_3.0.3  10.05   10.70  18.51
base_3.1.0   1.67    1.97   5.20
data.table   0.04    0.04   0.05

注意:您可以在以下位置查看完整的时间安排这个要点 https://gist.github.com/arunsrinivasan/91b9951a57ce85090104.

这清楚地显示了 R v3.1.0 中的改进,但也表明正在更改的列仍在复制,并且仍然消耗一些时间,这是通过以下方式克服的通过引用进行子分配 in data.table.

HTH

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

缓慢的 data.frame 行分配 的相关文章

  • 根据 pandas 中的条件交换列值

    我想按条件重新定位列 如果国家 地区是 日本 我需要将姓氏和名字反向重新定位 df pd DataFrame France Kylian Mbappe Japan Hiroyuki Tajima Japan Shiji Kagawa Eng
  • 如何将环境变量传递给shinyapps

    我想将安全参数传递给shinyapps io部署 以便我的应用程序可以通过以下方式获取它们 Sys getenv PASSWORD X 我找不到任何相关内容deployApp函数在rsconnect包裹 您可以使用Renviron网站 or
  • 如何在knitr中安装软件包?

    到目前为止 我一直在使用这段代码来加载 R 包并编写 R 文件 但我正在尝试使用knitr rm list ls all TRUE kpacks lt c ggplot2 install github devtools mapdata ne
  • 访问特征矩阵的行向量时复制或引用

    我正在使用的代码Eigen http eigen tuxfamily org index php title Main Page矩阵库 我注意到在整个代码中 有如下访问器 RowVector3f V size t vertex index
  • 为什么在排序输入上插入到树中比随机输入更快?

    现在我一直听说从随机选择的数据构建二叉搜索树比有序数据更快 这仅仅是因为有序数据需要显式重新平衡以将树高度保持在最低限度 最近我实现了一个不可变的treap http en wikipedia org wiki Treap 一种特殊的二叉搜
  • R 中的 huxtable 即使有选项也默认为科学记数法(scipen=999)

    我试图生成像样的桌子 并在过去的一周尝试了很多软件包 我的头在游泳 今天早上开始使用 package huxtable 并试图摆脱科学记数法 x lt mtcars 1 5 1 2 x mpg lt x mpg 10000000 get s
  • 如何缓存 ASP.NET 网站以获得更好的性能

    我是一名网页设计师 通常设计不需要更新的企业网站 所以我想将输出缓存一天 我怎样才能做到这一点 此外 任何有关在慢速服务器上提高 ASP NET 性能的建议都被接受 请注意 ASP NET 缓存有一个bug http connect mic
  • 删除绘图轴值

    我只是想知道是否有一种方法可以消除 r 图中的轴值 分别是 x 轴或 y 轴 我知道axes false将摆脱整个轴 但我只想摆脱编号 删除 x 轴或 y 轴上的编号 plot 1 10 xaxt n plot 1 10 yaxt n 如果
  • php字符串是值类型吗?

    为什么php的string是值类型 每次将参数传递给函数时 每次进行赋值时 每次连接都会导致字符串被复制时 它都会被复制到各处 我的 NET 经验告诉我 它似乎效率低下 迫使我几乎在任何地方都使用引用 考虑以下替代方案 替代方案1 This
  • 为什么 cross_val_predict 比 KNeighborsClassifier 的拟合慢得多?

    在 Jupyter 笔记本上本地运行并使用 MNIST 数据集 28k 条目 每个图像 28x28 像素 以下内容为27秒 from sklearn neighbors import KNeighborsClassifier knn clf
  • SQL Server RODBC 连接

    有没有人有使用 RODBC 并连接到 MS SQL Server 2005 或 2008 的连接字符串示例 谢谢 library RODBC dbhandle lt odbcDriverConnect driver SQL Server s
  • WPF 应用程序在第一次交互(例如单击按钮)后停止/冻结

    我目前在 WPF 中遇到问题 UI 加载正常 但每当进行第一次用户交互时 例如单击按钮 应用程序似乎会停止 或者例如 如果我有两个显示 MessageBox 的按钮 则第一次单击将等待几秒钟 然后显示MessageBox 但任何后续交互都是
  • 在 R 中捕获段错误

    我得到了caught segfault每次我尝试从以下位置运行任何绘图函数时都会出错ggplot2包 1 0 0 我已经尝试过这个qplot geom dotplot geom histogram等来自包的数据 例如diamonds or
  • R Shiny UI 子选项复选框?

    我有一个基本的 RShiny 应用程序 它有一个反应式复选框 它根据复选框中选择的数据 df 列 绘制时间序列数据 我当前的代码生成一个带有复选框输入的 UI 如下所示 Load R packages library shiny libra
  • RPostgreSQL 无法关闭连接

    我有一个闪亮的应用程序 使用它连接到数据库RPostgreSQL 在应用程序结束时 连接关闭 驱动程序应该被卸载 但我收到错误 警告我连接未关闭 代码看起来像这样 in the app R file but not in the serve
  • data.table:从不存在的列到现有列的“get”失败,静默失败

    gt d lt data table x 1 5 gt d x 6 y get i 9 Error in get i 9 object i 9 not found gt d y 1 add a new column y gt d x 6 y
  • 准备编程竞赛的缩写和函数[关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 不理解..密度的行为

    在下面的数据框中 我预计密度的 y 轴值为 0 6 和 0 4 但它们是 1 0 我觉得我使用的方式显然缺少一些非常基本的东西 密度 但是我的大脑冻结了 我将如何使用 密度 获得所需的行为 任何帮助将不胜感激 df lt data fram
  • 未使用的功能会产生什么后果

    我想知道在代码中使用未使用的函数会产生什么 如果有什么后果 如果您查找并删除所有未使用的函数和变量 性能是否会有明显的改进 或者删除未使用的函数和变量只是一个好习惯 未使用的功能不会损害性能 他们让维护代码的人的工作变得更加困难 现代 ID
  • 为什么 as.character() 返回日期列表中的整数?

    我惊讶地发现 R 中出现以下行为 as character c Sys Date gt 1 2018 02 05 as character list Sys Date gt 1 17567 为什么会出现这种情况 也就是说 显然 17567

随机推荐