2014-01-14 2 views
1

У меня есть CSV-файл этого форматаУдаление дубликатов в огромном файле .csv

testname unitname time data 
test1 1 20131211220159 123123 
test1 1 20131211220159 12345 
test1 1 20131211230180 1234 

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

Файл около 15 Мб (output_Temp.csv). Я скопировал его как output_temp2.csv

Это то, что у меня есть.

file1=open("output_temp.csv","r") 
file2=open("output_temp2.csv","r") 
file3=open("output.csv","w") 

flag=0 
linecounter=0 


for line in file1: 
    testname=line[0] 
    vid=line[1] 
    tstamp=line[2] 
    file2.seek(0) #reset 
    for i in range(linecounter): 
     file2.readline() #came down to the line # 
    for line2 in file2: 
     if testname==line2.split(",")[0] and vid==line2.split(",")[1] and tstamp!=line2.split(",")[2]: 
      flag==1 
      print line 
     if flag==1: 
      break 

    if flag==0: 
     file3.write(line) 
    linecounter=linecounter+1 #going down is ok dont go up. 
    flag=0 

Это занимает очень много времени, чтобы процесс, я думаю, что это может быть хорошо, но его буквально с 10 минут на 100kb и у меня есть длинный путь.

+0

В вашем вопросе постоянно говорится о «входном файле», но ваш код явно обрабатывает файлы ввода _two_. Что в каждом файле? – abarnert

+0

Это копии. Я собираю токены из первого файла, опуская второй файл. – Illusionist

+0

Для этого вам не нужна фактическая копия файла; вы можете «открыть» тот же файл дважды в режиме чтения и получить два разных файла. – abarnert

ответ

3

Основной причиной этого является то, что вы читаете весь файл (или, точнее, его копию) для каждой строки в файле. Итак, если есть 10000 строк, вы читаете 10000 строк 10000 раз, что означает 10000000 строк!

Если у вас достаточно памяти, чтобы сохранить строки, прочитанные до сих пор, есть очень простое решение: Храните линии, видимые до сих пор в наборе. (Или, скорее, для каждой строки сохраните кортеж из трех ключей, которые считаются дублирующими.) Для каждой строки, если она уже находится в наборе, пропустите ее; в противном случае обработайте его и добавьте в набор.

Например:

seen = set() 
for line in infile: 
    testname, vid, tstamp = line.split(",", 3)[:3] 
    if (testname, vid, tstamp) in seen: 
     continue 
    seen.add((testname, vid, tstamp)) 
    outfile.write(line) 

itertools recipes в документации есть функция unique_everseen, что позволяет обернуть это еще более красиво:

def keyfunc(line): 
    return tuple(line.split(",", 3)[:3]) 
for line in unique_everseen(infile, key=keyfunc): 
    outfile.write(line) 

Если набор занимает слишком много памяти , вы всегда можете подделать набор поверх dict, и вы можете подделать dict поверх базы данных, используя модуль dbm, который будет очень хорошо работать. o сохраняя достаточно в памяти, чтобы сделать все быстро, но недостаточно, чтобы вызвать проблему. Единственная проблема заключается в том, что ключи dbm должны быть строками, а не кортежами из трех строк ... но вы всегда можете просто объединить их (или вернуть join) вместо разделения, а затем у вас есть строка.


Я предполагаю, что, когда вы говорите, что файл «сортируется», вы имеете в виду с точки зрения временной метки, а не с точки зрения ключевых столбцов. То есть, нет гарантии, что две строки, которые являются дубликатами, будут рядом друг с другом. Если бы это было, это еще проще. Он может не выглядеть проще, если вы используете рецепты itertools; вы просто заменить everseen с justseen:

def keyfunc(line): 
    return tuple(line.split(",", 3)[:3]) 
for line in unique_justseen(infile, key=keyfunc): 
    outfile.write(line) 

Но под одеялом, это только отслеживанием последней строки, а не набор всех линий. Это не только быстрее, но и экономит массу памяти.


Теперь, когда (я думаю) я понимаю ваши требования лучше, что вы на самом деле хотите, чтобы избавиться от не все, кроме первой строки с тем же testname, vid и tstamp, но и все строки с такие же testname и vid кроме одного с наивысшим tstamp. И поскольку файл сортируется в порядке возрастания tstamp, это означает, что вы можете полностью игнорировать tstamp; вам просто нужен последний матч для каждого.

Это значит, что трюк everseen не будет работать - мы не можем пропустить первый, потому что мы еще не знаем, что есть более поздний.

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

def keyfunc(line): 
    return tuple(line.split(",", 2)[:2]) 
for line in reversed(list(unique_everseen(reversed(list(infile)), key=keyfunc))): 
    outfile.write(line) 

Поворачивая эти ленивые итераторы в списки, чтобы мы могли реверс их занимает слишком много памяти, это, вероятно, самый быстрый, чтобы сделать несколько проходов: обратный файл на диске, затем процеживают обращенного файл, затем снова отмените его. Это означает две дополнительные записи файлов, но это может быть намного лучше, чем, скажем, замена виртуальной памяти вашей ОС на и с диска сотни раз (или ваша программа просто сбой с MemoryError).

Если вы готовы сделать работу, это не было бы , что трудно написать обратный файл итератор, который считывает буферы с конца и расколы на переводы строк и дает таким же образом, объект file/io.Whatever делает. Но я бы не стал беспокоиться, если вам не понадобится.


Если вы когда-нибудь делать необходимости неоднократно прочитанными отдельные номера строк из файла, linecache модуля обычно ускорить много. Конечно, не так быстро, как не перечитывать, но намного лучше, чем чтение и анализ тысяч строк.

Вы также теряете время, повторяя некоторую работу во внутреннем цикле. Например, вы вызываете line2.split(",") три раза, вместо того, чтобы просто разбивать его один раз и фиксировать значение в переменной, что будет в три раза быстрее. 3-кратное постоянное усиление нигде не столь важно, как квадратичное линейное усиление, но когда оно приходит бесплатно, сделав ваш код более простым и понятным, он также может его принять.

+0

Спасибо, у вас есть пример в python? – Illusionist

+0

Это было бы очень хорошо, если бы у меня не было дубликатов в тесте/модуле/дате, которое я хочу сохранить. Я хочу только избавиться от старых данных. Любые указатели на то, как я могу это сделать? – Illusionist

+0

@Illusionist: Я не понимаю ваших требований. Что считается «старыми данными» и что считается «повторяющимися, но не старыми данными»? – abarnert

1

Для этого большого размера файла (~ 15 МБ) Pandas будет отличным выбором. Как это:

import pandas as pd 
raw_data = pd.read_csv() 
clean_data = raw_data.drop_duplicates() 
clean_data.to_csv('/path/to/clean_csv.csv') 

Я был в состоянии обрабатывать CSV файл о 151MB размера, содержащего более 5.9Million строк менее чем за секунду с указанным выше фрагментом. Обратите внимание, что проверка дубликатов может быть условной операцией или подмножеством полей, которые должны быть сопоставлены для повторной проверки. Pandas предоставляет множество этих функций из коробки. Документация here

Смежные вопросы