Сравнение двух excel файлов python

Время на прочтение
5 мин

Количество просмотров 12K

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

Дано

У нас есть две таблицы с условными названиями «Прайс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:UsersalDesktop[Прайс1.xlsx]Лист1'!$B$3:$B$5;1;0));"Нет";ВПР(A2;'D:UsersalDesktop[Прайс1.xlsx]Лист1'!$B$3:$C$5;2;0))

Для ячеек в столбце с Прайсом2:

=ЕСЛИ(ЕНД(ВПР(A2;'D:UsersalDesktop[Прайс2.xlsx]Лист1'!$B$3:$B$5;1;0));"Нет";ВПР(A2;'D:UsersalDesktop[Прайс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»;
  • нет необходимости вручную выправлять формулы по столбцам и сами таблицы.

Код и примеры таблиц можно скачать — здесь.

Надеюсь, решения, приведенные в статье, окажутся полезными.

Asked
6 years, 11 months ago

Viewed
94k times

I have two xlsx files as follows:

value1   value2   value3
0.456   3.456    0.4325436
6.24654 0.235435 6.376546
4.26545 4.264543 7.2564523

and

value1   value2  value3
0.456   3.456    0.4325436
6.24654 0.23546  6.376546
4.26545 4.264543 7.2564523

I need to compare all cells, and if a cell from file1 != a cell from file2 print that.

import xlrd
rb = xlrd.open_workbook('file1.xlsx')
rb1 = xlrd.open_workbook('file2.xlsx')
sheet = rb.sheet_by_index(0)
for rownum in range(sheet.nrows):
    row = sheet.row_values(rownum)
    for c_el in row:
        print c_el

How can I add the comparison cell of file1 and file2 ?

Martin Evans's user avatar

Martin Evans

45.3k16 gold badges83 silver badges94 bronze badges

asked May 9, 2016 at 10:14

1

Use pandas and you can do it as simple as this:

import pandas as pd

df1 = pd.read_excel('excel1.xlsx')
df2 = pd.read_excel('excel2.xlsx')

difference = df1[df1!=df2]
print difference

And the result will look like this:

enter image description here

answered May 9, 2016 at 10:46

Abbas's user avatar

AbbasAbbas

3,8126 gold badges35 silver badges62 bronze badges

The following approach should get you started:

from itertools import zip_longest
import xlrd

rb1 = xlrd.open_workbook('file1.xlsx')
rb2 = xlrd.open_workbook('file2.xlsx')

sheet1 = rb1.sheet_by_index(0)
sheet2 = rb2.sheet_by_index(0)

for rownum in range(max(sheet1.nrows, sheet2.nrows)):
    if rownum < sheet1.nrows:
        row_rb1 = sheet1.row_values(rownum)
        row_rb2 = sheet2.row_values(rownum)

        for colnum, (c1, c2) in enumerate(zip_longest(row_rb1, row_rb2)):
            if c1 != c2:
                print("Row {} Col {} - {} != {}".format(rownum+1, colnum+1, c1, c2))
    else:
        print("Row {} missing".format(rownum+1))

This will display any cells which are different between the two files. For your given two files, this will display:

Row 3 Col 2 - 0.235435 != 0.23546

If you prefer cell names, then use xlrd.formular.colname():

print "Cell {}{}  {} != {}".format(rownum+1, xlrd.formula.colname(colnum), c1, c2)

Giving you:

Cell 3B  0.235435 != 0.23546

Scott Stafford's user avatar

answered May 9, 2016 at 10:44

Martin Evans's user avatar

Martin EvansMartin Evans

45.3k16 gold badges83 silver badges94 bronze badges

2

I use a code for doing something similar. It’s a bit generalized works well.
Input Excel Sheets and expected Output Dataframe image

import pandas as pd
import numpy as np
from xlsxwriter.utility import xl_rowcol_to_cell

template = pd.read_excel("template.xlsx",na_values=np.nan,header=None)
testSheet = pd.read_excel("test.xlsx",na_values=np.nan,header=None)

rt,ct = template.shape
rtest,ctest = testSheet.shape

df = pd.DataFrame(columns=['Cell_Location','BaseTemplate_Value','CurrentFile_Value'])

for rowNo in range(max(rt,rtest)):
  for colNo in range(max(ct,ctest)):
    # Fetching the template value at a cell
    try:
        template_val = template.iloc[rowNo,colNo]
    except:
        template_val = np.nan

    # Fetching the testsheet value at a cell
    try:
        testSheet_val = testSheet.iloc[rowNo,colNo]
    except:
        testSheet_val = np.nan

    # Comparing the values
    if (str(template_val)!=str(testSheet_val)):
        cell = xl_rowcol_to_cell(rowNo, colNo)
        dfTemp = pd.DataFrame([[cell,template_val,testSheet_val]],
                              columns=['Cell_Location','BaseTemplate_Value','CurrentFile_Value'])
        df = df.append(dfTemp)

df is the required dataframe

answered Oct 11, 2018 at 15:15

sharinganSawant's user avatar

simple solution using load_workbook library

from openpyxl import load_workbook
wb1 = load_workbook('test.xlsx')
wb2 = load_workbook('validation.xlsx')
for worksheet in wb1.sheetnames:
    sheet1 = wb1[worksheet]
    sheet2 = wb2[worksheet]

    # iterate through the rows and columns of both worksheets
    for row in range(1, sheet1.max_row + 1):
        for col in range(1, sheet1.max_column + 1):
            cell1 = sheet1.cell(row, col)
            cell2 = sheet2.cell(row, col)
            if cell1.value != cell2.value:
                print("Sheet {0} -> Row {1} Column {2} - {3} != {4}".format(worksheet ,row, col, cell1.value, cell2.value))

    

answered Jun 13, 2022 at 11:26

Shivam verma's user avatar

df_file1 = pd.read_csv("Source_data.csv")
df_file1 = df_file1.replace(np.nan, '', regex=True) #Replacing Nan with space
df_file2 = pd.read_csv("Target_data.csv")
df_file2 = df_file2.replace(np.nan, '', regex=True)

df_i = pd.concat([df_file1, df_file2], axis='columns', keys=['file1', 'file2'])

df_f = df_i.swaplevel(axis='columns')[df_s.columns[0:]]

def highlight_diff(data, color='yellow'):
    attr = 'background-color: {}'.format(color)
    other = data.xs('file1', axis='columns', level=-1)
    return pd.DataFrame(np.where(data.ne(other, level=0), attr, ''), index=data.index, columns=data.columns)

df_final = df_f.style.apply(highlight_diff, axis=None)

writer = pd.ExcelWriter('comparexcels.xlsx')
df_final.to_excel(writer)
 writer.save()

Below Picture shows the output of file highlighting the differences

answered Nov 4, 2020 at 9:06

sowmya Sree's user avatar

import pandas as pd
import numpy as np

df1=pd.read_excel('Product_Category_Jan.xlsx')
df2=pd.read_excel('Product_Category_Feb.xlsx')

df1.equals(df2)

comparison_values = df1.values == df2.values
print (comparison_values)


rows,cols=np.where(comparison_values==False)

for item in zip(rows,cols):
    df1.iloc[item[0], item[1]] = '{} --> {}'.format(df1.iloc[item[0], 
    item[1]],df2.iloc[item[0], item[1]])

answered Nov 24, 2021 at 8:12

nick's user avatar

nicknick

3162 silver badges3 bronze badges

Comparing two excel spreadsheets and writing difference to a new excel was always a tedious task and Long Ago, I was doing the same thing and the objective there was to compare the row,column values for both the excel and write the comparison to a new excel files. In those days I have used xlrd module to read and write the comparison result of both the files in an excel file. I can still recall that we have written long lines of code to achieve that.

Recently at work, I encountered the same issue and retrieving my old xlrd script was not an option. So, i thought to give Pandas a try and amazingly I completed comparing the two excel files and writing the results to a new excel file in not more than 10 line of codes. I’m pretty sure that if I spend some more time then I can optimize the code further but this was a quick code that I wrote almost in no time for comparing over 100K records in both the excel file.

Let’s Start

I was comparing two excel files which contains the sales record of all the assets which the company sells to their customers in EU/EMEA/NA/APAC region. The two excel files I’m using is sample records from two Months i.e. Jan and Feb 2019 and contains the same no. of rows and columns

Import

First we need to import the two excel files in two separate dataframes

import pandas as pd

df1=pd.read_excel('Product_Category_Jan.xlsx')
df2=pd.read_excel('Product_Category_Feb.xlsx')

Next Step

Compare the No. of Columns and their types between the two excel files and whether number of rows are equal or not.

First,We will Check whether the two dataframes are equal or not using pandas.dataframe.equals , This function allows two Series or DataFrames to be compared against each other to see if they have the same shape and elements. NaNs in the same location are considered equal. The column headers do not need to have the same type, but the elements within the columns must be the same dtype

This function requires that the elements have the same dtype as their respective elements in the other Series or DataFrame

Basically, it checks for the following three things between two dataframe:

a) They have the same types and values for their elements and column labels
b) They have the same element types and values, but have different types for the column labels
c) They have different types for the same values for their elements

