TIBCO Spotfire Community

Welcome to TIBCO Spotfire Community Sign in | Join | Help

Differences Between R and Spotfire S+

R and Spotfire S+ have many differences. Functions with the same names might have different arguments or defaults might be set differently.  Other significant differences might exist. This paper discusses some of the important distinctions between R and Spotfire S+. If you see functions in R that you would like to see implemented in Spotfire S+, please contact Technical Support at http://spotfire.tibco.com/support.

Arithmetic

64-bit Arithmetic

On 64-bit systems, Spotfire S+ supports 64-bit integer arithmetic; R supports only 32-bit integer arithmetic, even on 64-bit systems. Both R and Spotfire S+ support 64-bit addressing on 64-bit systems.

Differences in Complex Arithmetic

The following differences occur between the results of certain R and Spotfire S+ complex math functions.

b2 <- c(-1+0i, 1+0i)
Z <- 1.25+0i

Function Spotfire S+ R
atanh(b2) NA NA -Inf+0i Inf+0i
Arg(-b2) 0.000000 3.141593 0.000000 -3.141593
sign(b2) -1+0i 1+0i Error in sign(b2) : unimplemented complex function
asin(Z) 1.570796-0.6931472i 1.570796+0.693147i
acosh(Z) -0.6931472+0i 0.6931472+0i

Notice that several of the differences are differences in sign (+/1)

C/Fortran Interface

Differences Between Spotfire S+ and R for the .Call() Function

There are differences between R and Spotfire S+ in how .Call() copies its arguments: The R version of .Call() never copies its arguments. The Spotfire S+ version of .Call() copies an argument if it is all or part of a named object. That is, .Call() cannot change any objects with names. Spotfire S+ lets you specify COPY=FALSE or to register the routine with setInterface to avoid the copy. Pointers to arguments will not remain valid from one .Call to another.

The first argument to .Call() can be unquoted in R, but it must be a quoted string in Spotfire S+. In R, the symbol name in the .Call() can be specified as unquoted:

  arimaSS <- function(y, mod)
  {
     .Call(R_ARIMA_Like, y, mod$phi, mod$theta,
        mod$Delta, mod$a, mod$P, mod$Pn, as.integer(0), TRUE)
  }

In Spotfire S+, the symbol name must be a string, so the first argument is quoted:

  arimaSS <- function(y, mod)
  {
     ## next call changes objects a, P, Pn so beware!
     .Call("R_ARIMA_Like", y, mod$phi, mod$theta, mod$Delta,
       mod$a, mod$P, mod$Pn, as.integer(0), TRUE)
  }

Porting C Code Using Rinternals.h and Rdefines.h

Some C code within R packages is written to manipulate internal R objects, such as SEXP structures. Typically, this code includes the files Rinternals.h or Rdefines.h and references structures and macros defined in these files. To make it easier to port C code from R to Spotfire S+, we have added the files Rinternals.h and Rdefines.h in the Spotfire S+ include directory. These files contain macro definitions that map some common R functions and macros to corresponding ones supported by Spotfire S+.

Even when you use these include files, most of the time, warnings and errors are generated when compiling the C code. One common problem is that R encodes integer values as C type int, whereas Spotfire S+ uses the C type long. Usually, these are the same on 32-bit architectures, but it is still important to examine warnings when one type is used instead of the other. For example, the R INTEGER(x) macro returns an int*, whereas Spotfire S+ returns a long*, and compiling the code

  int* x = INTEGER(y)

could give the following warning:

  warning: assignment from incompatible pointer type

There are many R functions and data names that do not have any meaning in Spotfire S+. For some such functions, Rinternals.h includes a macro definition to change the function call to an undefined variable, so it is easier for the user to understand the problem. For example, using R_GlobalEnv produces an error:

  'unsupported_R_entry_R_GlobalEnv' undeclared

Graphics

Log Scale for x- and y-axis Specified Differently

