PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0
Showing posts with label r-package. Show all posts
Showing posts with label r-package. Show all posts

Friday, September 16, 2022

[FIXED] How to custom print/show variables (with custom class) in my R package

 September 16, 2022     console, methods, printing, r, r-package     No comments   

Issue

Many R functions return objects that are printed to the console in a special manner. For instance, t_results = t.test(c(1,2,3), c(1,2,4)) will assign a list to the t_results variable, but when I enter this variable in the console, or call it as print(t_results) or show(t_results), it prints some plain text information (such as Welch Two Sample t-test... etc.) instead of returning the actual list. (This is a base R function, but I've seen this implemented in many custom user R packages just as well.)

My question is: how do I do this for objects created in my own custom R package? I've read several related questions and answers (e.g., this, this, and this), which do give a general idea (using setMethod for my custom classes), but none of them makes it clear to me what exactly I need to do to make it work properly in a custom R package. I also cannot find any official documentation or tutorial on the matter.

To give an example of what I want to do, here is a very simple function from my hypothetical R package, which simply return a small data.frame (with an arbitrary class name I add to it, here 'my_df_class'):

my_main_function = function() {
    my_df = data.frame(a = c('x1', 'y2', 'z2'),
                       b = c('x2', 'y2', 'z2'))
    class(my_df) = c(class(my_df), 'my_df_class')
    return(my_df)
}

I would like to have this printed/shown e.g. like this:

my_print_function = function(df) {
    cat('My results:', df$a[2], df$a[3])
}
# see my_print_function(my_main_function())

What exactly has to be done to make this work for my R package (i.e., that when someone installs my R package, assigns the my_main_function() results to a variable, and prints/shows that variable, it would be done via my_print_function())?


Solution

Here is a small explanation. Adding to the amazing answer posted by @nya:

First, you are dealing with S3 classes. With these classes, we can have one method manipulating the objects differently depending on the class the object belongs to.

Below is a simple class and how it operates:

  1. Class contains numbers,
  2. The class values to be printed like 1k, 2k, 100k, 1M,
  3. The values can be manipulated numerically.

-- Lets call the class my_numbers

Now we will define the class constructor:

 my_numbers = function(x) structure(x, class = c('my_numbers', 'numeric'))

Note that we added the class 'numeric'. ie the class my_numbers INHERITS from numeric class

We can create an object of the said class as follows:

b <- my_numbers(c(100, 2000, 23455, 24567654, 2345323))
b 
[1]      100     2000    23455 24567654  2345323
attr(,"class")
[1] "my_numbers" "numeric" 

Nothing special has happened. Only an attribute of class has been added to the vector. You can easily remove/strip off the attribute by calling c(b)

c(b)
[1]      100     2000    23455 24567654  2345323

vector b is just a normal vector of numbers.

Note that the class attribute could have been added by any of the following (any many more ways):

 class(b) <- c('my_numbers', 'numeric')
 attr(b, 'class') <- c('my_numbers', 'numeric')
 attributes(b) <- list(class = c('my_numbers', 'numeric'))

Where is the magic?

I will write a simple function with recursion. Don't worry about the function implementation. We will just use it as an example.

my_numbers_print = function(x, ..., digs=2,  d = 1,  L =   c('', 'K', 'M', 'B', 'T')){
  ifelse(abs(x) >= 1000, Recall(x/1000, d = d + 1),
         sprintf(paste0('%.',digs,'f%s'), x, L[d]))
}

my_numbers_print(b)
[1] "100.00" "2.00K"  "23.45K" "24.57M" "2.35M" 

There is no magic still. Thats the normal function called on b.

Instead of calling the function my_numbers_print we could write another function with the name print.my_numbers ie method.class_name (Note I added the parameter quote = FALSE

print.my_numbers = function(x, ..., quote = FALSE){
   print(my_numbers_print(x), quote = quote)
 }
   
 b
[1] 100.00 2.00K  23.45K 24.57M 2.35M 

Now b has been printed nicely. We can still do math on b

 b^2
 [1] 10.00K  4.00M   550.14M 603.57T 5.50T 

Can we add b to a dataframe?

data.frame(b)
         b
1      100
2     2000
3    23455
4 24567654
5  2345323 

b reverts back to numeric instead of maintaining its class. That is because we need to change another function. ie the formats function.

Ideally, the correct way to do this is to create a format function and then the print function. (Becoming too long)


Summary : Everything Put Together

# Create a my_numbers class definition function
my_numbers = function(x) structure(x, class = c('my_numbers', 'numeric'))

# format the numbers
format.my_numbers =  function(x,...,digs =1, d = 1,  L =   c('', 'K', 'M', 'B', 'T')){
      ifelse(abs(x) >= 1000, Recall(x/1000, d = d + 1),
         sprintf(paste0('%.',digs,'f%s'), x, L[d]))
}

#printing the numbers
print.my_numbers = function(x, ...) print(format(x), quote = FALSE)

# ensure class is maintained after extraction to allow for sort/order etc
'[.my_numbers' = function(x, ..., drop = FALSE)  my_numbers(NextMethod('['))


b <- my_numbers(c(2000, 100, 20, 23455, 24567654, 2345323))

data.frame(x = sort(-b) / 2)                     
   
       x
1 -12.3M
2  -1.2M
3 -11.7K
4  -1.0K
5  -50.0
6  -10.0


Answered By - onyambu
Answer Checked By - Clifford M. (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

Thursday, August 25, 2022

[FIXED] How to include and display images in R package?

 August 25, 2022     module, package, r, r-package, shinymodules     No comments   

Issue

I am trying to package a Shiny module that displays a modal with a logo (in png format). For this purpose, I have created a "inst/www" directory to store the logo file. The directory tree looks something like this:

├─ DESCRIPTION
├─ inst
│   └── www
│       └── logo.png
├── man
│   └── test.Rd
├── NAMESPACE
├── packagetest.Rproj
└── R
    └── test.R

However, after building and installing, the package doesn't seem to read from the predefined directory where I put my "logo.png". Instead, it reads from the main project where I inserted the function from the package. The testUI() function of the package is something like this:

testUI <- function(id) {
  ns <- NS(id)

  shiny::showModal(
    modalDialog(
      title = img(src="inst/www/logo.png", style="display:block; margin-left:auto; margin-right:auto;"),
      br(),
      fluidRow(
        column(6, align="center", offset = 3,
               textInput(ns("username"), label = NULL, placeholder = "Username"),
               passwordInput(ns("password"), label = NULL, placeholder = "Password")
        )
      ),
      footer = (
        fluidRow(
          column(6, align="center", offset = 3,
                 actionButton(ns("signin"),"Sign in")
          )
        )
      )
    )
  )
}

From what I've seen in other projects, the "inst" folder seems to be the way to go but I'm still new to R packages so I don't really know what I'm doing. Any help on this is much appreciated, thanks!


Solution

...and almost a year later, I found the answer to my question while working on an actual package that needs to include image assets! According to the official R package documentation section on the inst folder:

To find a file in inst/ from code use system.file(). For example, to find inst/extdata/mydata.csv, you’d call system.file("extdata", "mydata.csv", package = "mypackage"). Note that you omit the inst/ directory from the path. This will work if the package is installed, or if it’s been loaded with devtools::load_all().

In this case, assuming my package is called "testpackage":

testUI <- function(id) {
  ns <- NS(id)

  shiny::showModal(
    modalDialog(
      title = img(
                src=system.file("www/logo.png", package = "testpackage"),
                style="display:block; margin-left:auto; margin-right:auto;"
      ),
      br(),
      fluidRow(
        column(6, align="center", offset = 3,
               textInput(ns("username"), label = NULL, placeholder = "Username"),
               passwordInput(ns("password"), label = NULL, placeholder = "Password")
        )
      ),
      footer = (
        fluidRow(
          column(6, align="center", offset = 3,
                 actionButton(ns("signin"),"Sign in")
          )
        )
      )
    )
  )
}

Note that I didn't use the comma separated directory structure as shown in the official example as I found that it's more intuitive to enter the full relative path, keeping in mind that:

When a package is installed, everything in inst/ is copied into the top-level package directory. In some sense inst/ is the opposite of .Rbuildignore - where .Rbuildignore lets you remove arbitrary files and directories from the top level, inst/ lets you add them. You are free to put anything you like in inst/ with one caution: because inst/ is copied into the top-level directory, you should never use a subdirectory with the same name as an existing directory. This means that you should avoid inst/build, inst/data, inst/demo, inst/exec, inst/help, inst/html, inst/inst, inst/libs, inst/Meta, inst/man, inst/po, inst/R, inst/src, inst/tests, inst/tools and inst/vignettes.

Hope this helps anyone else that's facing the same issue :)



Answered By - fabian_h
Answer Checked By - Mildred Charles (PHPFixing Admin)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg

Saturday, May 14, 2022

[FIXED] How can I use R function download.file?

 May 14, 2022     install.packages, r, r-package, ubuntu     No comments   

Issue

I've tried to run this command:

url <- 'http://www.rob-mcculloch.org/chm/nonlinvarsel_0.0.1.9001.tar.gz'
download.file(url, destfile = 'temp')
install.packages('temp', repos = NULL, type='source')

But at first, when I ran download.file, that returned me:

Warning messages: 1: In download.file(url, destfile = "temp") : URL http://www.rob-mcculloch.org/chm/nonlinvarsel_0.0.1.9001.tar.gz: cannot open destfile 'temp', reason 'Is a directory' 2: In download.file(url, destfile = "temp") : download had nonzero exit status

So I tried to run install.packages and that returned me:

Installing package into ‘/usr/local/lib/R/site-library’ (as ‘lib’ is unspecified) Warning: invalid package ‘temp’ Error: ERROR: no packages specified Warning message: In install.packages("temp", repos = NULL, type = "source") : installation of package ‘temp’ had non-zero exit status


Solution

You don't have to do it from inside R (though that is doable, just look more close at options and see below). I actually like the shell for this:

/tmp> wget http://www.rob-mcculloch.org/chm/nonlinvarsel_0.0.1.9001.tar.gz
--2022-04-11 03:03:59--  http://www.rob-mcculloch.org/chm/nonlinvarsel_0.0.1.9001.tar.gz
Resolving www.rob-mcculloch.org (www.rob-mcculloch.org)... 162.241.219.65
Connecting to www.rob-mcculloch.org (www.rob-mcculloch.org)|162.241.219.65|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 20001 (20K) [application/x-gzip]
Saving to: ‘nonlinvarsel_0.0.1.9001.tar.gz’

nonlinvarsel_0.0.1.9001.tar.gz                       100%[=====================================================================================================================>]  19.53K  --.-KB/s    in 0.03s   

2022-04-11 03:03:59 (634 KB/s) - ‘nonlinvarsel_0.0.1.9001.tar.gz’ saved [20001/20001]

/tmp> R CMD INSTALL nonlinvarsel_0.0.1.9001.tar.gz
* installing to library ‘/usr/local/lib/R/site-library’
* installing *source* package ‘nonlinvarsel’ ...
** using staged installation
** R
** data
*** moving datasets to lazyload DB
** byte-compile and prepare package for lazy loading
** help
*** installing help indices
** building package indices
** testing if installed package can be loaded from temporary location
** testing if installed package can be loaded from final location
** testing if installed package keeps a record of temporary installation path
* DONE (nonlinvarsel)
/tmp> 

Or equally within R (where I also switch to /tmp) first:

> setwd("/tmp")
> download.file("http://www.rob-mcculloch.org/chm/nonlinvarsel_0.0.1.9001.tar.gz", "nonlinvarsel_0.0.1.9001.tar.gz")
trying URL 'http://www.rob-mcculloch.org/chm/nonlinvarsel_0.0.1.9001.tar.gz'
Content type 'application/x-gzip' length 20001 bytes (19 KB)
==================================================
downloaded 19 KB

> install.packages("nonlinvarsel_0.0.1.9001.tar.gz", repos = NULL)
Installing package into ‘/usr/local/lib/R/site-library’
(as ‘lib’ is unspecified)
* installing *source* package ‘nonlinvarsel’ ...
** using staged installation
** R
** data
*** moving datasets to lazyload DB
** byte-compile and prepare package for lazy loading
** help
*** installing help indices
** building package indices
** testing if installed package can be loaded from temporary location
** testing if installed package can be loaded from final location
** testing if installed package keeps a record of temporary installation path
* DONE (nonlinvarsel)
> 


Answered By - Dirk Eddelbuettel
Answer Checked By - Willingham (PHPFixing Volunteer)
Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Older Posts Home

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
All Comments
Atom
All Comments

Copyright © PHPFixing