2017-02-22 13 views
0

У меня есть представление о том, что я хочу, чтобы мой список словарей Python выглядел, но у меня возникают проблемы, которые вытягивают данные таблицы в структуру данных. Проблема заключается в том, что одна строка может иметь данные для заполнения значений родительского словаря, а также одного ребенка. Для последующих строк, если значения в столбцах для родителя пусты, тогда предположим, что столбцы для дочерних относятся к предыдущему родительскому элементу. Если мы столкнулись с новой строкой, где родительские данные не пусты, рассмотрите ее как новый родитель для добавления в список.Заполнение вложенного словаря данными электронной таблицы

Это пример того, что таблица будет выглядеть следующим образом:

+--------------+-------------------+---------+----------+--------------------+--------------+------------------+-------------+---------------+----------------+ 
| name   | descr    | adminSt | authSt | server_hostname_ip | server_descr | server_preferred | server_EPG | server_minPol | server_maxPoll | 
+--------------+-------------------+---------+----------+--------------------+--------------+------------------+-------------+---------------+----------------+ 
| test1-NTPPOL | Test NTP Policy | enabled | disabled | 10.10.10.10  | NTP1 server | yes    | oob-default | 4    | 6    | 
+--------------+-------------------+---------+----------+--------------------+--------------+------------------+-------------+---------------+----------------+ 
|    |     |   |   | 10.10.10.11  | NTP2 server | no    | oob-default | 4    | 6    | 
+--------------+-------------------+---------+----------+--------------------+--------------+------------------+-------------+---------------+----------------+ 
|    |     |   |   | 10.10.10.12  | NTP3 server | no    | oob-default | 4    | 6    | 
+--------------+-------------------+---------+----------+--------------------+--------------+------------------+-------------+---------------+----------------+ 
| test2-NTPPOL | Test 2 NTP policy | enabled | disabled | 20.10.10.10  | NTP1 server | yes    | oob-default | 4    | 6    | 
+--------------+-------------------+---------+----------+--------------------+--------------+------------------+-------------+---------------+----------------+ 
|    |     |   |   | 20.10.10.11  | NTP2 server | no    | oob-default | 4    | 6    | 
+--------------+-------------------+---------+----------+--------------------+--------------+------------------+-------------+---------------+----------------+ 
|    |     |   |   | 20.10.10.12  | NTP3 server | no    | oob-default | 4    | 6    | 
+--------------+-------------------+---------+----------+--------------------+--------------+------------------+-------------+---------------+----------------+ 

Я хотел бы структуру данных, чтобы выглядеть следующим образом:

[ 
    { 
    "name": "NTP_Policy1", 
    "descr": "NTP Policy 1", 
    "adminSt": "enabled", 
    "authSt": "disabled", 
    "servers": [ 
     { 
     "hostname": "10.10.10.10", 
     "descr": "NTP1 Server", 
     "preferred": true, 
     "server_EPG": "oob-default", 
     "minPoll": 4, 
     "maxPoll": 6 
     }, 
     { 
     "hostname": "20.10.10.10", 
     "descr": "NTP2 Server", 
     "preferred": false, 
     "server_EPG": "oob-default", 
     "minPoll": 4, 
     "maxPoll": 6 
     } 
    ] 
    }, 
    { 
    "name": "NTP_Policy2", 
    "descr": "NTP Policy 2", 
    "adminSt": "enabled", 
    "authSt": "disabled", 
    "servers": [ 
     { 
     "hostname": "30.10.10.10", 
     "descr": "NTP3 Server", 
     "preferred": true, 
     "server_EPG": "oob-default", 
     "minPoll": 4, 
     "maxPoll": 6 
     }, 
     { 
     "hostname": "40.10.10.10", 
     "descr": "NTP4 Server", 
     "preferred": false, 
     "server_EPG": "oob-default", 
     "minPoll": 4, 
     "maxPoll": 6 
     } 
    ] 
    } 
] 

Ближайший код, который я пришел посмотрел, как это, однако последующие строки имели дочерние элементы, прикрепленные к родительскому уровню.