In R, the xlog value in par() indicates whether the current plot has a log x-axis. In Spotfire S+, the xaxt value in par() is "l" if it is a log axis. Similarly, R has ylog, and Spotfire S+ has yaxt.

The following type of code makes the output the same:

  if (is.R()){
    par("xlog")
  }
  else {
    (par("xaxt")=="l")
  }

pairs() and dotchart() main Argument

The pairs() and dotchart() functions in R have a "main" argument. The same functions in Spotfire S+ do not.

I/O

T and F

In R, T and F are not reserved words (TRUE and FALSE are) and are global variables. Spotfire S+ reserves T, F, TRUE, and FALSE. It is strongly recommended that you not use T and F as variable names.

Differences in format.default()

In R, format.default() has the following arguments that are not present in format.default() in Spotfire S+:

  • trim
  • width
  • na.encode
  • zero.print

Differences in read.table

In R, the sep argument of read.table() cannot be a vector; in Spotfire S+ it can.

Language

Parsing Differences

In R, object names can include underscores ("_"), and the underscore is not used as an assignment operator; in Spotfire S+, an underscore means assignment (although this behavior is deprecated in Spotfire S+ 8.1).

You can set the default to use either the R or S parser with set.parse.mode() or parse(). See the Spotfire S+ help topic for set.parse.mode() for more information about the differences.

Example
   set.parse.mode("R")
or
   parse(mode="R", ...)

Use either mode="Splus" or 0 to parse in Spotfire S+ mode, and "R" or 1 to parse in R mode. The default mode is given by getenv("S_PARSER_MODE"). To see the current parse mode without changing it, type:

  set.parse.mode(mode=-1).

Optionally, You can set the parser to parse in R mode to read in R functions and then set it back to Spotfire S+ mode to convert object names automatically. For example:

  prev.parse.mode <- set.parse.mode("R")
  source("my_fun.R") # assume this creates my_fun
     set.parse.mode(prev.parse.mode)
     dump("my_fun", "my_fun.q")

R parses numbers without decimal points as reals, unless you explicitly append an L. Spotfire S+ parses numbers without decimal points as integers (as long as they are less than or equal to 2147483647).

In R:

  > class(1)
  [1] "numeric"
  > class(1L)
  [1] "integer"
  > class(2147483647)
  [1] "numeric"
  > class(2147483648)
  [1] "numeric"

In Spotfire S+:

  > class(1)
  [1] "integer"
  > class(1L)
  [1] "integer"
  > class(2147483647)
  [1] "integer"
  > class(2147483648)
  [1] "numeric"

Spotfire S+ can be set to mimic R's behavior in this regard by the command

  set.parse.mode(mode="R")

Namespaces

Spotfire S+ does not support namespaces, so the value of either package::name or package:::name is just name.

Data Object Storage

In Spotfire S+, objects are stored as files in the .Data directory. In R, objects are stored in memory and might be stored in one big file when you exit R.

as.data.frame Additional Arguments

The as.data.frame function in Spotfire S+ has additional arguments to control how arrays are converted to data frames. Using row.dims=1 in Spotfire S+ makes the output like R's output.

Vector Length Matching

1:5 + 1:2 succeeds with a warning in R, but it fails in Spotfire S+. The length of one argument must be a multiple of the length of the other for the arithmetic operators.

Assignment Operators

  • In R, you use x <<- newValue to find the current definition of x and put its new value in the same place. If x does not exist, put it in .GlobalEnv.
  • In Spotfire S+, you use x <<- newValue to put x on the working database (a permanent data set).

Functions Return the Results of an Assignment Invisibly

Consider the following function:

  test<-function()
  {
    x <- 3
  }

In Spotfire S+, running test() results in

[1] 3

In R, no return value is printed unless you wrap the call in print():

print(test())

String Manipulation Functions in Core R but not in Spotfire S+

These string manipulation functions are in core R, but not in Spotfire S+:

  • agrep
  • gregexpr
  • strtrim
  • substr
  • substr<-
  • toString
  • toString.default
  • chartr
  • apropos

