Форум сайта python.su
16
Всем здравствуйте! Читаю книгу Лутца “Изучаем Python”, после прочтения части книги про функциональное программирование решил для закрепления прочитанного, а заодно для облегчения
задач по работе, попрактиковаться.
Имеется файл('input.xlsx'), в котором входные данные представлены в виде перечня участковых лесничеств, которые состоят из кварталов, которые, в свою очередь, состоят из выделов и занимают определенную площадь. Необходимо вывести данные в виде таблицы в выходном файле ('output.xlsx'), где в первой графе участковое лесничество, во второй перечень вида кварталы(выделы), в третьей площадь по участковому лесничеству (образцы файлов прикрепляю).
Вот что у меня получилось:
''' Создание списка арендуемых кварталов и выделов по участковым лесничествам для лесного участка (в качестве исходных данных необходим файл .xlsx с 4 колонками: Участковое лесничество, Квартал, Выдел, Площадь) ''' import os.path import openpyxl as xl def read_data(): ''' Читаем данные, на выходе получаем список списков вида [[Участковое лесничество, квартал, выдел, площадь], [...]] без шапки таблицы ''' # Выбираем входной файл в формате .xlsx с данными на рабочем столе wb = xl.load_workbook((os.path.join(os.path.expanduser('~'), 'Desktop', input('Enter the file name on your desktop: \n')))) ws = wb.active # Получаем список списков вида [Участковое лесничество, # Квартал, Выдел, Площадь] data = [[val.value for val in row] for row in ws.iter_rows()] # Удаляем шапку таблицы del data[0] return sorted(data) def list_vyd_to_str(list_vyd): ''' Получение строки со списком выделов вида '1-3,5,7,10-12' ''' result_list = [] lst = [] # Проверяем список на наличие выделов-дубликатов, # преобразуем элементы списка в числа, сортируем for vyd in list_vyd: if vyd not in lst: lst.append(vyd) else: print('Выделы-дубликаты в квартале!') lst = sorted(map(int, lst)) def check_list(): """ Функция для проверки отсутствия выделов в списке выделов в квартале """ for k, v in enumerate(range(lst[0], lst[-1])): if v not in lst: return k return len(lst) while lst: t = check_list() if t > 2: result_list += '{}-{},'.format(lst[0], lst[t-1]) lst = lst[t:] else: result_list += '{},'.format(lst[0]) lst = lst[1:] result_list = result_list[:-1] return ''.join(result_list) def data_processing(data_from_file): ''' Получаем список вида ['Участковое лесничество', 'кв....(выд....)', 'Сумма выделов по участковому лесничеству'] из списка вида [['Участковое лесничество', квартал, выдел, площадь], [...]] ''' # Получаем список вида ['Участковое лесничество', 'Квартал', [],[]], # который будем использовать в качестве шаблона # для отбора выделов, соответствующих определенному лесничеству # и определенному кварталу, два пустых списка для # перечня выделов и площадей соответственно list_temp = [[line[0], line[1], [], []] for line in data_from_file] # Удаляем повторяющиеся элементы этого списка list_forestry_kv = [] for forestry_kv in list_temp: if forestry_kv not in list_forestry_kv: list_forestry_kv.append(forestry_kv) # Добавляем в список выделы и их площади, получаем # список вида [['Участковое лесничество', # 'Квартал', [1,2,3,5,...], [0.1, ...]], [...]] for template in list_forestry_kv: for line in data_from_file: if (template[0] == line[0] and template[1] == line[1]): template[2].append(line[2]) template[3].append(line[3]) # Получаем список вида [['Участковое лесничество', # 'Квартал', '1-3,5', 0.1], [...]] for line in list_forestry_kv: line[2] = list_vyd_to_str(line[2]) line[3] = round(sum(map(float, line[3])), 1) # Сливаем квартал и выделы каждого элемента # последовательности в строку вида # кв.1(выд.1,2,3) list_forestry_kv = [(str(item[0]), 'кв.' + str(item[1]) + ' (выд.' + str(item[2]) + '),', item[3]) for item in list_forestry_kv] # Составляем список участковых лесничеств в файле list_temp = [line[0] for line in data_from_file] # Удаляем повторяющиеся элементы этого списка forestry_lst = [] for forestry in list_temp: if forestry not in forestry_lst: forestry_lst.append(forestry) # Создаем шаблон для выходных данных, который представляет # собой список списков по числу участковых лесничеств result_list = [[forestry, '', 0] for forestry in forestry_lst] # Приплюсовываем в каждый из этих списков во 2 и 3 # элемент соответственно строки вида кв.1(выд.1) и # площади for forestry in forestry_lst: for kv in list_forestry_kv: for item in result_list: if (forestry in kv and forestry in item): item[1] += kv[1] item[2] += kv[2] # Удаляем запятую в конце каждого списка, # а также округляем площади по лесничеству for item in result_list: item[1] = item[1][:-1] item[2] = round(item[2], 1) return result_list def write_data(data_for_write): ''' Записываем данные в файл ''' # Добавляем шапку таблицы # в список с данными wb = xl.Workbook() ws = wb.active ws.append(['Участковое лесничество', 'Квартал (выдел)', 'Площадь, га']) total = 0 # Сумма по лесному участку # Записываем в файл построчно все списки и # и подбиваем общую сумму for i in data_for_write: ws.append(i) total += float(i[-1]) ws.append(['Итого', '', total]) wb.save((os.path.join(os.path.expanduser('~'), 'Desktop', input('Enter the name for the saved file: \n')))) if __name__ == '__main__': write_data(data_processing(read_data()))
Отредактировано Stright (Март 22, 2015 00:34:32)
Офлайн
103
def list_vyd_to_str(list_vyd):
'кв.' + str(item[1]) + ' (выд.' + str(item[2]) + '),'
for forestry in forestry_lst: for kv in list_forestry_kv: for item in result_list:
Strightтам где вы пишите
но при выполнении занимает много памяти
Strightвы не удаляете а создаете новый!
# Удаляем повторяющиеся элементы
Отредактировано terabayt (Март 22, 2015 01:24:09)
Офлайн
568
del data[0]
def check_list(): """ Функция для проверки отсутствия выделов в списке выделов в квартале """ for k, v in enumerate(range(lst[0], lst[-1])): if v not in lst: return k return len(lst)
def check_list(): enum = enumerate(xrange(lst[0], lst[-1])) l = iter(lst) for k, v in enum: litem = l.next() if litem != v: return k return len(lst)
Отредактировано FishHook (Март 22, 2015 06:53:59)
Офлайн
16
terabaytОставил более читаемый, хотя способ решения понравился )
а как же мой код? ))
terabaytДа, действительно некрасиво, переделал
как-то не очень красиво, переделайте под format
terabaytдумал долго, прежде, чем такое написать, но это единственное, что пришло в голову
подумайте, может есть способ сделать все красивее, а то 3 вложенных один в один цикла выглядит не очень

