2009-11-14 2 views
2

Я озадачен с проблемой, показанной в примере ниже:маркировка дубликатов в файле CSV

"ID","NAME","PHONE","REF","DISCARD" 
1,"JOHN",12345,, 
2,"PETER",6232,, 
3,"JON",12345,, 
4,"PETERSON",6232,, 
5,"ALEX",7854,, 
6,"JON",12345,, 

Я хочу, чтобы обнаружить дубликаты в колонке «PHONE», и отметьте последующие дубликаты с помощью колонки «REF », со значением, указывающим на„ID“из первого пункта, а значения„Да“для„колонок DISCARD“

"ID","NAME","PHONE","REF","DISCARD" 
1,"JOHN",12345,1, 
2,"PETER",6232,2, 
3,"JON",12345,1,"Yes" 
4,"PETERSON",6232,2,"Yes" 
5,"ALEX",7854,, 
6,"JON",12345,1,"Yes" 

Итак, как я могу идти об этом? Я пробовал этот код, но моя логика была неправильной, конечно.

import csv 
myfile = open("C:\Users\Eduardo\Documents\TEST2.csv", "rb") 
myfile1 = open("C:\Users\Eduardo\Documents\TEST2.csv", "rb") 

dest = csv.writer(open("C:\Users\Eduardo\Documents\TESTFIXED.csv", "wb"), dialect="excel") 

reader = csv.reader(myfile) 
verum = list(reader) 
verum.sort(key=lambda x: x[2]) 
for i, row in enumerate(verum): 
    if row[2] == verum[i][2]: 
     verum[i][3] = row[0] 

print verum 

Ваше руководство и помощь будут очень признательны.

+0

Вы, кажется, полагаетесь на точное соответствие, чтобы определить, являются ли два номера телефонов дублирующими. Это обычно только в классе. В реальном мире один и тот же номер телефона может быть записан 12345678, 1234-5678, 1234 5678, (555) 1234-5678, + 1-555-1234-5678 и т. Д. В США/Канаде/etc ... в других областях вводятся нулевые значения, например + 61-412-345-678 и (0412) 345-678 являются одним и тем же мобильным телефоном «сотовый» в Австралии. Также несколько человек могут использовать один и тот же номер мобильного телефона; вы уверены, что не должны проверять имя, прежде чем отказаться? –

+0

Этот пример упрощен для моей потребности здесь. Данные прошли через чистку и нормализацию, и все данные по телефону находятся внутри страны.Я объясню реальный случай: У меня была база данных наших клиентов (компании) в электронной таблице Excel. Затем я вставил целую кучу записей из «Желтых страниц». В прошлом, когда был найден дубликат, мы просто исключили эту строку. Но теперь я пытаюсь использовать ссылки и флаг «отбрасывать», особенно для того, чтобы иметь записи, которые немного похожи. Я сначала делал это вручную, и мне потребовалось слишком много времени для около 6000 записей! – Eduardo

+0

После первого тестирования телефона и анализа результатов я намерен использовать difflib.SequenceMatcher для поля адреса. В моих тестах он неплохо справился. – Eduardo

ответ

7

Единственное, что вы должны хранить в памяти, пока оно работает, - это карта номеров телефонов с их идентификаторами.

map = {} 
with open(r'c:\temp\input.csv', 'r') as fin: 
    reader = csv.reader(fin) 
    with open(r'c:\temp\output.csv', 'w') as fout: 
     writer = csv.writer(fout) 
     # omit this if the file has no header row 
     writer.writerow(next(reader)) 
     for row in reader: 
      (id, name, phone, ref, discard) = row 
      if map.has_key(phone): 
       ref = map[phone] 
       discard = "YES" 
      else: 
       map[phone] = id 
      writer.writerow((id, name, phone, ref, discard)) 
+0

+1 Очень четкое и практичное решение: не использует обфускационную софистику (dict.setdefault и itertools.groupby) и использует имена вместо чисел для столбцов. –

+0

Да, setdefault делает мой немного менее ясным. Я старался быть настолько эффективным, насколько это возможно, но это, вероятно, не обязательно здесь. – Omnifarious

+0

Ваши заявления «с» могут привести к закрытию файлов, пока читатель или писатель все еще привязаны к ним. – Omnifarious

0

