Writing custom function in R which takes input vector with units

Viewed 150

I am trying to write a function my_heights() that takes a vector-like heights and returns a numeric vector with the heights in a consistent unit. The unit should be specified as a function argument, with the default being cm. For the sake of simplicity, assume the unit can only be m or cm.

I have the following vector:

heights <- c("188 cm", "1.73 m", "192 cm", "175 cm", "165 cm", "191 cm", "1.85 m")

Desired output (sanity check):

my_heights(heights)
[1] 188 173 192 175 165 191 185

my_heights(heights, "m")
[1] 1.88 1.73 1.92 1.75 1.65 1.91 1.85

I understand that when the default function is called it would return the heights with cm as it is but the heights with m are multiplied by 100. And for the latter heights with m are returned as it is but the heights with cm are divided by 100. Not sure how should I separate the heights by cm and m to make it work correctly.

3 Answers

Create a function with the input 'x' for the vector of values, and the unit specifying the default case as 'cm', then either use parse_number from readr or sub from base R to extract the numeric part, create a logical vector (i1) with grepl based on the occurrence of 'm' at the end ($) of the string. Specify a condition to multiply or divide the subset of values based on the 'unit', assign and return the numeric converted vector

clean_heights <- function(x, unit = 'cm') {
          x1 <-  readr::parse_number(x) 
          # // base R with sub
          #x1 <- as.numeric(sub("\\s+\\D+", "", x))            
          i1 <- grepl('\\sm$', x)
          if(unit == 'cm') {
               x1[i1] <- x1[i1] * 100
           } else {
              x1[!i1] <- x1[!i1]/100
             }
            x1
  }

-testing

clean_heights(heights)
#[1] 188 173 192 175 165 191 185

clean_heights(heights, "m")
#[1] 1.88 1.73 1.92 1.75 1.65 1.91 1.85

This would be done as follows:

my_heights <- function(x, unit = "cm"){
  units <- c(mm = 0.1, cm = 1, dm=10, m = 100, km = 100000)
  unname(with(read.table(text=x, h=F), V1*units[V2]))/units[unit]
}

my_heights(heights)
[1] 188 173 192 175 165 191 185
my_heights(heights, "m")
[1] 1.88 1.73 1.92 1.75 1.65 1.91 1.85
my_heights(heights, "mm")
[1] 1880 1730 1920 1750 1650 1910 1850

You can try the following user function my_heights

my_heights <- function(h, unit = "cm") {
  ifelse(gsub(".*\\s+", "", h) == unit,
    1,
    100^ifelse(unit == "cm", 1, -1)
  ) * as.numeric(gsub("\\s.*$", "", h))
}

such that

> my_heights(heights)
[1] 188 173 192 175 165 191 185

> my_heights(heights, "m")
[1] 1.88 1.73 1.92 1.75 1.65 1.91 1.85

> my_heights(heights, "cm")
[1] 188 173 192 175 165 191 185
Related