Compare Two Dataframe Values

In the above step we ensure that the shape and type of both the dataframes are equal and now we will compare the values of two dataframes

comparison_values = df1.values == df2.values
print (comparison_values)

In just one line we have compared the values of two dataframes and the comparison value for each row and column is shown as True and False values

Index of the Cell with False value

Get the Index of all the cells where the value is False, Which means the value of the cell differ between the two dataframes.

import numpy as np
rows,cols=np.where(comparison_values==False)

Next we will iterate over these cells and update the first dataframe(df1) value to display the changed value in second dataframe(df2)

for item in zip(rows,cols):
    df1.iloc[item[0], item[1]] = '{} --> {}'.format(df1.iloc[item[0], item[1]],df2.iloc[item[0], item[1]])

Export to Excel

Finally we have replaced the old value of dataframe(df1) and entered the new value in the following format:

dfl (Old Value) —-> df2(New Value)

Here is how the updated dataframe(df1) looks like:

So wherever there was a false value in the Comparison_value ndarray in the above step that has been replaced with the old and new value. Now you can export this dataframe into an excel or csv file and name it as Excel_diff.

I have set the index parameter as false otherwise the index will also be exported in the xlsx file as the first column and I have set the headers as True so that by default the dataframe headers will be the header in excel file as well.