>>> import pyexcel 
>>> from pprint import pprint 
>>> def excel_to_dict(sheet): 
...  rows = sheet.iter_rows() 
...  keys = next(rows) 
...  dict_list = [] 
...  # For each row in the spreadsheet, 
...  # Create an iterator pair so that the key is iterated over at the same time as its matching cell in the row 
...  # Then save that pairing as descriptors of the switch 
...  for row in rows: 
...   dict = {} 
...   dict['servers'] = [] 
...   server_atts = {} 
...   for key,cell in zip(keys, row): 
...    if str(cell.value) != 'None' and str(key.value) == 'name': 
...     dict[str(key.value)] = str(cell.value) 
...     parentKey = str(key.value) 
...    elif (str(cell.value) != 'None' and str(key.value) == 'descr') or (str(cell.value) != 'None' and str(key.value) == 'adminSt') or (str(cell.value) != 'None' and str(key.value) == 'authSt'): 
...     dict[str(key.value)] = str(cell.value) 
...    elif str(cell.value) == 'None': 
...     continue 
...    else: 
...     server_atts[str(key.value)] = str(cell.value) 
...   dict['servers'].append(server_atts.copy()) 
...   dict_list.append(dict.copy()) 
...  return dict_list 
>>> wb = openpyxl.load_workbook('aci_config.xlsx') 
>>> ntpPolsSheet = wb.get_sheet_by_name('ntp_pol') 
>>> ntpPols = excel_to_dict(ntpPolsSheet) 
>>> 
>>> pprint(ntpPols) 
[{'adminSt': 'enabled', 
    'authSt': 'disabled', 
    'descr': 'Test NTP Policy', 
    'name': 'test1-NTPPOL', 
    'servers': [{'server_EPG': 'oob-default', 
       'server_descr': 'NTP1 server', 
       'server_hostname_ip': '10.10.10.10', 
       'server_maxPoll': '6', 
       'server_minPol': '4', 
       'server_preferred': 'yes'}]}, 
{'servers': [{'server_EPG': 'oob-default', 
       'server_descr': 'NTP2 server', 
       'server_hostname_ip': '10.10.10.11', 
       'server_maxPoll': '6', 
       'server_minPol': '4', 
       'server_preferred': 'no'}]}, 
{'servers': [{'server_EPG': 'oob-default', 
       'server_descr': 'NTP3 server', 
       'server_hostname_ip': '10.10.10.12', 
       'server_maxPoll': '6', 
       'server_minPol': '4', 
       'server_preferred': 'no'}]}, 
{'adminSt': 'enabled', 
    'authSt': 'disabled', 
    'descr': 'Test 2 NTP policy', 
    'name': 'test2-NTPPOL', 
    'servers': [{'server_EPG': 'oob-default', 
       'server_descr': 'NTP1 server', 
       'server_hostname_ip': '20.10.10.10', 
       'server_maxPoll': '6', 
       'server_minPol': '4', 
       'server_preferred': 'yes'}]}, 
{'servers': [{'server_EPG': 'oob-default', 
       'server_descr': 'NTP2 server', 
       'server_hostname_ip': '20.10.10.11', 
       'server_maxPoll': '6', 
       'server_minPol': '4', 
       'server_preferred': 'no'}]}, 
{'servers': [{'server_EPG': 'oob-default', 
       'server_descr': 'NTP3 server', 
       'server_hostname_ip': '20.10.10.12', 
       'server_maxPoll': '6', 
       'server_minPol': '4', 
       'server_preferred': 'no'}]}] 

Что должен знать код, чтобы правильно заполнить список dict? Есть ли лучший формат электронных таблиц, который может облегчить импорт данных? Я пытаюсь сделать все это на одном листе, а не на нескольких листах.

+1

Вы не можете использовать 'pandas' для этой цели? он достигнет такого же результата с несколькими строками кода. –

+0

, вы должны преобразовать его в 'json' –

+0

, в чем проблема, с которой вы сталкиваетесь? данные поступают, но не так, как вы ожидаете? – aydow

ответ

0

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

name,descr,adminSt,authSt,server_hostname_ip,server_descr,server_preferred,server_EPG,server_minPoll, 
test1-NTPPOL,Test NTP Policy,enabled,disabled,10.10.10.10,NTP1 server,yes,oob-default,4,6 
,,,,10.10.10.11,NTP2 server,no,oob-default,4,6 
,,,,10.10.10.12,NTP3 server,no,oob-default,4,6 
test2-NTPPOL,Test 2 NTP policy,enabled,disabled,20.10.10.10,NTP1 server,yes,oob-default,4,6 
,,,,20.10.10.11,NTP2 server,no,oob-default,4,6 
,,,,20.10.10.12,NTP3 server,no,oob-default,4,6 

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

