Get where an element appears inside each sublist in Python

Viewed 66

I have the following list of lists:

db=[['C','A','G','A','A','G','T'],['T','G','A','C','A','G'],['G','A','A','G','T']]

I need to get where each individual symbol (that is to say, ['C', 'A', 'G', 'T']) appears inside each of the sublist. In the end, the result must be a tuple containing the symbol and a list of tuples that contain the index of the sublist where the symbol appears, and a list of indexes representing in which position of such sublist the symbol appears

That is to say:

FinalList=[('C',[(0,[0]),(1,[3])]),
           ('A',[(0,[1,2,4]),(1,[2,4]),(2,[1,2])]),
           ('G',[(0,[2,5]),(1,[1,5]),(2,[0,3])]),
           ('T',[(0,[6]),(1,[0]),(2,[4])])  
           ]   

To better explain the result, lets take the first element of FinalList.

('C',[(0,[0]),(1,[3])])

Here, 'C' is the symbol, Because 'C' appears in the db[0] and db[1] but not in db[2], we have [(0,[0]),(1,[3])]. Furthermore, because in db[0], 'C' appears in position 0, we have (0,[0]), and because in db[1] 'C' appears in position 3, we have (1,[3])

The only thing I have managed to do is extract every unique symbol via the following code, although I believe it's inefficient as it combines every list and then makes the result a dictionary, something that would take ages to run if db had more elements

symbols=[]
for i in db:
    symbols+=i
symbols=list(dict.fromkeys(symbols))

I now planned to compare each element in symbols with every element in db, then save the index where the symbols are the same, but I think it would be inefficient to use for loops inside for loops. But I am stuck here and don't know how to proceed.

4 Answers

Try this code:

db=[['C','A','G','A','A','G','T'],
    ['T','G','A','C','A','G'],
    ['G','A','A','G','T']]

ltrs = set()  # unique letter list

for x in db:
  for y in x:
     ltrs.add(y)

d = {x:[] for x in ltrs}  # main list

for k in d:  # each letter
    for xi,x in enumerate(db):  # each list in main list
      q = []
      for yi,y in enumerate(x):  # each letter in sublist
         if y == k:
            q.append(yi)  # add index to sublist
      if len(q):
         d[k].append((xi,q)) # add tuple to main list
         
print([(k,d[k]) for k in d])

Output

[('C', [(0, [0]), (1, [3])]), 
 ('G', [(0, [2, 5]), (1, [1, 5]), (2, [0, 3])]), 
 ('A', [(0, [1, 3, 4]), (1, [2, 4]), (2, [1, 2])]), 
 ('T', [(0, [6]), (1, [0]), (2, [4])])]

It can be made in a clean way, iterating on the original data only once, by building an intermediate structure, a dict with the bases as keys, whose values are dicts with sublist indices as keys and whose values are the indices of bases in the sublists (well, see the sample in the comment). Then, we just transform it to build the final list structured in the way you want:

from collections import defaultdict

db=[['C','A','G','A','A','G','T'],['T','G','A','C','A','G'],['G','A','A','G','T']]

bases = ['C', 'A', 'G', 'T']
by_base = {base: defaultdict(list) for base in bases}

for sublist_idx, sublist in enumerate(db):
    for base_idx, base in enumerate(sublist):
        by_base[base][sublist_idx].append(base_idx)
        
# by_base looks like {'C': defaultdict(<class 'list'>, {0: [0], 1: [3]}), ...}


final_list = [(base, list(occurences.items())) for base, occurences in by_base.items()]

which gives us:

print(final_list)
# [('C', [(0, [0]), (1, [3])]), ('A', [(0, [1, 3, 4]), (1, [2, 4]), (2, [1, 2])]), ('G', [(0, [2, 5]), (1, [1, 5]), (2, [0, 3])]), ('T', [(0, [6]), (1, [0]), (2, [4])])]

Below

SYMBOLS = {'A', 'C', 'T', 'G'}
db = [['C', 'A', 'G', 'A', 'A', 'G', 'T'], ['T', 'G', 'A', 'C', 'A', 'G'], ['G', 'A', 'A', 'G', 'T']]
final_list = []
for sym in SYMBOLS:
    lst = []
    for idx1, entry in enumerate(db):
        found = False
        for idx2, s in enumerate(entry):
            if s == sym:
                if not found:
                    lst.append((idx1, [idx2]))
                    found = True
                else:
                    lst[-1][1].append(idx2)
    final_list.append((sym, lst))
for entry in final_list:
    print(entry)

output

('G', [(0, [2, 5]), (1, [1, 5]), (2, [0, 3])])
('A', [(0, [1, 3, 4]), (1, [2, 4]), (2, [1, 2])])
('T', [(0, [6]), (1, [0]), (2, [4])])
('C', [(0, [0]), (1, [3])])

You could achieve this using list comprehensions:

chars = ['C', 'A', 'G', 'T']
final_list = [
  (character, 
    [
      (i, indexes) for i, indexes in
        [(i, [j for j, c in enumerate(l) if c == character]) for i, l in enumerate(db)]
      if len(indexes) > 0
    ]
  ) for character in chars
]
Related