terabaytНемного пообрезал входные данные для краткости:
а вот data_processing выделите в отдельную программу и на форум с примерами входных и выходных данных
lst = [['Курбское', 1, 1, 0.6], ['Курбское', 1, 2, 0.6], ['Курбское', 1, 3, 2.4], ['Курбское', 1, 7, 0.2], ['Курбское', 1, 8, 1.8], ['Курбское', 1, 9, 1.7], ['Курбское', 2, 1, 10.2], ['Курбское', 2, 2, 0.2], ['Курбское', 2, 3, 4], ['Курбское', 2, 4, 6.9], ['Ставотинское', 1, 28, 0.6], ['Ставотинское', 1, 29, 2.9], ['Ставотинское', 1, 30, 1], ['Ставотинское', 1, 31, 1.4], ['Ставотинское', 1, 37, 0.1], ['Ставотинское', 1, 38, 0.1]]
lst[i][0] - участковое лесничество, lst[i][1] - квартал, lst[i][2] - выдел, lst[i][3] - площадь
[['Курбское', 'кв.1 (выд.1-3,7-9),кв.2 (выд.1-4)', 28.6], ['Ставотинское', 'кв.1 (выд.28-31,37,38)', 6.1]]
def list_vyd_to_str(list_vyd): ''' Получение строки со списком выделов вида '1-3,5,7,10-12' из списка вида [1,2,3,5,7,10,11,12] ''' result_list = [] lst = sorted(list(set(list_vyd))) if len(lst) != len(list_vyd): print('Выделы-дубликаты в квартале') def check_list(): """ Функция для проверки отсутствия выделов в списке выделов в квартале """ l = iter(lst) for k, v in enumerate(range(lst[0], lst[-1])): l_item = l.__next__() if v != l_item: return k return len(lst) while lst: t = check_list() if t > 2: result_list.append('{}-{},'.format(lst[0], lst[t-1])) lst = lst[t:] else: result_list.append('{},'.format(lst[0])) lst = lst[1:] return ''.join(result_list)[:-1] def data_processing(data_from_file): ''' Получаем список вида ['Участковое лесничество', 'кв....(выд....)', 'Сумма выделов по участковому лесничеству'] из списка вида [['Участковое лесничество', квартал, выдел, площадь], [...]] ''' # Получаем список вида ['Участковое лесничество', 'Квартал', [],[]], # который будем использовать в качестве шаблона # для отбора выделов, соответствующих определенному лесничеству # и определенному кварталу, два пустых списка для # перечня выделов и площадей соответственно list_temp = [[line[0], line[1], [], []] for line in data_from_file] # Удаляем повторяющиеся элементы этого списка list_forestry_kv = [] for forestry_kv in list_temp: if forestry_kv not in list_forestry_kv: list_forestry_kv.append(forestry_kv) # Добавляем в список выделы и их площади, получаем # список вида [['Участковое лесничество', # 'Квартал', [1,2,3,5,...], [0.1, ...]], [...]] for template in list_forestry_kv: for line in data_from_file: if (template[0] == line[0] and template[1] == line[1]): template[2].append(line[2]) template[3].append(line[3]) # Получаем список вида [['Участковое лесничество', # 'Квартал', '1-3,5', 0.1], [...]] for line in list_forestry_kv: line[2] = list_vyd_to_str(line[2]) line[3] = round(sum(map(float, line[3])), 1) # Сливаем квартал и выделы каждого элемента # последовательности в строку вида # кв.1(выд.1,2,3) list_forestry_kv = [(str(item[0]), 'кв.{} (выд.{}),'.format(item[1], item[2]), item[3]) for item in list_forestry_kv] # Составляем список участковых лесничеств в файле list_temp = [line[0] for line in data_from_file] # Удаляем повторяющиеся элементы этого списка forestry_lst = [] for forestry in list_temp: if forestry not in forestry_lst: forestry_lst.append(forestry) # Создаем шаблон для выходных данных, который представляет # собой список списков по числу участковых лесничеств result_list = [[forestry, '', 0] for forestry in forestry_lst] # Приплюсовываем в каждый из этих списков во 2 и 3 # элемент соответственно строки вида кв.1(выд.1) и # площади for forestry in forestry_lst: for kv in list_forestry_kv: for item in result_list: if (forestry in kv and forestry in item): item[1] += kv[1] item[2] += kv[2] # Удаляем запятую в конце каждого списка, # а также округляем площади по лесничеству for item in result_list: item[1] = item[1][:-1] item[2] = round(item[2], 1) return result_list print(data_processing(lst))
Отредактировано Stright (Март 23, 2015 21:27:20)
Офлайн
16
FishHookСпасибо, поправил
А точно нельзя указать начало диапазона для iter_rows(), чтобы потом не удалять?
FishHookНет, не со мной, был бы благодарен за ссылку на эту тему. Спасибо за замечание, действительно, намного быстрее работает.
Мы, по моему, с вами уже обсуждали in в применении к списку
Офлайн
103
нужно же в том же духе и вторуюфункцию сделать ))
def data_processing(lst, a={}): for i in lst: a[i[0]][1][i[1]], a[i[0]][0] = a.setdefault(i[0], [0, {}])[1].setdefault(i[1], []) + [i[2]], a[i[0]][0] + i[3] return [[i, ','.join(['кв.%d (выд.%s)' % (j, list_vyd_to_str(a[i][1][j][::2])) for j in a[i][1]]), a[i][0]] for i in a]
Отредактировано terabayt (Март 24, 2015 02:46:38)
Офлайн
16
Спасибо большое! Так и думал, что словари нужны, пытался сначала с помощью генераторов создать,
но запутался и взялся за списки 
Получилось как-то так:
lst = [['Курбское', 1, 1, 0.6], ['Курбское', 1, 2, 0.6], ['Курбское', 1, 3, 2.4], ['Курбское', 1, 7, 0.2], ['Курбское', 1, 8, 1.8], ['Курбское', 1, 9, 1.7], ['Курбское', 2, 1, 10.2], ['Курбское', 2, 2, 0.2], ['Курбское', 2, 3, 4], ['Курбское', 2, 4, 6.9], ['Ставотинское', 1, 28, 0.6], ['Ставотинское', 1, 29, 2.9], ['Ставотинское', 1, 30, 1], ['Ставотинское', 1, 31, 1.4], ['Ставотинское', 1, 37, 0.1], ['Ставотинское', 1, 38, 0.1]] def list_vyd_to_str(list_vyd): ''' Получение строки со списком выделов вида '1-3,5,7,10-12' из списка вида [1,2,3,5,7,10,11,12] ''' # Проверка полученного списка на дубликаты lst = sorted(list(set(list_vyd))) if len(lst) != len(list_vyd): print('Выделы-дубликаты в квартале') # Получаем список выделов, отсутствующих в # списке в виде списка [n - 1, ..., n + 1] items1 = sorted(list(set(range(int(list_vyd[0])-1, int(list_vyd[-1])+2)) - set(map(int, list_vyd)))) # Получаем список выделов, отсутствующих в # списке в виде списка [n, ..., n + 1] items2 = sorted(list(set(range(int(list_vyd[0])-1, int(list_vyd[-1])+2)) - set(map(int, list_vyd)))) missing_items = zip(items1, items2[1:]) # Соединяем в строку return ','.join('{}-{}'.format(a+1, b-1) if b - a > 3 else ','.join(map(str, range(a+1, b))) for a, b in missing_items if b - a > 1) def data_processing(lst, a={}): ''' Из списка вида [[Участковое лесничество, квартал, выдел, площадь], [...]], полученного с файла получаем список вида [Участковое лесничество, 'кв.n(выд. ...)', ] ''' # Создаем словарь вида # {'Участковое лесничество': [{'Кварталы': [выделы]}, # Площадь по участковому]} for i in lst: a[i[0]] = a.setdefault(i[0], [{}, 0]) a[i[0]][0][i[1]] = a[i[0]][0].setdefault(i[1], []) a[i[0]][0][i[1]].append(i[2]) a[i[0]][1] += i[3] # Создаем выходной список result = [] for i in a: result.append([i, ','.join(['кв.{} (выд.{})'.format(j, list_vyd_to_str(a[i][0][j])) for j in a[i][0]]), a[i][1]]) return result print(data_processing(lst))
list_vyd_to_str(a[i][1][j][::2]) # [::2] не надо
Отредактировано Stright (Март 25, 2015 22:34:23)
Офлайн