Найти все числа в одном файле, которых нет в другом файле в Python

Есть два файла, скажем FileA и FileB, и нам нужно найти все числа в FileA, которых нет в FileB. Все числа в FileA отсортированы, и все числа в FileB отсортированы. Например,

Вход:

FileA = [1, 2, 3, 4, 5, ...]
FileB = [1, 3, 4, 6, ...]

Выход:

[2, 5, ...]

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

Поэтому, если файлы достаточно малы, чтобы поместиться в память, мы могли бы загрузить их и инициализировать их содержимое в виде двух наборов, а затем взять разность наборов, чтобы решить проблему в O (1) или с постоянной сложностью времени.

set(contentsofFileA)-set(contentsofFileB)

Но поскольку файлы настолько велики, они не смогут полностью загружаться в память, и это невозможно.

Кроме того, другой подход заключается в использовании метода грубой силы с пакетной обработкой. Итак, мы загружаем фрагмент или пакет данных из FileA, а затем пакет из FileB, затем сравниваем его, а затем следующий фрагмент из FileB и так далее. Затем, после проверки фрагмента FileA над всеми элементами в FileB, загрузите следующий пакет из FileA, и это продолжится. Но это создаст сложность O (n ^ 2) или квадратичного времени и будет неэффективной для очень большого файла с большими записями.

Проблема должна быть решена с линейной или меньшей временной сложностью и без загрузки всех файлов в память. Любая помощь?

Ответ 1

Если вы хотите читать файлы построчно, поскольку у вас не так много памяти, и вам нужно линейное решение, вы можете сделать это с помощью iter, если ваши файлы основаны на строках, в противном случае см. это:

Сначала вы можете сделать это в своем терминале, чтобы сгенерировать несколько тестовых файлов:

seq 0 3 100 > 3k.txt
seq 0 2 100 > 2k.txt

Затем вы запускаете этот код:

i1 = iter(open("3k.txt"))
i2 = iter(open("2k.txt"))
a = int(next(i1))
b = int(next(i2))
aNotB = []
# bNotA = []
while True:
    try:
        if a < b:
            aNotB += [a]
            a = int(next(i1, None))
        elif a > b:
            # bNotA += [a]
            b = int(next(i2, None))
        elif a == b:
            a = int(next(i1, None))
            b = int(next(i2, None))
    except TypeError:
        if not b:
            aNotB += list(i1)
            break
        else:
            # bNotA += list(i1)
            break
print(aNotB)

Выход:

[3, 9, 15, 21, 27, 33, 39, 45, 51, 57, 63, 69, 75, 81, 87, 93, 99] If you want both the result for aNotB и bNotA you can uncomment those two lines.

Сравнение сроков с ответом Андрея Кесели:

$ seq 0 3 1000000 > 3k.txt
$ seq 0 2 1000000 > 2k.txt
$ time python manual_iter.py        
python manual_iter.py  0.38s user 0.00s system 99% cpu 0.387 total
$ time python heapq_groupby.py        
python heapq_groupby.py  1.11s user 0.00s system 99% cpu 1.116 total

Ответ 2

Когда файлы отсортированы, вы можете просто перебирать каждую строку за раз, если строка файла A меньше строки файла B, то вы знаете, что A нет в B, поэтому вы увеличиваете только файл A и затем проверяете снова. Если строка в A больше, чем строка в B, то вы знаете, что B не в A, поэтому вы увеличиваете только файл B. Если A и B равны, то вы знаете, что строка находится в обоих, так что увеличивайте оба файла. в то время как в исходном вопросе вы указали, что вас интересуют записи, которые находятся в A, но не в B, этот ответ расширит его, а также даст записи в B, а не в A. Это расширяет гибкость, но все же позволяет вам печатать только те, что в A, а не B.

def strip_read(file):
    return file.readline().rstrip()

in_a_not_b = []
in_b_not_a = []
with open("fileA") as A:
    with open("fileB") as B:
        Aline = strip_read(A)
        Bline = strip_read(B)
        while Aline or Bline:
            if Aline < Bline and Aline:
                in_a_not_b.append(Aline)
                Aline = strip_read(A)
            elif Aline > Bline and Bline:
                in_b_not_a.append(Bline)
                Bline = strip_read(B)
            else:
                Aline = strip_read(A)
                Bline = strip_read(B)

print("in A not in B", in_a_not_b, "\nin B not in A", in_b_not_a)

ВЫХОД для моих образцов файлов

in A not in B ['2', '5', '7'] 
in B not in A ['6']

Ответ 3

Вы можете комбинировать itertools.groupby (doc) и heapq.merge (doc), чтобы лениво перебирать FileA и FileB (это работает, пока файлы сортируются!)

FileA = [1, 1, 2, 3, 4, 5]
FileB = [1, 3, 4, 6]

from itertools import groupby
from heapq import merge

gen_a = ((v, 'FileA') for v in FileA)
gen_b = ((v, 'FileB') for v in FileB)

for v, g in groupby(merge(gen_a, gen_b, key=lambda k: int(k[0])), lambda k: int(k[0])):
    if any(v[1] == 'FileB' for v in g):
        continue
    print(v)

Печать:

2
5

ОБНОВЛЕНИЕ (чтение из файлов):

from itertools import groupby
from heapq import merge

gen_a = ((int(v.strip()), 1) for v in open('3k.txt'))
gen_b = ((int(v.strip()), 2) for v in open('2k.txt'))

for v, g in groupby(merge(gen_a, gen_b, key=lambda k: k[0]), lambda k: k[0]):
    if any(v[1] == 2 for v in g):
        continue
    print(v)

Benchmark:

Создание файлов из 10_000_000 элементов:

seq 0 3 10000000 > 3k.txt
seq 0 2 10000000 > 2k.txt

Сценарий занимает ~ 10 секунд.

real    0m10,656s
user    0m10,557s
sys 0m0,076s

Ответ 4

Простое решение, основанное на чтении файлов (при условии, что каждая строка содержит номер):

results = []
with open('file1.csv') as file1, open('file2.csv') as file2:
        var1 = file1.readline()
        var2 = file2.readline()
        while var1:
            while var1 and var2:
                if int(var1) < int(var2):
                    results.append(int(var1))
                    var1 = file1.readline()
                elif int(var1) > int(var2):
                    var2 = file2.readline()
                elif int(var1) == int(var2):
                    var1 = file1.readline()
                    var2 = file2.readline()
            if var1:
                results.append(int(var1))
                var1 = file1.readline()
print(results)
output = [2, 5, 7, 9]

Ответ 5

Это похоже на классическую сортировку и поиск Кнута. Вы можете рассмотреть возможность прочтения стека вопросов, интерактивные заметки для лекций pdf и Википедия. Вопрос стека упоминает кое-что, с чем я согласен, используя команду сортировки unix. Всегда проверяйте свои собственные данные, чтобы убедиться, что выбранный метод является наиболее эффективным для ваших данных, поскольку некоторые из этих алгоритмов зависят от данных.