2013-11-11 4 views
4

Я новичок в Python и пробовал все, что мог придумать, и не мог найти решение этого. У меня есть список, который содержит в качестве последнего из своих элементов один словарь с различным количеством ключей, который выглядит.Как написать список с вложенным словарем в файл csv?

l = [('Apple', 1, 2, {'gala': (2, 1.0)}), 
('Grape ', 2, 4, {'malbec': (4, 0.25), 'merlot': (4, 0.75)}), 
('Pear', 4, 5, {'anjou': (5, 0.2), 'bartlet': (5, 0.4), 'seckel': (5, 0.2)}), 
('Berry', 5, 5, {'blueberry': (5, 0.2), 'blackberry': (5, 0.2), 'straw': (5, 0.2)})] 

Когда я пытаюсь написать CSV-файл из текущего списка, я использовал:

test_file =() 
length = len(l[0]) 

with open('test1.csv', 'w', encoding = 'utf-8') as test_file: 
    csv_writer = csv.writer(test_file, delimiter=',') 
    for y in range(length): 
     csv_writer.writerow([x[y] for x in l]) 

Это делает последний элемент в списке, словарь, чтобы быть только одна строка в выходной файл:

Apple 1 2 {'gala': (2, 1.0)} 
Grape 2 4 {'malbec': (4, 0.25), 'merlot': (4, 0.75)} 
Pear 4 5 {'anjou': (5, 0.2), 'bartlet': (5, 0.4), 'seckel': (5, 0.2), 'bosc': (5, 0.2)} 
Berry 5 5 {'blueberry': (5, 0.2), 'blackberry': (5, 0.2), 'straw': (5, 0.2)} 

Это делает невозможным выполнение любых операций со значениями внутри последнего элемента.

Я попытался сгладить вложенный словарь, чтобы получить простой список, но результат не сохраняет отношения между элементами. Что мне нужно, чтобы разделить словарь и иметь выход, который будет выглядеть примерно так:

Apple 1 2 gala  2 1.0 
Grape 2 4 malbec 4 0.25 
      merlot  4 0.75 
Pear 4 5 anjou  5 0.2 
      bartlet  5 0.4 
      seckel  5 0.2 
      bosc  5 0.2 
Berry 5 5 blueberry 5 0.2 
      blackberry 5 0.2 
      straw  5 0.2 

Я имею в виду нечто вроде этого, потому что я не совершала в этот формат, но к идее о том, что иерархическое отношение словарь не будет потерян в выходном файле. Есть ли способ сделать это? Я действительно новичок в python и ценю любую помощь. Благодаря!

+1

Вы смешивание яблок и апельсинов здесь.Существуют способы представления дерева в плоском формате, таком как csv, но если у вас нет веских оснований придерживаться csv, вы должны сохранить его в формате, который позволяет вам сохранять dicts. Файл JSON может быть вариантом. –

ответ

1

Предполагая, что вы должны хранить его в CSV с одной строкой на элемент в dict, следующее показывает, как вы можете его писать и читать. Это неэффективно и не оптимально, если у вас большой набор данных, поскольку он повторяет данные в каждой строке, однако он будет очень хорошо сжиматься.

#!/usr/bin/env python 
# -*- coding: utf-8 -*- 
"""csv_dict.py 
""" 
import csv 
import pprint 
from collections import namedtuple 


Row = namedtuple('Row', [ 
    'name', 
    'value_1', 
    'value_2', 
    'extra_name', 
    'extra_value_1', 
    'extra_value_2' 
]) 


l = [ 
    ('Apple', 1, 2, {'gala': (2, 1.0)}), 
    ('Grape ', 2, 4, {'malbec': (4, 0.25), 'merlot': (4, 0.75)}), 
    ('Pear', 4, 5, { 
     'anjou': (5, 0.2), 
     'bartlet': (5, 0.4), 
     'seckel': (5, 0.2)} 
    ), 
    ('Berry', 5, 5, { 
     'blueberry': (5, 0.2), 
     'blackberry': (5, 0.2), 
     'straw': (5, 0.2) 
    }) 
] 

print('List before writing: ') 
pprint.pprint(l) 

# Writing the data. 
with open('test1.csv', 'wb') as fout: 
    writer = csv.writer(fout) 

    for row in l: 
     for k, v in row[3].iteritems(): 
      writer.writerow(row[0:3] + (k,) + v) 

# Reading the data. 
format_extra = lambda row: (int(row.extra_value_1), float(row.extra_value_2)) 