Differences in unique()

R has matrix and array methods for unique(). Spotfire S+ does not.

Spotfire S+ Does Not Let You Remove All Columns From data.frame with [ ] (bug)

The following code produces a 3x0 data frame in R. It produces an error in Spotfire S+:

  x <- data.frame(a=1:3); x[,-1]

remove() Behaves Differently in R and Spotfire S+

In R:

  > args(remove)
  function (..., list = character(0), pos = -1, envir = as.environment(pos), inherits = FALSE)

In Spotfire S+:

  > args(remove)
  function(list, frame, where, meta = 0)
  NULL

This difference results in the following behavior in Spotfire S+ (which works):

  x <- 1
  y <- 1
  remove(c("x", "y"))

The following fails in R:

  > x <- 1
  > y <- 1

  > remove(c("x", "y"))
  Error in remove(c("x", "y")) :
  ... must contain names or character strings

The following code works in R:

  > remove(x, y)

In Spotfire S+, it produces the following failure:

  remove(x, y)
  Problem in remove(x, y): need names of objects to remove, not the objects themselves
  Use traceback() to see the call stack

The following code works in both Spotfire S+ and R:

  remove(list=c("x", "y"))

This difference is a compatibility issue.

Differences in Missing Values

The missing value bit position differs between R and Spotfire S+. The missing value code is a NaN in both systems. To see the codes:

Spotfire S+: > writeBin(as.double(NA), pipe("od -x"))
Spotfire S+: 0000000 0001 7ff0 0001 7ff0
Spotfire S+: 0000010
R: > tfile<-tempfile()
R: > writeBin(as.double(NA), tfile)
R: > cat(system(paste("od -x < ", tfile)), sep="\\n")
     # system replaces unix in R

R: 0000000 07a2 0000 0000 7ff8

Object Oriented Programming

Differences in class()

In R, class() can return a vector; in Spotfire S+, the result always has length 1.

typeof() in R

R has a typeof() function that returns S4 when appropriate, and otherwise acts something like class() or storage.mode().

Differences in S4

In R, S4 objects can have attributes without changing their classes.

Random Numbers

How to Get the Same Random Number Streams in Spotfire S+ and R

In general, the random number generators in R and in Spotfire S+ generate different streams of random numbers. The following code illustrates a method that you can use to generate the same stream of random numbers in R and in Spotfire S+ for a particular instance (that is, it works if you use runif, but not another random number generator).

In R:

 

 

  > RNGkind("Super-Duper")
  > set.seed(17)
  > dput(.Random.seed[-1])

  c(786472236L, -1895491523L)

  > # Now generate 12 random uniform numbers:
  > runif(12)
  [1] 0.30746849 0.82811554 0.65195342 0.98243238 0.42425382 0.79464223
  [7] 0.03540204 0.22236288 0.09750879 0.88091935 0.01319564 0.62546799

Use the above result ("c(786472236L, -1895491523L)") as an input to the function below to generate an equivalent .Random.seed for Spotfire S+'s random number generator:

In Spotfire S+:

  # Define the function
  "seed.RtoS" <- function(seed)
  {
     seed1 <- seed %% 2^16
     seed2 <- seed/seed1
     f <- function(k)
     {
        if(k < 0)
           k <- k + 2^32
        result <- rep(0, 6)
        for(i in 1:6) {
           result[i] <- k %% 64
           k <- k %/% 64
        }
        result
     }
     as.integer(c(f(seed[2]), f(seed[1])))
  }

Now use this function to set up Spotfire S+'s random number generator. Note that we use the argument generated in R as input.

  > RNGkind("Super-Duper")
  > set.seed(seed.RtoS(c(786472236L, -1895491523L)))

  > # Now generate 12 random uniform numbers
  > runif(12)

  [1] 0.30746849 0.82811554 0.65195342 0.98243238 0.42425382 0.79464223
  [7] 0.03540204 0.22236288 0.09750879 0.88091935 0.01319564 0.62546799

