Skip to content

[R-gui] your favourite spreadsheet-like data viewer or editor?

2 messages · Liviu Andronic, j verzani

#
Dear all
I've been looking for a solution to this problem ever since I started
learning R, and I still haven't found one I'm comfortable with. And I
would rather avoid exporting and importing into a spreadsheet just for
this. I am looking for a data viewer/editor that could, among other
things:
- have a 'view' mode (not an 'edit only' mode, to avoid bugs or
unintended modifications)
- have a 'filter' view mode (not a data-modifying subset)
- have a 'sort' view mode (not a data-modifying sort operation)
- select multiple rows/cols
- select multiple individual cells

Here's a list of what I've found sofar:
- View()/Edit(): rudimentary interface; don't interact very well with
tcltk (Rcmdr) on Linux
- relimp::showData(): nice viewer, but too often too unstable; lacks
most of the features above
- tcltk2::tk2edit(): allows multiple row/col/cell selections; has no
'view' mode nor 'filter' or 'sort', and pops up scary errors whenever
'cancelling' changes
- rdataviewer: the interface is too exotic for my habits, and lacks
most of the features above
- RGtk2Extras::dfedit(): allows multiple cell selections and can
perform a 'sort' and displays several features in the c-menu; it is
quite slow on my system with 'iris', no 'view' mode; since it uses
RGtk2 it interacts very badly with tcltk (Rcmdr)
- playwith: features a nice viewer hidden in Labels > Select from
table, but it is awkwardly located, with no advanced features, and
RGtk2 based
- JGR/Deducer::data.viewer(): includes a nifty data editor, perhaps my
favourite from this list, but it is a bit awkward to use when using an
editor different than JGR (console monopolization); it also lacks a
'view' and 'filter' mode as well well as select multiple rows/cols; it
can sort, but not easily when outside JGR


Anything that I've missed? Regards
Liviu
#
Liviu Andronic <landronimirc <at> gmail.com> writes:
The following uses gWidgets to create something close to what you want. I didn't
add in the editing feature, but that can be had with gdf in place of gtable. (as
well, the filter and sort dialogs could be much improved) The gdf widget isn't
nearly as nice as the Deducer one or the RGtk2Extras one though.


liviu <- function(df,
                  container=gwindow(sprintf("Viewing %s", deparse(substitute(df)))),
                  ...) {

  ##' return logical of length x
  filterFun <- function(x, expr, ...) UseMethod("filterFun")
  filterFun.default <- function(x, expr, ...) x %in% expr
  filterFun.numeric <- function(x, expr, ...) x < as.numeric(expr) # modify
  
  filterDialog <- function() {
    dlg <- gbasicdialog("Filter by:", handler=function(h,...) {
      val <- svalue(filterExpression)
      var <- svalue(variableSelector)
      if(var == "") {
        ## evaluate expression within df
        ind <- with(tbl[], eval(parse(text=val)))
      } else {
        ind <- filterFun(tbl[][[var]], val)
      }
      if(is.logical(ind) && !any(is.na(ind)))
        visible(tbl) <- ind
      updateTbl()
    })
    ## layout
    gp <- ggroup(cont=dlg) 
    filterExpression <- gedit("", cont=gp)
    variableSelector <- gcombobox(c("",names(df)), cont=gp)
    ## show
    visible(dlg, set=TRUE)
  }

  sortDialog <- function() {
    dlg <- gbasicdialog("Sort by:", handler=function(h,...) {
      vars <- sortVars[,1]
      if(length(vars) && !any(is.na(vars))) {
        ind <- order(subset(df, select=vars), 
            decreasing=!svalue(sortOrder))
        tbl[,] <- df[ind,]
        visible(tbl) <- rep(TRUE, dim(tbl)[1])
        updateTbl()
      }
    })
    ## layout
    size(dlg) <- c(400,400)
    nms <- data.frame(Variables=names(df), stringsAsFactors=FALSE)
    nms1 <- data.frame("Sort by"=character(0), stringsAsFactors=FALSE)
    ind <- c()

    gp <- ggroup(cont=dlg)
    allVars <- gtable(nms, cont=gp, expand=TRUE)
    bg <- ggroup(horizontal=FALSE, cont=gp)
    lbutton <- gbutton("<", cont=bg)
    rbutton <- gbutton(">", cont=bg)
    sortOrder <- gcheckbox("Increasing", checked=TRUE, 
          use.togglebutton=TRUE, cont=bg)
    sortVars <- gtable(nms1, cont=gp, expand=TRUE)

    addHandlerClicked(rbutton, function(h,...) {
      vars <- svalue(allVars, index=TRUE)
      if(!is.null(vars)) {
        ind <<- c(ind, vars)
        sortVars[,] <- nms[ind,]
      }
    })
    addHandlerClicked(lbutton, function(h,...) {
      vars <- svalue(sortVars, index=TRUE)
      if(!is.null(vars)) {
        ind <<- setdiff(ind, vars)
         sortVars[,] <- nms[ind,]
      }
    })

    ## show
    visible(dlg, set=TRUE)
  }

  ## layout
  g <- ggroup(cont=container, horizontal=FALSE)
  tbl <- gtable(df,  cont=g, expand=TRUE, multiple=TRUE, 
     filter.FUN="manual")
  gp <- ggroup(cont=g)
  gbutton("Filter", cont=gp, handler=function(h,...) {
    filterDialog()
  })
  gbutton("Sort...", cont=gp, handler=function(h,...) {
    sortDialog()
  })
  addSpring(gp)
  caseLabel <- glabel("", cont=gp)
  
  updateTbl <- function() {
    ## update label for no cases, ...
    svalue(caseLabel) <- sprintf("%s cases", sum(visible(tbl)))
    
  }

  updateTbl()



  
  tbl
}

## test it
tbl <- liviu(mtcars)