2014-10-30 2 views
1

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

Date Timestamp Identifier Value 

Размер файла составляет около 2 Мб и для каждого идентификатора в нем около 200 значений (около 600 тыс. строк). Можно прочитать этот файл с помощью pythons csv reader и извлечь нужные столбцы идентификатора. Поскольку у меня есть несколько наборов этих файлов, чтение в чувствует себя довольно медленно:

import csv 
def read_file(fhandle, identifiers): 
    #identifiers = [identifier1, identifier2, ...] 
    #dialect from csvr.sniffer() 
    csvr = csv.reader(fhandle, dialect) 
    data = [] 
    EOF = False    
    while not EOF: 
     try: 
      row=csvr.next()       
      if row[2] in identifiers: 
       data.append(tuple(row[1:])) 
     except StopIteration: 
      EOF = True 
    fhandle.close() 
    sorted(data, keyfunc) #keyfunc = lambda x: (x[1],x[0]) 
    return data 

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

import re 
import numpy as np 
def power_read(fhandle, identifier): 
    findme = '(?<=%s\W)[0-9.]+' %identifier  
    m = re.compile(findme) 
    result = np.zeros(10000) 
    cnt = 0 
    EOF = False 
    while not EOF: 
     try: 
      ln = fhandle.next() 
      found = re.search(m, ln) 
      if found: 
       result[cnt] = float(found.group(0)) 
       cnt += 1 
     except StopIteration: 
      EOF=True 
      fhandle.close() 
    return result[0:cnt] 

Это работает, но на самом деле не быстрее. Что еще я могу настроить?

+2

Вы пробовали 'numpy.loadtxt()' или 'numpy.genfromtxt()'? – Anton

+0

Я только что попробовал 'loadtxt' и' genfromtxt'. Я не мог использовать 'loadtxt()', потому что, по-видимому, некоторые отсутствующие значения, поэтому я переключился на 'genfromtxt()'. Очень медленно. – Faultier

+0

Другой ответ предполагает, что 'pandas.read_csv()' работает быстро: http://softwarerecs.stackexchange.com/questions/7463/fastest-python-library-to-read-a-csv-file – Anton

ответ

0

короткий ответ:

csv reader и haraprasadj (см ответ ниже) практически идентичны по скорости.

Длинный ответ:

Я попробовал все предложенные методы, будучи numpy.genfromtxt(), код haraprasadj предложил, и я возился с моей power_read() функции следующим образом:

def power_read(fhandle, identifiers): 
    findme = '(?<=%s\W)[0-9.]+' %identifiers[0]  
    result = np.zeros(10000) 
    cnt = 0 
    ALL = fhandle.read() 
    fhandle.close() 
    found = re.findall(findme, ALL, flags=re.S) 
    for f in found: 
     result[cnt] = float(f) 
    return result[0:cnt] 

Почему это изменение? Я понял, что, по всей видимости, это мудрая расследование занимает гораздо больше времени. Версия для вопросов - 18 с, комбинированная для 10 запусков, а версия выше - всего 6 секунд.

numpy.genfromtxt(fhandle, delimiter='\t', 
    dtype={'names':('Time', 'Identifier', 'Value'), 
      'formats':('datetime64[ns]', 'S50', 'f8')}) 

В той же установке используется около 41 секунды и дисквалифицируется.

Далее я оценивал среднее значение более 50 трасс и результат заключается в следующем:

  • нового power_read: 0,582 с
  • haraprasadj: 1,033 с
  • readfile: 1.081 s

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

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

def power_read(fhandle, identifiers): 
    ALL = fhandle.read() 
    fhandle.close() 
    result = {} 
    for i in identifiers: 
     findme = ('(?P<timestamp>\d+-\d+-\d+ \d+:\d+:\d+.[\d\+]+)\W%s\W(?P<value>[\d.]+)' %i) 
     res = np.empty(shape=(10000, 2), dtype=[('time','datetime64[ns]'), ('value','f4')]) 
     cnt = 0 
     found = re.findall(findme, ALL, flags=re.S) 
     for f in found: 
      res[cnt] = np.array(f, dtype=[('time','datetime64[ns]'), ('value','f4')]) 
     result[i] = res[0:cnt,:] 
    return result 

я тестировал с 1 ключевым словом и 3 ключевыми словами:

  • read_file 1kword = 1.1s 3kword = 1.1s
  • haraprasadj 1kword = 1.0с 3kword = 1.1s
  • power_read 1kword = 1.4s 3kword = 4.2s

резюмировать, если один только не стремится извлечь только одно значение для чтения CSV и метод haraprasadj кажутся выше. Тем не менее, пока еще не было выбора для двух верхних методов. У кого-то есть хорошая идея, как эффективно вывести результаты? Для того, чтобы исследовать, я изменил методы следующим образом и вызывается с тремя ключевыми словами:

def read_file(fhandle, identifiers, dialect): 
    csvr = csv.reader(fhandle, dialect) 
    data = [] 
    EOF = False    
    while not EOF: 
     try: 
      row=csvr.next()       
      if row[2] in identifiers: 
       data.append(tuple(row[1:-1])) 
     except StopIteration: 
      EOF = True 
    fhandle.close() 
    data = np.array(data, dtype=None) 
    time = data[:,0] 
    ids = data[:,1] 
    value = data[:,2] 
    res = {} 
    for i in identifiers: 
     msk = np.where(ids==i, True, False) 
     res[i] = np.array(zip(time[msk], value[msk]), dtype=[('time','datetime64[us]'), ('value','f8')]) 
    return res 

и второй функции, оптимизированы для точных данных, что имею дело с:

def haraprasadj(fhandle, identifiers): 
    data = [line.strip().split('\t')[1:] for line in fhandle.readlines()] 
    fhandle.close() 
    result = np.array(filter(lambda x:x[1] in identifiers, data)) 
    time =result[:,0] 
    ids = result[:,1] 
    value = result[:,2] 
    res = {} 
    for i in identifiers: 
     msk = np.where(ids==i, True, False) 
     res[i] = np.array(zip(time[msk], value[msk]), dtype=[('time','datetime64[us]'), ('value','f8')]) 
    return res 

Оказывается, что оба подходы примерно с одинаковой скоростью (по крайней мере, для моего тестового размера файла):

  • read_file() 1,12 s
  • haraprasadj 1.06 s

Сравнение с предыдущими результатами показывает, что приведение типов требует лишь незначительное количество времени, что было для меня неожиданным.
Оставшаяся разница в том, что haraprasadj занимает больше памяти, что может быть важно для некоторых приложений. Для удобства чтения я поеду с моим оригинальным файлом read_file(), и теперь начну смотреть на параллельные вычисления.

1

Если бы подобное требование, и я сделал что-то вроде:

data = [line.strip('\n').split(',') for line in open('test.txt','r').readlines()] 
identifiers = ['2069784', '2640650'] 
filteredData = filter(lambda x:x[2] in identifiers, data) 

Пробовал сроков его теперь и для текстового файла размером 52 МБ (64 столбцов, 60897 строк) она занимает менее 3 секунд, чтобы извлекающих необходимые строки , Результат имеет 308 строк.

Обратите внимание: мой файл разделен запятой и разделяется запятой. Также я использую машину Windows 7 с оперативной памятью 8 ГБ.

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

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