Решим достаточно тривиальную задачу с помощью языка python — сравним две таблицы excel и выведем результат в третью. Что может быть проще, и почему просто не использовать средства самой программы, входящей в пакет office? Попробуем разобраться.

Дано
У нас есть две таблицы с условными названиями «Прайс1» и «Прайс2».


Обе имеют расширение .xlsx и открываются программой excel без каких-либо дополнительных действий. Но есть проблема — таблицы доступны в формате read-only дабы никто кроме владельца не мог изменить данные. Поэтому, для того, чтобы начать применять какие-либо формулы в самих таблицах необходимо таблицы продублировать, сохранив их дубликаты.
Вторая проблема — позиции товаров перемешаны, идут не в алфавитном порядке и вообще могут иметь разное количество позиций наименований.
И проблема третья — столбец с количеством товара не обязательно следует за столбцом с наименованиями товаров.
Как сравнить данные таблицы с наименьшими трудозатратами и сделать, так чтобы это сравнение легко адаптировалось под иные вводные?
Как правило, в задачах подобного рода применяется функция ВПР.
Например формула может выглядеть следующим образом:
Логика следующая: берем позицию в Прайсе2 и ищем ее по Прайсу1, выводя значение.
Однако, этот вариант работает не для всех случаев: если в Прайсе2 нет позиции, которая была в Прайсе1, формула не работает —
Формула посложней
Она повторяет предыдущую, но уже учитывает значение (количество товара) при поиске.
Но она также бесполезна, если позиция выбыла в Прайсе2:

И третий вариант формулы
Для небольшого удобства прайсы разнесены по разным листам одной таблицы, а сама итоговая таблица на третьем листе.
Для ячеек в столбце с Прайсом1 формула примет вид:
Для ячеек в столбце с Прайсом2:
Выглядит это следующим образом:

Здесь видно, что формула учитывает моменты, если в Прайсах пропадают или появляются позиции. В таблице они обозначены словом «Нет».
Формула работает. Но, помимо ужасающих размеров, она имеет одно «но», точнее два «но».
Чтобы все работало корректно, необходимо:
Неудобно.
Посмотрим, что предлагает python.
Решение №1
Можно пойти через использование библиотеки openpyxl и тогда решение будет выглядеть примерно так.
*Код написан не для прайсов, но для вычисления прямого и косвенного владения в компаниях, но логика та же.
Программа собирает все наименования и количество товара по ячейкам в обоих Прайсах, далее заполняет итоговую таблицу excel наименованиями и, найдя по координатам, количество товара — также и значениями количества товара.
Работает. Но громоздко и легко запутаться.
Решение №2
Воспользуемся возможностями библиотеки pandas, если она не установлена, то pip install pandas.
Импортируем библиотеку и считаем Прайсы в датафреймы(наборы данных):
Произведем слияние датафреймов, чтобы охватить случаи, когда товары исчезают/появляются как в первом Прайсе, так и во втором:
Создадим третий датафрейм из запросов к двум предыдущим и уберем оттуда дубликаты:
Осталось сохранить новую таблицу:
На выходе мы получаем итоговую таблицу:

Как видно, в нее не попала позиция «сок», так как в этой позиции не произошло изменений.
Обозначены позиции «пиво» со старым и новым значениями, а также учтены позиции, которые «добавились» и «пропали» в Прайсах.
Какое из решений использовать — дело вкуса.
Однако данный вариант имеет преимущества:
Код и примеры таблиц можно скачать — здесь.
Надеюсь, решения, приведенные в статье, окажутся полезными.

Дано
У нас есть две таблицы с условными названиями «Прайс1» и «Прайс2».


