Keyboard driven pager of data.tables
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 25/100
Research direction
Start with the pager function and its num_lines and num_cols helpers in the issue body. Assess how terminal width is detected and how horizontal movement could coexist with the existing j/k and g/G commands. Done would mean a defined, tested pager behavior for wide data.tables, including terminal resizing and horizontal scrolling.
Written by the indexing model from the issue text.
Description
I wrote this because R's built-in page is SLOW for large data.tables (also, apparently, page doesn't do what I thought it did). Combine that with the fact that I'm not a big user of the various R gui's (e.g. RStudio), but I do, occasionally, want to see my actual data, and I decided to create a little pager for data.tables. I've been using this hack, which is a less-like interface for data.tables for a while now. Unfortunately, it only scrolls up and down, not left and right, which works for me as my data is usually long and narrow. But it's definitely a shortcoming.
To get horizontal scrolling working, what should probably be done is only print the columns that fit in the width of the terminal with some indicator that there are more columns to the left/right, with maybe an option to "freeze" the key columns so they don't scroll off the page when moving right (though that assumes the key columns are already organized to be to the left of the data.table). This would prevent the extra columns from being printed below the others, which otherwise really screws with the utility of the pager.
In any case, I don't think this is clean enough to warrant a pull request (I suspect the way I'm getting the number of rows/columns for the terminal is linux specific), and I have no idea how to make test cases for it. But it's been sitting in my collection of scripts for a while and thought I should share it. If someone has some ideas on how to get the width of that data.table in at least the current range and horizontal scrolling working, that might make it more worthy of inclusion.
num_lines = function(default=24) {
x = Sys.getenv("LINES")
if (grepl('^[0-9]+$', x)) {
return(as.integer(x))
} else {
x = system2('tput', 'lines', stdout=T)
if (grepl('^[0-9]+$', x)) {
return(as.integer(x))
} else {
return(default)
}
}
}
num_cols = function(default=80) {
x = Sys.getenv("COLUMNS")
if (grepl('^[0-9]+$', x)) {
return(as.integer(x))
} else {
x = system2('tput', 'cols', stdout=T)
if (grepl('^[0-9]+$', x)) {
return(as.integer(x))
} else {
return(default)
}
}
}
#' A pager built for data.table
#'
#' There are 4 basic commands: j, k, G, g.
#' k/j: move up and down a page at a time
#' G/g: move to the bottom/top of the object
#' q: quit
#'
#' k/j can be modified with a number. by default, the pager will go down one
#' page. If you provide a number along with j/k it will move that number of
#' rows down. So "1j" would move just 1 row down instead of a full page.
#' "500j" would move down 500 rows. The counting starts from the top row.
#' Similary for up using 'k'. This will not wrap around/go beyond the beginning
#' or end of the object.
#'
#' A simple enter implies the previous command, or just 'j' if no command has
#' yet been entered
pager = function(dt) {
cur_width = options()$width
options(width=num_cols())
bufferlines = 3 # take into account header row and such
termheight = num_lines() - bufferlines
ndt = nrow(dt)
if (ndt < termheight) {
print(dt)
return(NULL)
}
state = 1
command = 'j'
num = termheight
while (TRUE) {
# use data.frame so that the row names with the actual row numbers are printed
dt2 = as.data.frame(dt[state:(state+termheight),])
rownames(dt2) = state:(state+termheight)
print(dt2)
if (state + termheight >= ndt) {
r = readline("End of Object. ")
} else {
r = readline(': ')
}
if (r == '') { # repeat previous command
r = sprintf('%s%s', num, command)
}
termheight = num_lines() - bufferlines # in case terminal resized
# set state for next print
if (tolower(r) == 'q') {
break
} else if (r == 'g') {
state = 1
command = 'g'
num = ''
} else if (r == 'G') {
state = ndt - termheight
command = 'G'
num = ''
} else if (grepl('j', r, ignore.case=T)) {
command = 'j'
num = gsub('j', '', r, ignore.case=T)
if (num == '') {
num = termheight + 1
} else {
num = as.integer(num)
}
if (num + state + termheight >= ndt) {
state = ndt - termheight
} else {
state = state + num
}
} else if (grepl('k', r, ignore.case=T)) {
command = 'k'
num = gsub('k', '', r, ignore.case=T)
if (num == '') {
num = termheight + 1
} else {
num = as.integer(num)
}
if (state - num < 1) {
state = 1
} else {
state = state - num
}
} else {
cat('illegal option.\n')
break
}
}
options(width=cur_width)
}
- Dominant language
- R
- Stars
- 3.9k
- Forks
- 1.1k
- Avg merge
- 14h 4m
- Merged PRs (30d)
- 4
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from Rdatatable/data.table
-
as.data.table() recurses without end on a survival::Surv object (or any data.frame carrying one) Open
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
Rdatatable/data.table#7887 ·
-
consistency tests
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
Rdatatable/data.table#7853 · 3 comments ·
-
internals
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
Rdatatable/data.table#6938 · 1 comment ·
-
encoding fread
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
Rdatatable/data.table#5179 · 8 comments ·
-
documentation programming
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
Rdatatable/data.table#3199 · 3 comments ·
All issues in Rdatatable/data.table
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
r-lib/pkgdepends#485 · 3 comments ·
-
Difficulty 1/5 Under an hour Newbie friendliness 92/100
-
beginners blocker
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
enviPathR OpenBuild Error Build OK Build Warning policies-accepted pre-review precheck-passed
Difficulty 1/5 Under an hour Newbie friendliness 84/100
Bioconductor/BiocContributions#207 · 6 comments ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
datacarpentry/semester-biology#1255 ·