Observe that these are the same numbers generated in R.

For different seeds in R, we would use the same procedure to generate the equivalent seeds in Spotfire S+.

Statistics/Modeling

model.matrix

This was noted while comparing code in R vs. Spotfire S+:

  d<-data.frame(f1=factor(rep(c("One","Two"),c(6,6)),
     levels=c("One","Two")),
           f2=factor(rep(c("I","II","III"),4),
     levels=c("I","II","III")),
  x=101:112, y=log(1:12))
  f<-function(form, data){
       z<-model.frame(form, data=data)
           zz<-model.matrix(attr(z, "terms"), z)
           invisible(dput(attr(zz, "assign")))
  }
  > f(y~., data=d)

In Spotfire S+, we get a list:

  named("(Intercept)" = 1, "f1" = 2, "f2" = c(3, 4), "x" = 5)

and in R we get an integer vector:

  c(0, 1, 2, 2, 3)

Here are the results for various formulae:

  Spotfire S+ > f(y~f2+x-1, data=d)
  named("f2" = c(1,2,3), "x" = 4)

  R> f(y~f2+x-1, data=d)
  c(1, 1, 1, 2)

  and

  Spotfire S+ > f(y~(f2+f1)^2, data=d)
  named("(Intercept)" = 1
    , "f2" = c(2, 3)
     , "f1" = 4
     , "f2:f1" = c(5, 6)
    )
 

  R > f(y~(f2+f1)^2, data=d)
  c(0, 1, 1, 2, 3, 3)

  and

  Spotfire S+ > f(y~f1*f2-1, data=d)
      named("f1" = c(1, 2)
  , "f2" = c(3, 4)
  , "f1:f2" = c(5, 6)
  )

  R > f(y~f1*f2-1, data=d)
  c(1, 1, 2, 2, 3, 3)

They both seem to encode the same information but in different ways.

R's model.frame Adds predvars attribute

model.frame() in R returns information that model.frame() in Spotfire S+ does not. In particular, the terms attribute of the result has an attribute, predvars, that is not in the Spotfire S+ output. It is like the variables attribute, but it includes information that is needed to make predictions from new datasets. model.frame.default() calls makepredictcall() to create this attribute. help(makepredictcall) provides documentation on its use.

It is important to know about this when porting R code that deals with modeling functions. You might want to use a similar scheme to make predictions involving poly() or ns() valid.

The following is an example comparing predvars and variables in the output of R's model.frame():

  > library("splines")
  > d<-data.frame(y=log(1:12),
      x=sqrt(1:12),
      letter=factor(rep(c("a","b"),c(6,6))),
      roman=factor(rep(c("I","II","III"),4)))

For "ordinary" terms in the model, predvars and variables are identical:

  > attr(attr(model.frame(log(y)~sqrt(x+1),data=d), "terms"), "variables")
  list(log(y), sqrt(x + 1))
  > attr(attr(model.frame(log(y)~sqrt(x+1),data=d), "terms"), "predvars")
  list(log(y), sqrt(x + 1))

But if your model has a term like bs(x) or poly(x) where there are other implied arguments whose default values depend on x, then predvars contains the calls you would need to use this with new data:

  > attr(attr(model.frame(y~bs(x),d), "terms"), "variables")
  list(y, bs(x))
  > attr(attr(model.frame(y~bs(x),d), "terms"), "predvars")
  list(y, bs(x, degree = 3, knots = numeric(0),
      Boundary.knots = c(1, 3.46410161513775), intercept = FALSE))

The arguments that makepredictcall() adds to bs() come from bs(d$x):

  > bs(d$x)
                   1          2           3
     [1,] 0.00000000 0.00000000 0.000000000
     ...
     [12,] 0.00000000 0.00000000 1.000000000

  > attr(,"degree")
  [1] 3
  > attr(,"knots")
  numeric(0)
  > attr(,"Boundary.knots")
  [1] 1.000000 3.464102
  > attr(,"intercept")
  [1] FALSE
  > attr(,"class")
  [1] "bs" "basis"

