# Interp: Interpolate between numbers or numbers of characters In Ecfun: Functions for Ecdat

## Description

Numeric interpolation is defined in the usual way:

`xOut <- x*(1-proportion) + y*proportion`

Character interpolation does linear interpolation on the number of characters of `x` and `y`. If `length(proportion) == 1`, interpolation is done on `cumsum(nchar(.))`. If `length(proportion) > 1`, interpolation is based on `nchar`. In either case, the interpolant is rounded to an integer number of characters. `Interp` then returns `substring(y, ...)` unless `nchar(x)` > `nchar(y)`, when it returns `substring(x, ...)`.

Character interpolation is used in two cases: (1) At least one of `x` and `y` is character. (2) At least one of `x` and `y` is neither logical, integer, numeric, complex nor raw, and `class(unclass(.))` is either integer or character.

In all other cases, numeric interpolation is used.

NOTE: This seems to provide a relatively simple default for what most people would want from the six classes of atomic vectors (logical, integer, numeric, complex, raw, and character) and most other classes. For example, `class(unclass(factor))` is integer. The second rule would apply to this converting it to character. The `coredata` of an object of class `zoo` could be most anything, but this relatively simple rule would deliver what most people want in most case. An exception would be an object with integer coredata. To handle this as numeric, a `Interp.zoo` function would have to be written.

## Usage

 ```1 2 3 4 5 6 7 8``` ```Interp(x, ...) ## Default S3 method: Interp(x, y, proportion, argnames=character(3), message0=character(0), ...) InterpChkArgs(x, y, proportion, argnames=character(3), message0=character(0), ...) InterpChar(argsChk, ...) InterpNum(argsChk, ...) ```

## Arguments

 `x, y` two vectors of the same class or to be coerced to the same class. `proportion` A number or numeric vector assumed to be between 0 and 1. `argnames` a character vector of length 3 giving args `name.x`, `name.y`, and `proportion` to pass to `compareLengths` to improve the value of any diagnostic message in case lengths are not compatible. `message0` A character string to be passed with `argnames` to `compareLengths` to improve the value of any diagnostic message in case lengths are not compatible. `argsChk` a list as returned by `interpChkArgs` `...` optional arguments for `compareLengths`

## Details

`Interp` is an S3 generic function to allow users to easily modify the behavior to interpolate between special classes of objects.

`Interp` has two basic algorithms for "Numeric" and "Character" interpolation.

The computations begin by calling `InterpChkArgs` to dispose quickly of simple cases (e.g, `x` or `y` `missing` or `length` 0 or if `proportion` is <= 0 or >= 1 or `missing`). It returns a list.

If the list contains a component named "xout", `Interp` returns that value with no further computations.

Otherwise, the list returned by `InterpChkArgs` includes components "algorithm", "x", "y", "proportion", "pLength1" (defined below), "raw", and "outclass". The "algorithm" component must be either "Numeric" or "Character". That algoritm is then performed as discussed below using arguments "x", "y", and "proportion"; all three will have the same length. The class of "x" and "y" will match the algorithm. The list component "raw" is logical: `TRUE` if the output will be raw or such that `class(unclass(.))` of the output will be raw. In that case, a "Numeric" interpolation will be transformed back into "raw". "outclass" will either be a list of attributes to apply to the output or NA. If a list, "xout" will be added as component ".Data" to the list "outclass" and then then processed as `do.call('structure', outclass)` to produce the desired output.

These two basic algorithms ("Numeric" and "Character") are the same if `proportion` is missing or not numeric: In that case `Interp` throws an error.

We now consider "Character" first, because it's domain of applicability is easier to describe. The "Numeric" algorithm is used in all other cases

1. "CHARACTER"

* 1.1. The "CHARACTER" algorithem is used when at least one of `x` and `y` is neither logical, integer, numeric, complex nor raw and satisfies one of the following two additional conditions:

** 1.1.1. Either `x` or `y` is character.

** 1.1.2. `class(unclass(.))` for at least one of `x` and `y` is either character or integer.

NOTE: The strengths and weaknesses of 1.1.2 can be seen in considering factors and integer vectors of class `zoo`: For both, `class(unclass(.))` is integer. For factors, we want to use `as.character(.)`. For zoo objects with `coredata` of class integer, we would want to use numeric interpolation. This is not allowed with the current code but could be easily implemented by writing `Interp.zoo`.

* 1.2. If either `x` or `y` is missing or has `length` 0, the one that is provided is returned unchanged.

* 1.3. Next determine the class of the output. This depends on whether neither, one or both of `x` and `y` have one of the six classes of atomic vectors (logical, integer, numeric, complex, raw, character):

** 1.3.1. If both `x` and `y` have one of the six atomic classes and one is character, return a character object.

** 1.3.2. If only one of `x` and `y` have an atomic class, return an object of the class of the other.

** 1.3.3. If neither of `x` nor `y` have a basic class, return an object with the class of `y`.

* 1.4. Set `pLength1 <- (length(proportion) == 1)`:

** 1.4.1. If(pLength1) do the linear interpolation on `cumsum(nchar(.))`.

** 1.4.2. Else do the linear interpolation on `nchar`.

* 1.5. Next check `x`, `y` and `proportion` for comparable lengths: If all have length 0, return an object of the appropriate class. Otherwise, call `compareLengths(x, proportion)`, `compareLengths(y, proportion)`, and `compareLengths(x, y)`.