Звучит как домашнее задание. Поскольку это CSV-файл (и, следовательно, изменение размера записи почти невозможно), вам лучше всего загрузить весь файл в память и манипулировать им там, прежде чем записывать его в новый файл. Создайте список строк, которые являются исходными строками файла. Затем создайте карту, вставьте в номер телефона (ключ) и значение (id). Перед вставкой вы ищете номер, если он уже существует, вы обновляете строку, содержащую дублированный номер телефона. Если он еще не на карте, вы вставляете пару (телефон, идентификатор).

+0

Да, это звучит как один, но это не так. Реальная работа над реальной работой в рабочей среде, не связанной с программистами. Я просто упростил код, чтобы сосредоточиться на способе выразить свою проблему. И я был рад узнать, что использование dicts - это путь. – Eduardo

0
from operator import itemgetter 
from itertools import groupby 

import csv 
verum = csv.reader(open('data.csv','rb')) 

verum.sort(key=itemgetter(2,0)) 
def grouper(verum): 
    for key, grp in groupby(verum,itemgetter(2)): 
     # key = phone number, grp = records with that number 
     first = grp.next() 
     # first item gets its id written into the 4th column 
     yield [first[0],first[1],first[2],first[0],''] #or list(itemgetter(0,1,2,0,4)(first)) 
     for x in grp: 
      # all others get the first items id as ref 
      yield [x[0],x[1],x[2], first[0], "Yes"] 

for line in sorted(grouper(verum), key=itemgetter(0)): 
    print line 

Выходы:

['1', 'JOHN', '12345', '1', ''] 
['2', 'PETER', '6232', '2', ''] 
['3', 'JON', '12345', '1', 'Yes'] 
['4', 'PETERSON', '6232', '2', 'Yes'] 
['5', 'ALEX', '7854', '5', ''] 
['6', 'JON', '12345', '1', 'Yes'] 

Запись данных обратно читателю ;-)

+0

-1 Считывает файл в память. Четкость кода невелика. –

+0

Мне определенно нужно узнать больше об itertools.grouby. Но я все еще не понимаю это хорошо. – Eduardo

0

Я знаю одну вещь. Я знаю, что вам не нужно читать весь файл в памяти, чтобы выполнить это.

import csv 
myfile = "C:\Users\Eduardo\Documents\TEST2.csv" 

dest = csv.writer(open("C:\Users\Eduardo\Documents\TESTFIXED.csv", "wb"), dialect="excel") 

phonedict = {} 

for row in cvs.reader(open(myfile, "r")): 
    # setdefault sets the value to the second argument if it hasn't been set, and then 
    # returns what the value in the dictionary is. 
    firstid = phonedict.setdefault(row[2], row[0]) 
    row[3] = firstid 
    if firstid is not row[0]: 
     row[4] = "Yes" 
    dest.writerow(row) 
+0

-1 ... причины (1) с использованием dict.setdefault (который нуждается в объяснении) (2) с использованием индексов вместо значимых имен столбцов (3) с использованием 'is not' вместо '! =' (Что требует тщательного чтения проанализируйте код, чтобы убедиться, что он работает) (4) не использует префикс 'r' для строковых констант, которые являются именами файлов, содержащими обратную косую черту (5), устанавливая строку [3] безоговорочно, а не только при наличии дубликата (как указано в OP). –

+0

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

+0

@John Machin, OP ошибочен в последней точке. Результат ясно показывает, что столбец id установлен безоговорочно. – Omnifarious

0

Я работаю с большими 40k плюс записи файлов CSV, самый простой способ избавиться от простофили его с Access. 1. Создать новую базу данных, 2, вкладка «Таблицы» Получить внешние данные 3. Сохранить таблицу. 4. Вкладка «Запросы» Новый мастер поиска ошибок (совпадение по полю телефона, показать все поля и счет) 5. Сохранить запрос (экспорт имеет .txt, но имя dupes.txt) 6. Импортировать результат запроса в новую таблицу, не импортировать поле с номером ошибки 7. Запрос Найти непревзойденный (сопоставить по полю телефона, показать все поля в результате сохранить запрос, а затем экспортировать .txt, но name unique.txt) 8. Внести уникальный файл в существующую таблицу (обманы) 9. Теперь вы можете сохранять и экспортировать снова то, что вам нужно, и не иметь никаких обманов