Обе имеют расширение .xlsx и открываются программой excel без каких-либо дополнительных действий. Но есть проблема — таблицы доступны в формате read-only дабы никто кроме владельца не мог изменить данные. Поэтому, для того, чтобы начать применять какие-либо формулы в самих таблицах необходимо таблицы продублировать, сохранив их дубликаты.
Вторая проблема — позиции товаров перемешаны, идут не в алфавитном порядке и вообще могут иметь разное количество позиций наименований.
И проблема третья — столбец с количеством товара не обязательно следует за столбцом с наименованиями товаров.
Как сравнить данные таблицы с наименьшими трудозатратами и сделать, так чтобы это сравнение легко адаптировалось под иные вводные?
Какие предложения от excel ?
Как правило, в задачах подобного рода применяется функция ВПР.
Например формула может выглядеть следующим образом:
=ЕСЛИОШИБКА(ВПР(F4;$B$3:$C$5;2;0);0)
Логика следующая: берем позицию в Прайсе2 и ищем ее по Прайсу1, выводя значение.
Однако, этот вариант работает не для всех случаев: если в Прайсе2 нет позиции, которая была в Прайсе1, формула не работает —

Формула посложней
Она повторяет предыдущую, но уже учитывает значение (количество товара) при поиске.
=ЕСЛИ(ЕСЛИОШИБКА(ВПР(F3;$B$3:$C$5;2;0);0)=G3;"";ЕСЛИОШИБКА(ВПР(F3;$B$3:$C$5;2;0);0))
Но она также бесполезна, если позиция выбыла в Прайсе2:

И третий вариант формулы
Для небольшого удобства прайсы разнесены по разным листам одной таблицы, а сама итоговая таблица на третьем листе.
Для ячеек в столбце с Прайсом1 формула примет вид:
=ЕСЛИ(ЕНД(ВПР(A2;'D:\Users\al\Desktop\[Прайс1.xlsx]Лист1'!$B$3:$B$5;1;0));"Нет";ВПР(A2;'D:\Users\al\Desktop\[Прайс1.xlsx]Лист1'!$B$3:$C$5;2;0))
Для ячеек в столбце с Прайсом2:
=ЕСЛИ(ЕНД(ВПР(A2;'D:\Users\al\Desktop\[Прайс2.xlsx]Лист1'!$B$3:$B$5;1;0));"Нет";ВПР(A2;'D:\Users\al\Desktop\[Прайс2.xlsx]Лист1'!$B$3:$C$5;2;0))
Выглядит это следующим образом:

