How to parse numbers in strings?

Viewed 76

At first glance, I have a simple problem. I have a column with measurements that partly consist of one area. I.e:

a <- c("28", "", "5.6", "12-24")

Now I want to convert all values. I have chosen the following approach:

res <- ifelse(str_detect(a, "-"), 
                     yes = paste(
                         as.character(as.double(unlist(str_extract_all(a, "[0-9.]+"))[1]) * 10),
                         "-",
                         as.character(as.double(unlist(str_extract_all(a, "[0-9.]+"))[2]) * 10)),
                     no = a)

The result looks like:

res
[1] "28"       ""         "5.6"      "280 - 56"

The expected output is the following:

res
[1] "28"       ""         "5.6"      "120 - 240"

Can someone explain to me why this is happening and what a correct approach can be?

6 Answers

You can split the string on "-" and based on the output length apply different conditions :

a <- c("28", "", "5.6", "12-24")

sapply(strsplit(a, '-'), function(x) 
 if(length(x) > 1) paste0(as.numeric(x) * 10, collapse = ' - ') else toString(x))

#[1] "28"        ""          "5.6"       "120 - 240"

The reason your code does not work is because ifelse passes all the values to both yes and no part of the code and not only those values that satisfy the condition. So when you do

unlist(str_extract_all(a, "[0-9.]+"))[1]
#[1] "28"

it is actually returning output of 1st value in a (28) and not 12 (from 12-24) as you were expecting.

If you want to seperate the numbers in two columns I suggest the following from tidyr:

  a <- data.frame( Numbers = c("28", "", "5.6", "12-24"))
  b <- a  %>% tidyr::separate(Numbers,  c("A", "B"), sep = "([-])")

Try ifelse like below

ifelse(
  grepl("-", a),
  paste0(as.numeric(strsplit(a, "-")[[grep("-", a)]]) * 10, collapse = "-"),
  a
)

or replace

replace(
  a,
  grep("-", a),
  paste0(as.numeric(strsplit(a, "-")[[grep("-", a)]]) * 10, collapse = "-")
)

I think it is the assigned position [1] and [2]

If you use [3] [4] then it works.

res <- ifelse(str_detect(a, "-"), 
              yes = paste(
                as.character(as.double(unlist(str_extract_all(a, "[0-9.]+"))[3]) * 10),
                "-",
                as.character(as.double(unlist(str_extract_all(a, "[0-9.]+"))[4]) * 10)),
              no = a)

Output:

> res
[1] "28"        ""          "5.6"       "120 - 240"
gsubfn::gsubfn("(\\d+)-(\\d+)",~paste(as.numeric(.x)*10,as.numeric(.y)*10,sep=" - "),a)
[1] "28"        ""          "5.6"       "120 - 240"

purrr style

map_chr(str_split(a, "-"), ~ifelse(length(.x)>1, paste(as.numeric(.x)*10, collapse = "-"), .x))

[1] "28"      ""        "5.6"     "120-240"
Related