1

Say I have 4 lists:

A = [1.1, 1.4, 2.1, 2.4]
B = [1.3, 6.5, -1.0, 2.3]
C = [0.5, -1.0, -1.1, 2.0]
D = [1.5, 6.3, 2.2, 3.0]

How do I 1)compare the lists eg A,B B,C C,D A,C so on and 2)return true if the elements are +/-0.2 ?

Example output: (Or any other way to represent the data)
A,B [true, false, false, true]
B,C [false, false, true, false]

My thoughts are to append the lists have a for loop to iterate through all.

A.append(B)
A.append(C)
.
.

But then I'm stuck since if I do

for x in A:
    for y in A[x]:
        if A[x][y] - A[x+1][y] <= 0.2
            if A[x+1][y] - A[x][y] <= 0.2

Obviously it doesn't work. Are there ways to iterate through the lists without duplicates and compare at the same time?

Thanks in advance

smci
  • 32,567
  • 20
  • 113
  • 146
Steve. C
  • 15
  • 3
  • I am not a big supporter of combinning 2 questions in a single stackoverflow question. – Elmex80s Apr 28 '17 at 23:55
  • It looks like require the output format to contain the names of the lists, which is slightly annoying and requires indexing into a list-of-lists and possibly overloading their `str()` method to get their name. – smci Apr 29 '17 at 00:08

3 Answers3

3

Update:

OK, now I think I understand both questions you're asking:

from itertools import combinations

A = [1.1, 1.4, 2.1, 2.4]
B = [1.3, 6.5, -1.0, 2.3]
C = [0.5, -1.0, -1.1, 2.0]
D = [1.5, 6.3, 2.2, 3.0]
lists = {'A': A, 'B': B, 'C': C, 'D': D}
tol = 0.2

def compare_lists(a, b, tol):
    return [abs(elem1-elem2) <= tol for elem1, elem2 in zip(a, b)]  # Might want '<' instead

for name1, name2 in combinations(lists.keys(), 2):
    a, b = lists[name1], lists[name2]
    print('{}, {} {}'.format(name1, name2, compare_lists(a, b, tol)))

Output:

A, B [True, False, False, True]
A, C [False, False, False, False]
A, D [False, False, True, False]
B, C [False, False, True, False]
B, D [True, False, False, False]
C, D [False, False, False, False]

Update 2:

To answer your follow up question, if the lists are actually members of a list-of-lists, you could similarly do something like this:

# An alternative for when the lists are nested inside another list

from itertools import combinations

lists = [
    [1.1, 1.4, 2.1, 2.4],
    [1.3, 6.5, -1.0, 2.3],
    [0.5, -1.0, -1.1, 2.0],
    [1.5, 6.3, 2.2, 3.0]
]
tol = 0.2

def compare_lists(a, b, tol):  # unchanged
    return [abs(elem1-elem2) <= tol for elem1, elem2 in zip(a, b)]  # Might want '<' instead

for i, j in combinations(range(len(lists)), 2):  # all combinations of pairs of indices
    a, b = lists[i], lists[j]
    print('{}[{}], [{}] {}'.format('lists', i, j, compare_lists(a, b, tol)))

Output:

lists[0], [1] [True, False, False, True]
lists[0], [2] [False, False, False, False]
lists[0], [3] [False, False, True, False]
lists[1], [2] [False, False, True, False]
lists[1], [3] [True, False, False, False]
lists[2], [3] [False, False, False, False]
martineau
  • 119,623
  • 25
  • 170
  • 301
  • thanks for answering! But just as a follow up question are there any ways to do it without having to make a dictionary literal? – Steve. C Apr 29 '17 at 12:44
  • @Steve.C: You're welcome. There may very well be ways—but I need more information. Where are the dictionaries & how (or what) are they named? If they're in a container of some sort, where is it and how would you like their names displayed in that case? – martineau Apr 29 '17 at 16:04
  • The lists ABCD are actually a list of lists (say data = [[1.1,....],[1.3,....]]) being generated from a for-loop, and there won't always be 4 lists. Therefore I am not exactly sure how to parse the data to compare_lists and output results using lists={}. – Steve. C Apr 29 '17 at 17:43
  • @Steve. C: Doesn't seem too difficult—see Update 2. – martineau Apr 29 '17 at 19:39
