3

I'm trying to solve a problem that involves the figuring out if one list is a subset of another, except there is an added twist that the code should consider the list a subset even if the values don't completely match, as long as it's within a tolerance.

EXAMPLE:

If I have the lists:

A = [0.202, 0.101]
B = [0.1, 0.2, 0.3, 0.4, 0.5]

and I set a tolerance of tol = 0.002, then the code should return that list A is a subset of list B since its values are within the tolerance (0.202 <= 0.2 + tol, 0.101 <= 0.1 + tol).

I don't have much code to show since I know how to figure out if a list is a subset of another using the traditional issubset function, but I'm not sure how to incorporate the tolerance into it.

Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
Alan Xu
  • 31
  • 1

1 Answers1

9

You could do the following (pseudo-code first):

For each elment a of A:
    for each element b of B:
        if a is close enough to b, consider a to be in B
    if a was not close enough to an element in B, break the loop as A is not a subset of B

But notice that dealing with floating point numbers is dangerous. According to this post, we can use the decimal module for the comparisons.

Translation to a simple Python code:

from decimal import Decimal

for a in A:
    for b in B:
        if abs(Decimal(str(a)) - Decimal(str(b))) <= tol:
            break
    else:
        print('A is not a subset of B')
        break
else:
    print(f"A is a subset of B with a tolerance of {tol}")

Now more compactly with generators and a function:

def tol_subset(A, B, tol):
    return all(any(abs(Decimal(str(a)) - Decimal(str(b))) <= tol for b in B) for a in A)
CallMeStag
  • 5,467
  • 1
  • 7
  • 22
  • 2
    I keep forgetting about for-else … thanks! the continue was supposed to break the outer for-loop, but ofc that doesn't work. TBH I'd always just use the generator version, so I probably didn't put enough effort in the other one … – CallMeStag Apr 08 '21 at 18:04
  • I don't think you want that second break in there. You will only check the first item in `A` that way. Once you're done looping through `B`, you'll go into the `else` and break out of the outer `for` loop. – ThatNewGuy Apr 08 '21 at 18:14
  • @ThatNewGuy And that will only happen if the list is not a subset so that's fine... Read the link I provided above. If the condition is fulfilled and the first `break` is executed, then the `else` part is not executed at all – Tomerikoo Apr 08 '21 at 18:16
  • won't this always run `print(f"A is a subset of B with a tolerance of {tol}")` ? – Hugo Apr 08 '21 at 18:17
  • @Tomerikoo I misread the question; I thought we were checking if any of A was in B within a tolerance. – ThatNewGuy Apr 08 '21 at 18:20
  • 1
    @Hugo No. If the first `else` is executed, then it breaks the outer loop so its `else` clause will not be executed – Tomerikoo Apr 08 '21 at 18:21
  • @CallMeStag I hope you didn't mind me changing your answer like that. It just got some attention and I thought it should be accurate and correct – Tomerikoo Apr 08 '21 at 18:35
  • No, that's fine :) – CallMeStag Apr 09 '21 at 06:00