# Interpolate between numbers or numbers of characters

### 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 |

### 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 |

`message0` |
A character string to be passed with |

`argsChk` |
a list as returned by |

`...` |
optional arguments for |

### 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.

### Author(s)

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").

### See Also

`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`

.

### Examples

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
``` |