В этом сообщении блога мы углубимся в процесс сравнения двух файлов CSV с использованием Python и создания отдельного файла CSV, в котором будут отражены различия между ними.

Ссылка на GitHub — https://github.com/satsundev/compare-csv

Прежде чем мы отправимся в это путешествие, важно убедиться, что Python (https://www.python.org/downloads/) установлен на вашем компьютере.

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

Давайте создадим новый файл с именем compare.py и импортируем необходимые библиотеки: Pandas, os, json и sys. Это подготовит почву для процесса сравнения файлов CSV.

Чтобы облегчить множественное сравнение, мы можем создать входную конфигурацию, используя файл JSON. Этот подход позволит нам легко предоставлять входные данные и получать доступ к значениям. Давайте создадим новый файл с именем input.json внутри папки с именем data.

Шаг 1. Проверка существования файла

Прежде чем отправиться в путешествие, крайне важно проверить наличие необходимых файлов CSV. Эти файлы содержат ожидаемые и фактические данные для сравнения. Если какой-либо файл отсутствует, наши усилия корректно прекратятся.

# get the current working directory
cwd = os.getcwd()

# get the input JSON file.
input_JSON = os.path.join(cwd, "data", "input.json")
# load the JSON file into a variable
input_data = json.load(open(input_JSON))
# get the data object from input_data variable
# if we need we can also ensure the node is available
data = input_data["info"]

for info in data:
    print("Summary: " + info["description"])
    # get the expected JSON file
    expected = os.path.join(cwd, "inputs" ,"expected" ,info["expected"])
    # get the actual JSON file
    actual = os.path.join(cwd, "inputs", "actual" ,info["actual"])
    # diff path
    diff = os.path.join(cwd, "inputs", "diff" ,info["diff"])
    # summary path
    summary = os.path.join(cwd, "inputs", "diff", info["summary"])
    # common identity column with unique value between two CSV files to compare
    identity = info["identity"]
    # check the actual and expected CSV files are present
    # if not present intimate not present and stop execution
    if(not os.path.exists(expected) and not os.path.exists(actual)):
        # we can modify the custom message as needed
        print("## file in expected or actual path not present")
        sys.exit(0)

Шаг 2. Преобразование и сортировка данных

Подготовив файлы, мы переключаем внимание на преобразование данных и выполнение сравнения. Наша первая задача — отсортировать столбцы как ожидаемых, так и фактических наборов данных, чтобы обеспечить плавное выравнивание столбцов.

    else:
        # read the expected, actual CSV file
        expected_CSV = pd.read_csv(expected, encoding="utf-8")
        actual_CSV = pd.read_csv(actual, encoding="utf-8")
        # sort the expected file and actual file with same axis
        expected_CSV = expected_CSV.sort_index(axis=1)
        actual_CSV = actual_CSV.sort_index(axis=1)

Шаг 3: Объединение и сравнение данных

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

        expected_CSV_col = list(expected_CSV)
        # add suffix _expected for all expected column names
        expected_CSV_sfx = expected_CSV.add_suffix("_expected")
        # add suffix _actual for all actual column names
        actual_CSV_sfx = actual_CSV.add_suffix("_actual")
        # Outer Join expected and actual csv with identity
        comparision = pd.merge(expected_CSV_sfx, actual_CSV_sfx, how="outer", left_on=identity + "_expected", right_on=identity + "_actual")
        # create new column for every expected column with suffix '_compare'
        # compare each column and update the value comparision to '_compare' column
        for col in expected_CSV_col:
            comparision[(col + "_#compare")] = comparision[(col + "_actual")].fillna("-") == comparision[(col + "_expected")].fillna("-")

Шаг 4. Запишите разницу в формате CSV.

Наше путешествие завершается сохранением результатов сравнения данных. Кульминация наших усилий увековечена в новом файле CSV — артефакте, который отражает сложный танец между ожидаемыми и фактическими данными.

        # reorder the columns
        comparision = comparision.reindex(sorted(comparision.columns), axis=1)
        # write as CSV into diff folder
        comparision.to_csv(diff)

Шаг 5: Результат — CSV различий

Напомним весь код:

данные/input.json

{
    "info": [{
        "description": "This CSV file contains information about users present in an company.",
        "expected": "user_info_expected.csv",
        "actual": "user_info_actual.csv",
        "diff": "user_info_diff.csv",
        "summary": "user_info_summary.csv",
        "identity": "id"
    }]
}

сравнить.py

import pandas as pd
import os
import json
import sys

# get the current working directory
cwd = os.getcwd()

# get the input JSON file.
input_JSON = os.path.join(cwd, "data", "input.json")
# load the JSON file into a variable
input_data = json.load(open(input_JSON))
# get the data object from input_data variable
# if we need we can also ensure the node is available
data = input_data["info"]

for info in data:
    print("Summary: " + info["description"])
    # get the expected JSON file
    expected = os.path.join(cwd, "inputs" ,"expected" ,info["expected"])
    # get the actual JSON file
    actual = os.path.join(cwd, "inputs", "actual" ,info["actual"])
    # diff path
    diff = os.path.join(cwd, "inputs", "diff" ,info["diff"])
    # summary path
    summary = os.path.join(cwd, "inputs", "diff", info["summary"])
    # common identity column with unique value between two CSV files to compare
    identity = info["identity"]
    # check the actual and expected CSV files are present
    # if not present intimate not present and stop execution
    if(not os.path.exists(expected) and not os.path.exists(actual)):
        # we can modify the custom message as needed
        print("## file in expected or actual path not present")
        sys.exit(0)
    else:
        # read the expected, actual CSV file
        expected_CSV = pd.read_csv(expected, encoding="utf-8")
        actual_CSV = pd.read_csv(actual, encoding="utf-8")
        # sort the expected file and actual file with same axis
        expected_CSV = expected_CSV.sort_index(axis=1)
        actual_CSV = actual_CSV.sort_index(axis=1)
        # get all the expected column names as list
        expected_CSV_col = list(expected_CSV)
        # add suffix _expected for all expected column names
        expected_CSV_sfx = expected_CSV.add_suffix("_expected")
        # add suffix _actual for all actual column names
        actual_CSV_sfx = actual_CSV.add_suffix("_actual")
        # Outer Join expected and actual csv with identity
        comparision = pd.merge(expected_CSV_sfx, actual_CSV_sfx, how="outer", left_on=identity + "_expected", right_on=identity + "_actual")
        # create new column for every expected column with suffix '_compare'
        # compare each column and update the value comparision to '_compare' column
        for col in expected_CSV_col:
            comparision[(col + "_#compare")] = comparision[(col + "_actual")].fillna("-") == comparision[(col + "_expected")].fillna("-")
        # reorder the columns
        comparision = comparision.reindex(sorted(comparision.columns), axis=1)
        # write as CSV into diff folder
        comparision.to_csv(diff)
        

Надеюсь это поможет :)

Весь этот процесс, от проверки файлов до сравнения и сохранения данных, осуществляется посредством тщательно продуманной оркестровки магии Python и Pandas. Результатом является комплексный снимок различий в данных, который можно просмотреть, проанализировать и использовать для принятия обоснованных решений.