首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >R使用tcltk/tcltk 2:在用data.frame显示大TkTable时提高缓慢性能?

R使用tcltk/tcltk 2:在用data.frame显示大TkTable时提高缓慢性能?
EN

Stack Overflow用户
提问于 2016-01-08 00:25:42
回答 1查看 1K关注 0票数 2

请看下面的两个编辑(后面添加)..。

我已经将一个大data.frame加载到内存中(2.7mio行和7列- 74 MB的RAM)。

如果我想使用Tcl/Tk的Tktable小部件通过tcltk2包函数tk2edit查看数据

  • 需要超过15分钟的,直到窗口与数据一起显示。
  • --大约7GB的内存(!)--被R(包括)所消耗。( Tcl/Tk) n+!

示例:

代码语言:javascript
复制
library(tcltk2)

my.data.frame <- data.frame(ID=1:2600000,
                            col1=rep(LETTERS,100000),
                            col2=rep(letters,1E5),
                            col3=26E5:1)       # about 40 MB of data

tk2edit(my.data.frame)

基本问题似乎是,data.frame的每个单元必须通过两个嵌套循环(请参阅tktable问题中的代码。)加载到tcl数组中。

tcltk2包的函数tk2edit以同样的方式工作,过于简化:

代码语言:javascript
复制
# my.data.frame contains a lot of rows...
for (i in 0:(dim(my.data.frame)[1])) {
        for (j in 0:(dim(my.data.frame)[2]-1)) {
                tclarray1[[i,j]] <- my.data.frame[i, j]
        }
}

问题:是否有任何方法来优化使用tktable显示大data.frames,例如避免嵌套循环?我只是想查看数据(不需要编辑).

tktable-variable选项,您可以在其中设置包含表的所有数据的tcl数组变量。因此,我们必须找到从R data.frame创建tcl数组的方法,“从R调用一次tcl”.

PS:这不是tcltk2包的问题,但似乎是一个一般的问题,如何将data.frame的数据“批量加载”到Tcl变量中.

PS2:好消息是,Tktable似乎能够高效地显示这么多数据(我可以滚动甚至编辑单元格,而不注意到任何严重的延迟)。

编辑1 (09/01/2015):使用和数组中的数据添加纯Tcl/Tk基准测试结果

我在Tcl/Tk中准备了一个简单的基准测试,以测量填充类似Tktable的执行时间和内存消耗。

代码语言:javascript
复制
#!/usr/bin/env wish

package require Tktable

set rows 2700000
set columns 4

for {set row 0} {$row <= $rows} {incr row} {
  for {set column 0} {$column < $columns} {incr column} {
    if {$row == 0} {
      set data($row,$column) Titel$column
    } else {
      set data($row,$column) R${row}C${column}
    }
  }
}

ttk::frame .fr

table .fr.table -rows $rows -cols $columns -titlerows 1 -titlecols 0 -height 5 -width 25 -rowheight 1 -colwidth 9 -maxheight 100 -maxwidth 400 -selectmode extended -variable data -xscrollcommand {.fr.xscroll set} -yscrollcommand {.fr.yscroll set}

scrollbar .fr.xscroll -command {.fr.table xview} -orient horizontal
scrollbar .fr.yscroll -command {.fr.table yview}

pack .fr -fill both -expand 1
pack .fr.xscroll -side bottom -fill x
pack .fr.yscroll -side right -fill y
pack .fr.table -side right -fill both -expand 1

结果:

  • 内存消耗:3.2GB
  • 时间,直到表显示:15秒。