0

You want itertools.combinations

from itertools import combinations

lists = [A, B, C, D]

for first, second in combinations(lists, 2):
    # (first, second) will be (A, B), then (A, C), then (A, D), then
    # (B, C), then (B, D), then (C, D)

Then you can compare with:

EPSILON = 0.2

for first, second in combinations(lists, 2):
    for a, b in zip(first, second):
        if abs(a-b) < EPSILON:
            print(True)  # or add it to a list, or etc.
Adam Smith
  • 52,157
  • 12
  • 73
  • 112
  • Not quite, how do you get their name for the required output `A,B [true, false...`? I guess you'd need to have an overloaded list object whose `str()` returns its name. – smci Apr 28 '17 at 23:58
  • @smci you don't. You could do some Deep Magic with `globals()` but as always when it comes to getting identifier names, you should never need to do that. – Adam Smith Apr 29 '17 at 00:03
  • The question requires it: output `A,B [true, false...` – smci Apr 29 '17 at 00:05
  • @smci You must not have read the question too closely. "Example output: (Or any other way to represent the data)" the parenthetical is fine for me to handwave away the "A,B" part. martineau produced a solution that builds a new dict to store names. That'd be the way to go if that became necessary. – Adam Smith Apr 29 '17 at 00:51
  • I read the question four times. It's the output format shown, and "other way to represent the data" implies you name the lists being compared, not just spew out a stream of booleans... anyway I'll post an answer with the way I'd implement that. – smci Apr 29 '17 at 01:17
  • 1
    @smci sounds good. If you're trying to implement the approach you outlined in your first comment, though, I'd be wary. [Variable names aren't that useful in Python](https://stackoverflow.com/questions/2553354/how-to-get-a-variable-name-as-a-string-in-python) – Adam Smith Apr 29 '17 at 01:26
  • `lists_to_compare = [lname_list for lname_list in locals().items() if not lname_list[0].startswith('_')]` Anyway @martineau posted similar already. – smci Apr 29 '17 at 01:37
  • That's super fragile, and is exactly the sort of Deep Magic I admonished against earlier in the comments (just using `locals` instead of `globals`) – Adam Smith Apr 29 '17 at 01:40
  • I know it's super-fragile, it's a quick hack to implement what the OP asked for. I also started out by stating above the correct way would be to subclass list and override its `str()` method. – smci Apr 29 '17 at 02:48
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/142949/discussion-between-adam-smith-and-smci). – Adam Smith Apr 29 '17 at 02:58
  • @smci: Don't want to start an argument—or a discussion—but IMO subclassing `list` for just this is uncalled-for. – martineau Apr 29 '17 at 03:30
0

Here is a simple way with just zip:

A = [1.1, 1.4, 2.1, 2.4]
B = [1.3, 6.5, -1.0, 2.3]
C = [0.5, -1.0, -1.1, 2.0]
D = [1.5, 6.3, 2.2, 3.0]

second_list = [A, B, C, D]

statements = [[round(abs(c-b), 1) == 0.2 or round(abs(c-b), 1) == 0.1
                for c, b in zip(second_list[i], second_list[i+1])]
                    for i in range(len(second_list)) if i+1 < len(second_list)]

print statements
martineau
  • 119,623
  • 25
  • 170
  • 301
Ajax1234
  • 69,937
  • 8
  • 61
  • 102
  • Doesn't look that simple to me...even when better formatted. I also don't think the OP was looking for specific difference values. – martineau Apr 29 '17 at 03:18