* 1.6. Extend `x`, `y`, and `proportion` to the length of the longest using `rep`.

* 1.7. nchOut <- the number of characters to output using numeric interpolation and rounding the result to integer.

* 1.8. Return `substring(y, 1, nchOut)` except when the number of characters from `x` exceed those from `y`, in which case return `substring(x, 1, nchOut)`. [NOTE: This meets the naive end conditions that the number of characters matches that of `x` when `proportion` is 0 and matches that of `y` when `proportion` is 1. This can be used to "erase" characters moving from one frame to the next in a video. See the examples.

2. "NUMERIC"

* 2.1. Confirm that this does NOT satisfy the condition for the "Character" algorithm.

* 2.2. If either `x` or `y` is missing or has `length` 0, return the one provided.

* 2.3. Next determine the class of the output. As for "Character" described in section 1.3, this depends on whether neither, one or both of `x` and `y` have a basic class other than character (logical, integer, numeric, complex, raw):

** 2.3.1. If `proportion` <= 0, return `x` unchanged. If `proportion` >= 1, return `y` unchanged.

** 2.3.2. If neither `x` nor `y` has a basic class, return an object of class equal that of `y`.

** 2.3.3. If exactly one of `x` and `y` does not have a basic class, return an object of class determined by `class(unclass(.))` of the non-basic argument.

** 2.3.4. When interpolating between two objects of class raw, convert the interpoland back to class raw. Do this even when 2.3.2 or 2.3.3 applies and `class(unclass(.))` of both `x` and `y` are of class raw.

* 2.4. Next check `x`, `y` and `proportion` for comparable lengths: If all have length 0, return an object of the appropriate class. Otherwise, call `compareLengths(x, proportion)`, `compareLengths(y, proportion)`, and `compareLengths(x, y)`.

* 2.5. Compute the desired interpolation and convert it to the required class per step 2.3 above.

## Value

`Interp` returns a vector whose class is described in "* 1.3" and "* 2.3" in "Details" above.

`InterpChkArgs` returns a list or throws an error as described in "Details" above.

Spencer Graves

## References

The Writing R Extensions manual (available via `help.start()`) lists six different classes of atomic vectors: `logical`, `integer`, `numeric`, `complex`, `raw` and `character`. See also Wickham, Hadley (2014) Advanced R, especially Wickham (2013, section on "Atomic vectors" in the chapter on "Data structures").

`classIndex` `interpPairs`
Many other packages have functions with names like "interp", "interp1", and "interpolate". Some do one-dimensional interpolation. Others do two-dimensional interpolation. Some offer different kinds of interpolation beyond linear. At least one is a wrapper for `approx`.
 ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125``` ```## ## 1. numerics ## # 1.1. standard xNum <- interpChar(1:3, 4:5, (0:3)/4) # answer xN. <- c(1, 2.75, 3.5, 4) all.equal(xNum, xN.) # 1.2. with x but not y: # return that vector with a warning xN1 <- Interp(1:4, p=.5) # answer xN1. <- 1:4 all.equal(xN1, xN1.) ## ## 2. Single character vector ## i.5 <- Interp(c('a', 'bc', 'def'), character(0), p=0.3) # with y = NULL or character(0), # Interp returns x all.equal(i.5, c('a', 'bc', 'def')) i.5b <- Interp('', c('a', 'bc', 'def'), p=0.3) # Cumulative characters (length(proportion)=1): # 0.3*(total 6 characters) = 1.2 characters i.5. <- c('a', 'b', '') all.equal(i.5b, i.5.) ## ## 3. Reverse character example ## i.5c <- Interp(c('a', 'bc', 'def'), '', 0.3) # check: 0.7*(total 6 characers) = 4.2 characters i.5c. <- c('a', 'bc', 'd') all.equal(i.5c, i.5c.) ## ## 4. More complicated example ## xCh <- Interp('', c('Do it', 'with R.'), c(0, .5, .9)) # answer xCh. <- c('', 'with', 'Do i') all.equal(xCh, xCh.) ## ## 5. Still more complicated ## xC2 <- Interp(c('a', 'fabulous', 'bug'), c('bigger or', 'just', 'big'), c(.3, .3, 1) ) x.y.longer <- c('bigger or', 'fabulous', 'big') # use y with ties # nch smaller 1 4 3 # nch larger 9 8 3 # d.char 8, 4, 0 # prop .3, .7, 1 # prop*d.char 2.4, 2.8, 0 # smaller+p*d 3, 7, 3 xC2. <- c('big', 'fabulou', 'big') all.equal(xC2, xC2.) ## ## 6. with one NULL ## null1 <- Interp(NULL, 1, .3) all.equal(null1, 1) null2 <- Interp('abc', NULL, .3) all.equal(null2, 'abc') ## ## 7. length=0 ## log0 <- interpChar(logical(0), 2, .6) all.equal(log0, 1.2) ## ## 8. Date ## Jan1.1980 <- as.Date('1980-01-01') Jan1.1972i <- Interp(0, Jan1.1980, .2) # check Jan1.1972 <- as.Date('1972-01-01') all.equal(Jan1.1972, round(Jan1.1972i)) ## ## 9. POSIXct ## Jan1.1980c <- as.POSIXct(Jan1.1980) Jan1.1972ci <- Interp(0, Jan1.1980c, .2) # check Jan1.1972ct <- as.POSIXct(Jan1.1972) abs(difftime(Jan1.1972ct, Jan1.1972ci, units="days"))<0.5 ```