Здесь видно, что формула учитывает моменты, если в Прайсах пропадают или появляются позиции. В таблице они обозначены словом «Нет».
Формула работает. Но, помимо ужасающих размеров, она имеет одно «но», точнее два «но».
Чтобы все работало корректно, необходимо:
- правильно указать диапазоны из Прайсов (выделить их в Прайсах Ctrl+Shift+Enter и перенести в формулу);
- позиции товаров в финальной таблице должны идти с учетом всех выбывших и/или прибывших позиций в обоих Прайсах. Сама формула не будет искать эти позиции в Прайсах и в вставлять в итоговую. Она просто берет наименование в итоговой таблице и ищет его в Прайсах, записывая количество товара и/или его отсутствие.
Неудобно.
Посмотрим, что предлагает python.
Python в деле
Решение №1
Можно пойти через использование библиотеки openpyxl и тогда решение будет выглядеть примерно так.
*Код написан не для прайсов, но для вычисления прямого и косвенного владения в компаниях, но логика та же.
текст программы
import openpyxl,pprint
from openpyxl.utils import get_column_letter,column_index_from_string
wb = openpyxl.load_workbook('Прайс1.xlsx')
sheet=wb.get_active_sheet()
wb2 = openpyxl.load_workbook('Прайс2.xlsx')
sheet2=wb2.get_active_sheet()
h = open('struct.txt','a')
test={}
test2={}
test3=[]
poisk=str(input('компания: '))
#test - словарь из "кто владеет:номер строки)
for row in sheet['A2':'A290']:
for cellObj in row:
i = cellObj.value
b = cellObj.row
test.setdefault(i,b)
#test2 - словарь из "кем владеют:номер столбца)
for row in sheet['B1':'HH']:
for cellObj in row:
i = cellObj.value
b = cellObj.column
c = column_index_from_string(b) #переводим названия столбцов excel в цифры
test2.setdefault(i,c)
print('\n'+'прямое владение')
# прямое владение
for row1 in sheet['B2':'HH290']:
for cellObj in row1:
if cellObj.value ==None: #пропускаем пустые значения в клетках
continue
i = float (cellObj.value)/100 #A в B
s =sheet.cell(row=cellObj.row,column=1).value
if s!=poisk:
continue
d=sheet.cell(row=1,column=column_index_from_string(cellObj.column)).value #B (кем владеют)
for k,v in test.items():
for u in range (2,217): # все значения- B2:F6
if sheet.cell(row=v, column=u).value ==None:
continue
b = sheet.cell(row=v, column=u).value # % владения
q=float('{:.5f}'.format(i*100))
y=sheet.cell(row=1,column=u).value #кем владеют
p=s+' владеет '+ d +' - '+str(q)+'%'
if p not in test3:
test3.append(p)
print(p)
h.write(p+'\n')
print('\n'+'1-е косвенное участие')
# 1-е косвенное участие
for row1 in sheet['B2':'HH290']:
for cellObj in row1:
if cellObj.value ==None: #пропускаем пустые значения в клетках
continue
i = float (cellObj.value)/100 #A в B
s =sheet.cell(row=cellObj.row,column=1).value
if s!=poisk:
continue
d=sheet.cell(row=1,column=column_index_from_string(cellObj.column)).value #B (кем владеют)
for k,v in test.items():
if d in k: # если кем владеют, есть в кто владеет - то ищем по строке значение
for u in range (2,217): # все значения
if sheet.cell(row=v, column=u).value ==None:
continue
b = sheet.cell(row=v, column=u).value # % владения
q=float(i)*float(b) #процент косвенного владения A через B в С
q1=float('{:.5f}'.format(q)) #5 знаков после запятой и * 100
y=sheet.cell(row=1,column=u).value #кем владеют
print (' через '+ d + ' в ' + y +' - '+str(q1)+'%')
h.write(s+' через '+ d + ' владеет ' + y +' - '+str(q1)+'%'+'\n')
h.write('\n')
Программа собирает все наименования и количество товара по ячейкам в обоих Прайсах, далее заполняет итоговую таблицу excel наименованиями и, найдя по координатам, количество товара — также и значениями количества товара.
Работает. Но громоздко и легко запутаться.
Решение №2
Воспользуемся возможностями библиотеки pandas, если она не установлена, то pip install pandas.
Импортируем библиотеку и считаем Прайсы в датафреймы(наборы данных):
import pandas as pd
df1 = pd.read_excel('Прайс1-.xlsx', sheet_name = 'Лист1')
df2 = pd.read_excel('Прайс2-.xlsx', sheet_name = 'Лист1')
Произведем слияние датафреймов, чтобы охватить случаи, когда товары исчезают/появляются как в первом Прайсе, так и во втором:
m = (df1.merge(df2, how='outer', on=['товар','Количество'],
suffixes=['', '_new'], indicator=True))
m2 = (df2.merge(df1, how='outer', on=['товар','Количество'],
suffixes=['', '_new'], indicator=True))
Создадим третий датафрейм из запросов к двум предыдущим и уберем оттуда дубликаты:
m3=pd.merge(m.query("_merge=='right_only'"), m2.query("_merge=='right_only'"), how ='outer').drop_duplicates(subset=['товар','Количество'])
Осталось сохранить новую таблицу:
m3.query("_merge=='right_only'").to_excel('out.xlsx')
На выходе мы получаем итоговую таблицу:

Как видно, в нее не попала позиция «сок», так как в этой позиции не произошло изменений.
Обозначены позиции «пиво» со старым и новым значениями, а также учтены позиции, которые «добавились» и «пропали» в Прайсах.
Какое из решений использовать — дело вкуса.
Однако данный вариант имеет преимущества:
- не требует перевода таблиц из «read-only»;
- нет необходимости вручную выправлять формулы по столбцам и сами таблицы.
Код и примеры таблиц можно скачать — здесь.
Надеюсь, решения, приведенные в статье, окажутся полезными.