Doug Edmunds
Doug Edmunds

Reputation: 531

R How to use which() with floating point values?

I have run into the same problem as described at R which () function returns integer(0)

price = seq(4,7, by=0.0025)
allPrices = as.data.frame(price)
lookupPrice = 5.0600
which(allPrices$price == lookupPrice)

The which() statement outputs integer(0), indicating no match. It should output 425, the matching row number in that sequence.

I understand that this is a floating point issue. The link suggests using all.equal(x,y) in some manner.

How do I incorporate the all.equal() function into the which() statement, so that I get the row number in allPrices that matches lookupPrice (in this case, 5.06)?

Is there some other approach? I need the row number, because values in other columns at that price will be modified.

Upvotes: 6

Views: 1684

Answers (5)

Marta Shocket
Marta Shocket

Reputation: 1

I just had the exact same problem.

I initially fixed it by converting both sets of data from numeric to characters with as.character() before calling which().

However, I wanted to figure out exactly why it wasn't working with the numeric data and did some further troubleshooting.

It appears that the problem is with the way R generates decimal sequences with seq(). Using the round() function works - as suggested by Tim Biegeleisen - but I think you only need to apply it to the numbers generated by seq(). You can check out my work below - the error is very sporadic, I just tried numbers until I found one that failed: 19.2.

> data <- 19.2
> x.seq <- seq(5, 45, 0.2)
> x.seq[72]
[1] 19.2
> 
> data == 19.2
[1] TRUE
> x.seq[72] == 19.2
[1] FALSE
> data == x.seq[72]
[1] FALSE
> data == round(x.seq[72], digits = 1)
[1] TRUE
> round(data, digits = 1) == x.seq[72]
[1] FALSE

Upvotes: 0

mt1022
mt1022

Reputation: 17299

There is a function near in dplyr:

near(x, y, tol = .Machine$double.eps^0.5)

For this case, you can try:

which(near(allPrices$price, lookupPrice))
#[1] 425

Upvotes: 1

Marius
Marius

Reputation: 60160

A manual approach to this involves specifying the tolerance for the comparison and doing:

# tol = 1e-7: comparison will be TRUE if numbers are equal up to 
#   7 decimal places
tol = 1e-7
which(abs(allPrices$price - lookupPrice) < tol)

Upvotes: 2

Tim Biegeleisen
Tim Biegeleisen

Reputation: 521997

You could also try rounding the prices in your data frame to 4 decimal places:

which(round(allPrices$price, digits=4) == lookupPrice)
[1] 425

After rounding to 4 places, the precision of the lookupPrice and your data frame of prices should match.

Demo

Upvotes: 1

SymbolixAU
SymbolixAU

Reputation: 26258

You can sapply over all the prices and apply the all.equal function to each one, to find the one that is TRUE

which(sapply(price, all.equal, lookupPrice) == TRUE)
# [1] 425

Upvotes: 1

Related Questions