How to transform a csv file into a multi-dimensional list using Python?

Question:

I started out with a 4d list, something like

tokens = [[[["a"], ["b"], ["c"]], [["d"]]], [[["e"], ["f"], ["g"]],[["h"], ["i"], ["j"], ["k"], ["l"]]]]

So I converted this to a csv file using the code

import csv
def export_to_csv(tokens):
    csv_list = [["A", "B", "C", word]]
    for h_index, h in enumerate(tokens):
        for i_index, i in enumerate(h):
            for j_index, j in enumerate(i):
                csv_list.append([h_index, i_index, j_index, j])
    
    with open('TEST.csv', 'w') as f:
      
        # using csv.writer method from CSV package
        write = csv.writer(f)

        write.writerows(csv_list)

But now I want to do the reverse process, want to convert a csv file obtained in this format, back to the list format mentioned above.

Asked By: kartikeya saraswat

||

Answers:

Assuming you wanted your csv file to look something like this (there were a couple typos in the posted code):

A,B,C,word                                                                          
0,0,0,a                                                                             
0,0,1,b                                                                             
0,0,2,c
...

here’s one solution:

import csv                                                                          
                                                                                    
def import_from_csv(filename):                                                      
    retval = []                                                                     
    with open(filename) as fh:                                                      
        reader = csv.reader(fh)                                                     
        # discard header row                                                        
        next(reader)   
        # process data rows                                                             
        for (x,y,z,word) in reader:                                                 
            x = int(x)                                                              
            y = int(y)                                                              
            z = int(z)                                                              
            retval.extend([[[]]] * (x + 1 - len(retval)))                           
            retval[x].extend([[]] * (y + 1 - len(retval[x])))                       
            retval[x][y].extend([0] * (z + 1 - len(retval[x][y])))                  
                                                                                    
            retval[x][y][z] = [word]                                                
                                                                                    
    return retval  
Answered By: DraftyHat
def import_from_csv(file):
    import ast
    import csv

    data = []
    # Read the CSV file
    with open(file) as fp:
        reader = csv.reader(fp)
        # Skip the first line, which contains the headers
        next(reader)
        
        for line in reader:
            # Read the first 3 elements of the line
            a, b, c = [int(i) for i in line[:3]]
            # When we read it back, everything comes in as strings. Use
            # `literal_eval` to convert it to a Python list
            value = ast.literal_eval(line[3])

            # Extend the list to accomodate the new element
            data.append([[[]]]) if len(data) < a + 1 else None
            data[a].append([[]]) if len(data[a]) < b + 1 else None
            data[a][b].append([]) if len(data[a][b]) < c + 1 else None

            data[a][b][c] = value
    return data

# Test
assert import_from_csv("TEST.csv") == tokens
Answered By: Code Different

First, I’d make writing this construction in a CSV format independent from dimensions:

import csv

def deep_iter(seq):
    for i, val in enumerate(seq):
        if type(val) is list:
            for others in deep_iter(val):
                yield i, *others
        else:
            yield i, val   
    
with open('TEST.csv', 'w') as f:
    csv.writer(f).writerows(deep_iter(tokens))

Next, we can use the lexicographic order of the indices to recreate the structure. All we have to do is sequentially move deeper into the output list according to the indices of a word. We stop at the penultimate index to get the last list, because the last index is pointing only at the place of the word in this list and doesn’t matter due to the natural ordering:

with open('TEST.csv', 'r') as f:
    rows = [*csv.reader(f)]

res = []
for r in rows:
    index = r[:-2]   # skip the last index and word
    e = res
    while index:
        i = int(index.pop(0))    # get next part of a current index
        if i < len(e):
            e = e[i]
        else:
            e.append([])   # add new record at this level
            e = e[-1]
    e.append(r[-1])   # append the word to the corresponding list
Answered By: Vitalizzare