and trace(bs) shows how predict() uses the data in predvars:

  > trace(bs)
  > fit<-lm(y~bs(x), data=d)
  trace: bs(x)
  > predict(fit, newdata=data.frame(x=c(2,3)))
  trace: bs(x, degree = 3, knots = numeric(0),
    Boundary.knots = c(1, 3.46410161513775), intercept = FALSE)

            1        2
     1.391730 2.191604

Plotting the results shows that R outputs the correct prediction, and Spotfire S+ does not. (The predictions from new data are drawn as triangles, and they lie on the fitted data in R but not in Spotfire S+):

> plot(d$x, fitted(fit))
> points(c(2,3), predict(fit, newdata=data.frame(x=c(2,3))), pch=2, cex=2)
  trace: bs(x, degree = 3, knots = numeric(0),
  Boundary.knots = c(1, 3.46410161513775), intercept = FALSE)

In Spotfire S+, the trace shows just bs(x), with no extra arguments.

factanal()

The factanal() function has a covmat argument in R and covlist argument in Spotfire S+. To get code that works in both, specify just "cov" as the argument name:

  Harman74.FA <- factanal(factors = 1, cov = Harman74.cor)

nls() and plot.formula() subset Argument

The nls() function has a subset argument in R and not in Spotfire S+. So R code of the form:

  fm1 <- nls(circumference ~ SSlogis(age, Asym, xmid, scal),
     data = Orange, subset = Tree == 3)

needs to use subscripting of the data argument instead:

  fm1 <- nls(circumference ~ SSlogis(age, Asym, xmid, scal),
     data = Orange[Orange$Tree == 3,])

The same is true for plot.formula():

  plot(circumference ~ age, data = Orange, subset = Tree == 3,
     xlab = "Tree age (days since 1968/12/31)",
     ylab = "Tree circumference (mm)", las = 1,
     main = "Orange tree data and fitted model
     (Tree 3 only)")

Time/Date

Mixing timeSeries and matrix Classes Causes Errors

Some objects that use old classes such as timeSeries and matrix try to inherit from both. In this example, Y.ts tries to inherit from both:

  x = c(1.0622, 1.0785, 1.0797, 1.0862, 1.1556, 1.1674,
      1.1365, 1.1155, 1.1267, 1.1714, 1.1710, 1.2298)
  y = c(1.5414, 1.5121, 1.4761, 1.4582, 1.3840, 1.3525,
       1.3821, 1.3963, 1.3634, 1.3221, 1.3130, 1.3128)
  Y.ts = ts(cbind(USDEUR = x, USDCAD = y))
  plot(Y.ts)
  Error in plot: Lengths of x and y must match

tsplot(Y.ts) does work as a possible workaround.

by="mon" in timeSequence Interpreted Differently

In the R version of timeSequence(), by="mon" gets partially matched with "month" so the sequence is by month:

  > timeSequence(from="1/1/2001", to="1/1/2002", by="mon")
  [1] "Zurich"
  [1] [2001-01-01] [2001-02-01] [2001-03-01] [2001-04-01] [2001-05-01] [2001-06-01] [2001-07-01]
  [8] [2001-08-01] [2001-09-01] [2001-10-01] [2001-11-01] [2001-12-01] [2002-01-01]

In the Spotfire S+ version of timeSequence(), by="mon" indicates the sequence should be by Mondays:

  > timeSequence(from=timeDate("1/1/2001"),
    to=timeDate("1/1/2002"), by="mon")
  from: 01/01/2001
  to: 01/01/2002
  by: +1mon
  [1] 01/01/2001 01/08/2001 01/15/2001 ... 12/31/2001

Published Nov 04 2010, 08:44 AM by Kathleen Thompson
Filed under: ,
Rating:
Comments