with open('test1.csv', 'rU') as fin: 
    reader = csv.reader(fin) 

    ll = [] 
    hl = {} 

    for row in (Row(*r) for r in reader): 
     if row.name in hl: 
      ll[hl[row.name]][3][row.extra_name] = format_extra(row) 
      continue 

     ll.append(row[0:3] + ({ 
      row.extra_name: format_extra(row) 
     },)) 
     hl[row.name] = len(ll) - 1 

    pprint.pprint(ll) 
+0

Благодарим вас за то, что вы пытаетесь помочь мне. Мне нравится ваше решение, но оно не работает для меня, потому что я получаю объект AttributeError: «dict» не имеет атрибута «итерации» после: для строки в l: для k, v в строке [3] .iteritems(): что как вы думаете, может быть причиной этого? – user2962024

+0

@ user2962024 Вы опечатали «iteritems»? Вы работаете на Python 3? В py3k 'iteritems()' заменен на 'items()'. Я могу подтвердить свой пример выше результатов на py2.7. – TkTech

+0

Я использую Python 3.2, и я заменил 'iteritems()' на 'items()', я должен был обратить больше внимания. Но теперь я получаю 'TypeError: 'str' не поддерживает буферный интерфейс' в той же точке. – user2962024

0

Похоже, вы довольно близко. Несколько баллов - вам не нужно инициализировать test_file, и вы можете поставить length в итераторе.

Если бы я писал это в формат CSV, я бы, вероятно, использовать

with open('test1.csv', 'w', encoding = 'utf-8') as test_file: 
    for row in l: 
    species_data = row[:3] 
    for subspecies, subspecies_data in row[4].iter_items(): 
     write_row = species_data + [subspecies] + list(subspecies_data) 
     test_file.write(','.join([str(j) for j in write_row])) 

Конечно, есть оптимизации вы могли бы сделать, если это был большой список, или если бы вы были очень обеспокоены повторением информации.

0

Вот быстрая функция, которую я модифицировал, чтобы взять список, кортеж или диктофон и сгладить его. Он сгладит все вложенные части.

Я изменил ваш код и протестировал его на python 2.7. Это должно генерировать вывод, который вы ищете:

def flatten(l): 
''' 
flattens a list, dict or tuple 
''' 
    ret = [] 
    for i in l: 
     if isinstance(i, list) or isinstance(i, tuple): 
      ret.extend(flatten(i)) 
     elif isinstance(i, dict): 
      ret.extend(flatten(i.items())) 
     else: 
      ret.append(i) 
    return ret 

l = [('Apple', 1, 2, {'gala': (2, 1.0)}), 
('Grape ', 2, 4, {'malbec': (4, 0.25), 'merlot': (4, 0.75)}), 
('Pear', 4, 5, {'anjou': (5, 0.2), 'bartlet': (5, 0.4), 'seckel': (5, 0.2)}), 
('Berry', 5, 5, {'blueberry': (5, 0.2), 'blackberry': (5, 0.2), 'straw': (5, 0.2)})] 

test_file =() 
length = len(l[0]) 

with open('test1.csv', 'wb') as test_file: 
    csv_writer = csv.writer(test_file, delimiter=',') 
    for y in range(length): 
     line = flatten(l[y]) 
     csv_writer.writerow([x for x in line]) 
+0

Спасибо за ваше предложение и время. Я продолжаю получать ошибку: TypeError: 'str' не поддерживает буферный интерфейс. Любая идея почему? – user2962024

+0

В какой строке вы принимаете ошибку? – emh

+0

В самой последней строке: 'csv_writer.writerow ([x for x in line])', возможно, это связано с кодировкой «utf-8» в Python 3. Но я не мог решить ее сам. Есть идеи? Еще раз спасибо. – user2962024

0

Если вы настаиваете на CSV/TSV, вы должны иметь в виду, что это представление таблицы, но вы ожидаете, что это выглядит как структурированный файл (XML/JSON/YAML). Я бы рекомендовал использовать CSV/TSV для хранения данных в виде реляционных таблиц, иначе вы можете попасть в несколько беспорядочных выходов. В вашем случае, возможность выбрать для будет выводиться так:

заголовки:

SuperSpecieName,SpecieName,Value1,Value2 

данные:

"",Apple,1,2 
Apple,gala,2,1.0 
"",Grape,2,4 
Grape,malbec,4,0.25 
Grape,merlot,4,0.75 
... 
Смежные вопросы