import pandas as pd 
from beeprint import pp 

def excel_to_dict(sheet): 
    dict_list = [] 
    last_test_dict = None 
    for i in xrange(len(sheet)): 
     # When we find a new row with a name value, we want to insert 
     # the old test_dict into the dict_list and make a new test_dict. 
     # Also, we want to skip the first row to not append an empty dict. 
     if pd.notnull(sheet.iloc[i]['name']): 
      if i != 0: 
       dict_list.append(test_dict) 
      test_dict = {} 
      test_dict['name'] = sheet.iloc[i]['name'] 
      test_dict['descr'] = sheet.iloc[i]['descr'] 
      test_dict['adminSt'] = sheet.iloc[i]['adminSt'] 
      test_dict['authSt'] = sheet.iloc[i]['authSt'] 
      test_dict['servers'] = [] 
      server_info = {} 
      server_info['server_hostname'] = sheet.iloc[i]['server_hostname_ip'] 
      server_info['server_descr'] = sheet.iloc[i]['server_descr'] 
      server_info['server_preferred'] = sheet.iloc[i]['server_preferred'] 
      server_info['server_EPG'] = sheet.iloc[i]['server_EPG'] 
      server_info['minPoll'] = sheet.iloc[i]['server_minPoll'] 
      server_info['maxPoll'] = sheet.iloc[i]['server_maxPoll'] 
      test_dict['servers'].append(server_info) 
      last_test_dict = test_dict # keep a handle to our new dict 
     else: 
      # Use the handle to the last test dict created to add info 
      # about a new server without modifying the name of the test 
      server_info = {} 
      server_info['server_hostname'] = sheet.iloc[i]['server_hostname_ip'] 
      server_info['server_descr'] = sheet.iloc[i]['server_descr'] 
      server_info['server_preferred'] = sheet.iloc[i]['server_preferred'] 
      server_info['server_EPG'] = sheet.iloc[i]['server_EPG'] 
      server_info['minPoll'] = sheet.iloc[i]['server_minPoll'] 
      server_info['maxPoll'] = sheet.iloc[i]['server_maxPoll'] 
      last_test_dict['servers'].append(server_info) 

    # In case we didn't enter the last test dict into the list 
    dict_list.append(last_test_dict) 
    return dict_list 

sheet = pd.read_csv('sheet.csv', sep=',') 
pp(excel_to_dict(sheet)) 
+0

Это, кажется, работает отлично. Единственный вопрос, который у меня есть, это «if i! = 0: then dict_list.append (test_dict)». Это будет означать, что мы не в первой строке, а не в строке с пустым именем, а затем добавим test_dict в наш основной список dict. Разве мы не добавляем все, что было в test_dict, когда мы в последний раз проходили эту часть кода? После этого мы берем данные с линии, как ожидалось. Я просто не понимаю, почему вам нужно добавить test_dict, если мы не указали индекс 0 и новую запись имени. – mikey

+0

Правильно, так что точка начала в начале такова, что всякий раз, когда мы обрабатываем новую строку, которая _does_ содержит новое тестовое имя, мы добавляем предыдущий 'test_dict' в' dict_list'. Однако, когда мы приходим к строке 1, предыдущий 'test_dict' был бы пустым, и мы пытались бы пустить пустой dict в' dict_list'. Поэтому я не делаю этого с i == 0. Это также означает, что окончательный 'test_dict' не будет введен в' dict_list' внутри цикла for, поэтому я добавил строку перед оператором return. – Chirag

+0

Я понимаю сейчас. Есть ли способ обобщить эту функцию? У меня есть другие листы с разными столбцами, которые выиграют от этой функции, за исключением того, что они жестко закодированы для встроенного макета, и мне нужно будет писать по-разному для каждого листа. Построить структуру данных из последнего столбца на первый будет самый простой способ сделать это? Я спрашиваю, потому что у меня есть лист с этими заголовками: сайт, здание, пол, комната, ряд, стойка. У сайта есть имя, которое может иметь несколько зданий, здание имеет имя и несколько этажей и так далее. – mikey

Смежные вопросы