найти общий список между файлами

У меня есть три текстовых файла:

файлА:

13  abc
123 def
234 ghi
1234    jkl
12  mno

файл Б:

12  abc
12  def
34  qwe
43  rty
45  mno

файлC:

12  abc
34  sdg
43  yui
54  poi
54  def

Я хотел бы увидеть, что все значения во 2-м столбце совпадают между файлами. Следующий код работает, если второй столбец уже отсортирован. но если 2-й столбец не отсортирован, как мне отсортировать 2-й столбец и сравнить файлы?

fileA = open("A.txt",'r')
fileB = open("B.txt",'r')
fileC = open("C.txt",'r')

listA1 = []
for line1 in fileA:
    listA = line1.split('\t')
    listA1.append(listA)


listB1 = []
for line1 in fileB:
    listB = line1.split('\t')
    listB1.append(listB)


listC1 = []
for line1 in fileC:
    listC = line1.split('\t')
    listC1.append(listC)

for key1 in listA1:
    for key2 in listB1:
        for key3 in listC1:
            if key1[1] == key2[1] and key2[1] == key3[1] and key3[1] == key1[1]:
                print "Common between three files:",key1[1]

print "Common between file1 and file2 files:"
for key1 in listA1:
    for key2 in listB1:
        if key1[1] == key2[1]:
            print key1[1]

print "Common between file1 and file3 files:"
for key1 in listA1:
    for key2 in listC1:
        if key1[1] == key2[1]:
            print key1[1]

person gthm    schedule 29.03.2013    source источник


Ответы (1)


Если вы просто хотите отсортировать A1, B1 и C1 по второму столбцу, это легко сделать:

listA1.sort(key=operator.itemgetter(1))

Если вы не понимаете itemgetter, это то же самое:

listA1.sort(key=lambda element: element[1])

Однако я думаю, что лучшим решением будет просто использовать set:

setA1 = set(element[1] for element in listA1)
setB1 = set(element[1] for element in listB1)
setC1 = set(element[1] for element in listC1)

Или, проще говоря, вообще не создавайте списки; сделай это:

setA1 = set()
for line1 in fileA:
    listA = line1.split('\t')
    setA1.add(listA[1])

Так или иначе:

print "Common between file1 and file2 files:"
for key in setA1 & setA2:
    print key

Чтобы еще больше упростить его, вы, вероятно, захотите сначала реорганизовать повторяющиеся вещи в функции:

def read_file(path):
    with open(path) as f:
        result = set()
        for line in f:
            columns = line.split('\t')
            result.add(columns[1])
    return result

setA1 = read_file('A.txt')
setB1 = read_file('B.txt')
setC1 = read_file('C.txt')

И тогда вы сможете найти дополнительные возможности. Например:

def read_file(path):
    with open(path) as f:
        return set(row[1] for row in csv.reader(f))

Как указывает Джон Клементс, вам даже не нужно, чтобы все три из них были наборами, просто A1, поэтому вместо этого вы могли бы сделать это:

def read_file(path):
    with open(path) as f:
        for row in csv.reader(f):
            yield row[1]

setA1 = set(read_file('A.txt'))
iterB1 = read_file('B.txt')
iterC1 = read_file('B.txt')

Единственное другое изменение, которое вам нужно, это то, что вы должны вызывать intersection вместо использования оператора &, поэтому:

for key in setA1.intersection(iterB1):

Я не уверен, что это последнее изменение на самом деле является улучшением. Но в Python 3.3, где единственное, что вам нужно сделать, это изменить return set(…) на yield from (…), я, вероятно, поступил бы таким образом. (Даже если файлы огромны и содержат тонны дубликатов, так что это снижает производительность, я бы просто вставил unique_everseen из itertools рецептов вокруг вызовов read_file.)

person abarnert    schedule 29.03.2013
comment
Или... иметь A1 и A2 в качестве генераторов, материализовать наименьший из них с помощью set, затем использовать его метод intersection и оставить остальные в качестве генератора.... - person Jon Clements♦; 30.03.2013
comment
@JonClements: Да, A2 и A3 могут быть просто (row[1] for row in csv.reader(f)), и только A1 должен быть явным set, сделанным из этого. - person abarnert; 30.03.2013