Search code examples
rdataframematrixcat

`print` function, how to include/exclude colnames or position them?


Motivation

The head function of base doesn't have a SKIP or RANGE feature, so I was writing a function to do that:

df.printHead = function(df, n=6, row.idx=1, ...)
    {
    idx = row.idx;          ## offset, e.g, SKIP
    nrow = nrow(df);
    # tails             
        lower = (idx - n);  if(lower < 1)    { lower = 1; }
        upper = lower + n;  if(upper > nrow) { upper = nrow; }
                            if(upper >= idx) { upper = idx - 1; }
    tails = df[ lower:upper, ];  rownames(tails) = lower:upper;
    print(tails, col.names=TRUE, col.at="top", ...);
    cat("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", "\n");
    # one
        one = df[idx, ]; rownames(one) = idx;
    print(one, col.names=FALSE, ...);
    cat("\n");
    cat("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", "\n"); 
    # heads     
        upper = (idx + n);  if(upper > nrow) { upper = nrow; }
        lower = upper - n;  if(lower < 1) { lower = 1; }
                            if(lower <= idx) { lower = idx + 1; }
    heads = df[ lower:upper , ];    rownames(heads) = lower:upper;
    print(heads, col.names=TRUE, col.at="bottom", ...);
    invisible(list("tails"=tails, "one"=one, "heads"=heads));
    }

Examples (iris)


dim(iris);
df.printHead(iris, n=6, row.idx=4);
df.printHead(iris, n=6, row.idx=148);

What output I get


    Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
142          6.9         3.1          5.1         2.3 virginica
143          5.8         2.7          5.1         1.9 virginica
144          6.8         3.2          5.9         2.3 virginica
145          6.7         3.3          5.7         2.5 virginica
146          6.7         3.0          5.2         2.3 virginica
147          6.3         2.5          5.0         1.9 virginica
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
148          6.5           3          5.2           2 virginica

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
149          6.2         3.4          5.4         2.3 virginica
150          5.9         3.0          5.1         1.8 virginica



What output I want


    Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
142          6.9         3.1          5.1         2.3 virginica
143          5.8         2.7          5.1         1.9 virginica
144          6.8         3.2          5.9         2.3 virginica
145          6.7         3.3          5.7         2.5 virginica
146          6.7         3.0          5.2         2.3 virginica
147          6.3         2.5          5.0         1.9 virginica
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

148          6.5           3          5.2           2 virginica

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

149          6.2         3.4          5.4         2.3 virginica
150          5.9         3.0          5.1         1.8 virginica
    Sepal.Length Sepal.Width Petal.Length Petal.Width   Species

The alignment left-to-right is nice, since all of the data in print is coming from the same dataframe. I am aware I can use cat and fully control the output.

I am searching for a print solution as it best integrates with base

https://rdrr.io/r/base/print.dataframe.html

This calls format which formats the data frame column-by-column, 
  then converts to a character matrix and dispatches 
    to the print method for matrices.

Question

I have tried col.names=TRUE inside of print I have also added col.at="bottom" and DEFAULT col.at="top" for the location if TRUE

print has the dots ... so do I need to write my own Rcpp code or something to get this to work?

Can I extend the print.matrix formatting that works internally in R? Can I write my own formatter that hooks into the base? If so how?


Solution

  • We could utilize capture.output() and restructure it with cat(..., sep = "\n"):

    df.printHead = function(df, n=6, row.idx=1, ...)
    {
      idx = row.idx;          ## offset, e.g, SKIP
      nrow = nrow(df);
      # tails             
      lower = (idx - n);  if(lower < 1)    { lower = 1; }
      upper = lower + n;  if(upper > nrow) { upper = nrow; }
      if(upper >= idx) { upper = idx - 1; }
      tails = df[ lower:upper, ];  rownames(tails) = lower:upper;
      print(tails);
      cat("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", "\n");
      # one
      one = df[idx, ]; rownames(one) = idx;
      printone <- capture.output(print(one));
      cat("\n");
      cat(printone[-1], sep = "\n");
      cat("\n");
      cat("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", "\n"); 
      # heads     
      upper = (idx + n);  if(upper > nrow) { upper = nrow; }
      lower = upper - n;  if(lower < 1) { lower = 1; }
      if(lower <= idx) { lower = idx + 1; }
      heads = df[ lower:upper , ];    rownames(heads) = lower:upper;
      printheads <- capture.output(print(heads));
      cat(printheads[-1], sep = "\n");
      cat(printheads[1], sep = "\n");
      invisible(list("tails"=tails, "one"=one, "heads"=heads));
    }
    

    Output:

    df.printHead(iris, n=6, row.idx=148)
    
        Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
    142          6.9         3.1          5.1         2.3 virginica
    143          5.8         2.7          5.1         1.9 virginica
    144          6.8         3.2          5.9         2.3 virginica
    145          6.7         3.3          5.7         2.5 virginica
    146          6.7         3.0          5.2         2.3 virginica
    147          6.3         2.5          5.0         1.9 virginica
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    
    148          6.5           3          5.2           2 virginica
    
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    149          6.2         3.4          5.4         2.3 virginica
    150          5.9         3.0          5.1         1.8 virginica
        Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
    

    If you want you could of course make a function out of taking the arguments col.names and col.at. + There is no need for all your ;'s + <- is usually preferred over =.