Saving data in a dict which has repetitive key values

Viewed 71

I need to transfer data contained in a text file into a dict. The txt file is like this:

chr7    127477031    127478198    Neg2    0    -    127477031    127478198    0,0,255
chr7    127478198    127479365    Neg3    0    -    127478198    127479365    0,0,255
chr7    127479365    127480532    Pos5    0    +    127479365    127480532    255,0,0
chr8    127480532    127481699    Neg4    0    -    127480532    127481699    0,0,255
chr8    127474697    127475864    Pos4    0    +    127474697    127475864    255,0,0

The code I used to do this is this:

def load_data(self):
        '''
        Load data from file 
        '''
        chr2position = {}
        with open(self.file_path) as f:
            for line in f:
                star_end_list = []
                if line.startswith(('chr',"1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","Y","X")):
                    # I have found bed files starinting with chr1 or directly with 1
                    chr, start, end = line.split()[:3]
                    star_end_list.append(int(start))
                    star_end_list.append(int(end))
                    chr2position[chr] = star_end_list
        return chr2position

And the dict returned is this

{'chr7': [127479365, 127480532], 'chr8': [127474697, 127475864]}

As you can see, this only saves the last line per any different chr because a dict cannot have repetitive key values. Because I really need to store every line of the txt in a dict like the one shown above. I have been thinking of how would be the best way to do this. Perhaps, adding some extra character in the keys values to allow python to store every line. Something like

{'chr7_line0': [127477031, 127478198], 'chr7_line1': [127478198, 127479365],'chr7_line2': [127478198, 127479365],'chr8_line0': [127480532, 127481699],... }

Is this the best way to do this? If this is correct, how can I do this in my loop for?

3 Answers

If you know that a key might have multiple values, one way of handling it is by making its value as a list. For example with Django, this is how it handles storing of query parameters which can have duplicates e.g. ?field1=any&field1=thing&field1=here would be {"field1": ["any", "thing", "here"]}.

Here is a solution that was built on top of your original solution:

from collections import defaultdict
...
    # Replace this line
    # chr2position = {}
    chr2position = defaultdict(list)
    ...
                # Replace this line
                # chr2position[chr] = star_end_list
                chr2position[chr].append(star_end_list)
...

Output:

{
    "chr7": [
        [127477031, 127478198],
        [127478198, 127479365],
        [127479365, 127480532]
    ],
    "chr8": [
        [127480532, 127481699],
        [127474697, 127475864]
    ]
}

You can choose of course another style as you mentioned with making the key different e.g. "chr7_line0". The style I used was inspired by QueryDict in Django.

QueryDict.__init__(query_string=None, mutable=False, encoding=None)

Instantiates a QueryDict object based on query_string.

>>> QueryDict('a=1&a=2&c=3')
<QueryDict: {'a': ['1', '2'], 'c': ['3']}>

Try this, it works in this condition

Create a list if there's nothing in the values part of the given key or if the key doesn't exists, append it if there is

Just a slight changes in your pre-existing code, nothing too much

Code:

def load_data(self):
    '''Load data from file'''
    chr2position = {}
    with open(self.file_path) as f:
        for line in f.readlines():
            star_end_list = []
            if line.startswith(('chr',"1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","Y","X")):
                chr, start, end = line.split()[:3]
                star_end_list = [int(start), int(end)]
                if chr in chr2position.keys():
                    chr2position[chr].append(star_end_list)
                else:
                    chr2position[chr] = [star_end_list]
    return chr2position

Output:

{
 'chr7': [
          [127477031, 127478198],
          [127478198, 127479365],
          [127479365, 127480532]
         ],
 'chr8': [
          [127480532, 127481699], 
          [127474697, 127475864]
         ]
}

tell me if its okay for you...

Just check if the key already exists in chr2position:

if chr in chr2position.keys():
    chr2position[chr].extend(star_end_list)
else:
    chr2position[chr] = star_end_list

Full working code:

chr2position = {}
with open("stackques.txt") as f:
    for line in f:
        star_end_list = []
        if line.startswith(('chr',"1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","Y","X")):
            # I have found bed files starinting with chr1 or directly with 1
            chr, start, end = line.split()[:3]
            star_end_list.append(int(start))
            star_end_list.append(int(end))
            if chr in chr2position.keys():
                chr2position[chr].extend(star_end_list)
            else:
                chr2position[chr] = star_end_list
print(chr2position)
Related