结论: Tcl/Tk阵列浪费内存,但性能很好(使用runtime 15分钟的运行时间似乎是由于runtime/Tk通信开销造成的。

测试设置:Ubuntu14.04 64位,16 GB内存.

编辑2 (10/01/2015):在列表中添加ttk::treeview的纯Tcl/Tk基准结果

为了比较Tktablettk::treeview的内存消耗,我编写了以下代码:

代码语言:javascript
复制
#!/usr/bin/env wish
set rows 2700000
set columns 4
set data {}
set colnames {}
for {set i 0} {$i < $columns} {incr i} {
  lappend colnames Title$i
}
for {set row 0} {$row <= $rows} {incr row} {
  set newrow {}
  for {set column 0} {$column < $columns} {incr column} {
      lappend newrow R${row}C${column}
  }
  lappend data $newrow
}

ttk::treeview .tv -columns $colnames -show headings -yscrollcommand {.sbY set} -xscrollcommand {.sbX set}
foreach Element $data {
   .tv insert {} end -values $Element
}
foreach column $colnames {
  .tv heading $column -text $column
}
ttk::scrollbar .sbY -command {.tv yview}
ttk::scrollbar .sbX -command {.tv xview} -orient horizontal
pack .sbY -side right -fill y
pack .sbX -side bottom -fill x
pack .tv -side left -fill both

结果:

  • 内存消耗:2GB(其中的数据存储为列表:1.2GB)
  • 时间,直到表显示:15秒。
  • 比较: 10兆行消耗7.2GB的RAM,但选择一行需要服务器秒(2-5)然后(可能的原因:内部列表遍历?)

结论:

  • treeviewTktable具有更高的内存效率,因为它可以使用列表而不是数组。
  • 对于较大的数据大小(>几百万行),行选择是缓慢的(结尾越多,速度就越慢!)
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-01-16 23:13:06

我已经找到了一种可能的解决方案/解决方案--使用Tktable 在"unbound“(命令)模式中使用

使用command选项Tktable,您可以指定每次在屏幕上显示单元格时调用的函数。这避免了将所有数据同时从R加载到Tcl,改善了“启动”时间,并显著减少了TCL存储数组和列表的方式造成的内存消耗。

这样,每次滚动一系列函数调用都会请求可见单元格的内容。

它对我很管用,即使超过10兆。一排排!

缺点:调用一个为每个单元返回一个Tcl变量的R函数仍然很不有效。如果您第一次滚动,可以查看正在更新的单元格。因此,仍在寻找R与Tcl/Tk.之间的大量数据传输解决方案。

欢迎任何提高性能的建议!

我已经实现了一个小的演示(与1 mio。行和21列消耗1.2 GB的RAM),并添加了一些按钮来测试不同的特性(比如缓存)。

注意:启动时间长是由创建底层测试数据造成的,而不是由Tktable造成的!

代码语言:javascript
复制
library(tcltk)
library(data.table)

# Tktable example with -command ("unbound" mode) ---------------------------
# Doc: http://tktable.sourceforge.net/tktable/doc/tkTable.html

NUM.ROWS <- 1E6
NUM.COLS <- 20

# generate a big data.frame - this will take a while but is required for the demo
dt.data <- data.table(ID = 1:NUM.ROWS)

for (i in 1:NUM.COLS) {
  dt.data[, (paste("Col",i)) := paste0("R", 1:NUM.ROWS, " C", i)]
}

# Fill one cell with a long text containing special control characters to test the Tktable behaviour
dt.data[3,3 := "This is a long text with backslash \\ and \"quotes\"!"]

tclRequire("Tktable")

t <- tktoplevel()

tkwm.protocol(t, "WM_DELETE_WINDOW", function() tkdestroy(t))

# Function to return the current row and column as "calculated" value (without an underlying data "model")
calculated.data <- function(C) {
  # Function arguments  for Tcl "substitutions":
  # See:   http://tktable.sourceforge.net/tktable/doc/tkTable.html
  #   %c the column of the triggered cell.
  #   %C A convenience substitution for %r,%c.
  #   %i 0 for a read (get) and 1 for a write (set). Otherwise it is the current cursor position in the cell.
  #   %r the row of the triggered cell.
  return(tclVar(C))  # this does work!
}

# Function to return the content of a data.table for the current row and colum
data.frame.data <- function(r, c) {
  if( r == "0")
    return(tclVar(names(dt.data)[as.integer(c)+1]))             # First row contains the column names
  else
    return(tclVar(as.character(dt.data[as.integer(r)+1, as.integer(c)+1, with = FALSE])))   # Other rows are data rows
}

frame <- ttklabelframe(t, text = "Data:")
# Add the table to the window environment to ensure killing it when the window is closed (= no more phantom calls to the data command handler)
# Cache = TRUE: This greatly enhances speed performance when used with -command but uses extra memory.
t$env$table <- tkwidget(frame, "table", rows = NUM.ROWS, cols = NUM.COLS, titlerows = 1, selecttype = "cell", selectmode = "extended", command = calculated.data, cache = TRUE, yscrollcommand = function(...) tkset(scroll.y, ...), xscrollcommand = function(...) tkset(scroll.x, ...))

scroll.x <- ttkscrollbar(frame, orient = "horizontal", command=function(...) tkxview(t$env$table,...))  # command that performs the scrolling
scroll.y <- ttkscrollbar(frame, orient = "vertical", command=function(...) tkyview(t$env$table,...))  # command that performs the scrolling

buttons <- ttkframe(t)
btn.read.only <- ttkbutton(buttons, text = "make read only", command = function() tkconfigure(t$env$table, state = "disabled"))
btn.read.write <- ttkbutton(buttons, text = "make writable", command = function() tkconfigure(t$env$table, state = "normal"))
btn.clear.cache <- ttkbutton(buttons, text = "clear cache", command = function() tcl(t$env$table, "clear", "cache"))
btn.bind.data.frame <- ttkbutton(buttons, text = "Fill cells from R data.table",
                                 command = function() {
                                   tkconfigure(t$env$table, command = data.frame.data, rows = nrow(dt.data), cols = ncol(dt.data), titlerows = 1)
                                   tcl(t$env$table, "clear", "cache")
                                   tkwm.title(t,"Cells are filled from an R data.table")
                                 })
btn.bind.calc.value <- ttkbutton(buttons, text = "Fill cells with calculated values",
                                 command = function() {
                                   tkconfigure(t$env$table, command = calculated.data, rows = 1E5, cols = 40)
                                   tcl(t$env$table, "clear", "cache")
                                   tkwm.title(t,"Cells are calculated values (to test the highest performance possible)")
                                 })

tkgrid(btn.read.only, row = 0, column = 1)
tkgrid(btn.read.write, row = 0, column = 2)
tkgrid(btn.clear.cache, row = 0, column = 3)
tkgrid(btn.bind.data.frame, row = 0, column = 5)
tkgrid(btn.bind.calc.value, row = 0, column = 6)

tkpack(frame, fill = "both", expand = TRUE)
tkpack(scroll.x, fill = "x", expand = FALSE, side = "bottom")
tkpack(scroll.y, fill = "y", expand = FALSE, side = "right")
tkpack(t$env$table, fill = "both", expand = TRUE, side = "left")
tkpack(buttons, side = "bottom")
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34667618

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档