df1.to_excel('./Excel_diff.xlsx',index=False,header=True)

Conclusion

Now if I compare my yesteryear code with the new and fast Pandas code then it really amuse me that how fast we have progressed and with the advent of modules like Pandas the things have become much simpler. Even you can directly read the records from SQL tables and write to the tables after processing. This new world is progressing at a faster speed and we all are optimistic with every day goes by we are near to see more intelligent and breakthroughs in the Python world.

Improve Article

Save Article

Like Article

  • Read
  • Discuss
  • Improve Article

    Save Article

    Like Article

    Given Two Excel Files, We want to compare the values of each column row-wise after sorting the values and print the changed column name and row number and values change.

    Input : 
    Two Excel files
    
    Output :
    Column name : 'location' and Row Number : 0
    Column name : 'location' and Row Number : 3
    Column name : 'date' and Row Number     : 1
    
    

    Code : Python code for comparing two excel files

    import pandas as pd

    sheet1 = pd.read_excel(r'Book1.xlsx')

    sheet2 = pd.read_excel(r'Book2.xlsx')

    for i,j in zip(sheet1,sheet2):

        a,b =[],[]

        for m, n in zip(sheet1[i],sheet2[j]):

            a.append(m)

            b.append(n)

        a.sort()

        b.sort()

        for m, n in zip(range(len(a)), range(len(b))):

            if a[m] != b[n]:

                print('Column name : '{}' and Row Number : {}'.format(i,m))

    Like Article

    Save Article

    import pandas as pd from pathlib import Path #define parameters #path to files path_old=Path(r’C:UsersownerDocumentsold.xlsx’) path_new=Path(r’C:UsersownerDocumentsnew.xlsx’) #list of key column(s) key=[‘id’] #sheets to read in sheet=‘Sheet1’ # Read in the two excel files and fill NA old = pd.read_excel(path_old).fillna(0) new = pd.read_excel(path_new).fillna(0) #set index old=old.set_index(key) new=new.set_index(key) #identify dropped rows and added (new) rows dropped_rows = set(old.index) set(new.index) added_rows = set(new.index) set(old.index) #combine data df_all_changes = pd.concat([old, new], axis=‘columns’, keys=[‘old’,‘new’], join=‘inner’) #prepare functio for comparing old values and new values def report_diff(x): return x[0] if x[0] == x[1] else ‘{} —> {}’.format(*x) #swap column indexes df_all_changes = df_all_changes.swaplevel(axis=‘columns’)[new.columns[0:]] #apply the report_diff function df_changed = df_all_changes.groupby(level=0, axis=1).apply(lambda frame: frame.apply(report_diff, axis=1)) #create a list of text columns (int columns do not have ‘{} —> {}’) df_changed_text_columns = df_changed.select_dtypes(include=‘object’) #create 3 datasets: #diff — contains the differences #dropped — contains the dropped rows #added — contains the added rows diff = df_changed_text_columns[df_changed_text_columns.apply(lambda x: x.str.contains(«—>») == True, axis=1)] dropped = old.loc[dropped_rows] added = new.loc[added_rows] #create a name for the output excel file fname = ‘{} vs {}.xlsx’.format(path_old.stem, path_new.stem) #write dataframe to excel writer=pd.ExcelWriter(fname, engine=‘xlsxwriter’) diff.to_excel(writer, sheet_name=‘diff’, index=True) dropped.to_excel(writer, sheet_name=‘dropped’, index=True) added.to_excel(writer, sheet_name=‘added’, index=True) #get xlswriter objects workbook = writer.book worksheet = writer.sheets[‘diff’] worksheet.hide_gridlines(2) worksheet.set_default_row(15) #get number of rows of the df diff row_count_str=str(len(diff.index)+1) #define and apply formats highligt_fmt = workbook.add_format({‘font_color’: ‘#FF0000’, ‘bg_color’:‘#B1B3B3’}) worksheet.conditional_format(‘A1:ZZ’+row_count_str, {‘type’:‘text’, ‘criteria’:‘containing’, ‘value’:‘—>’, ‘format’:highligt_fmt}) #save the output writer.save() print (nDone.n)

    Понравилась статья? Поделить с друзьями:
  • Сравнение данных в двух столбцах для поиска дубликатов в excel
  • Справочник по функциям excel если
  • Сравнение две ячейки в excel
  • Сравнение данных в excel в процентах
  • Справочник по формулам excel скачать