2015-09-09 5 views
2

У меня есть набор дат:Performance - поиск строки в текстовом файле - Python

dates1 = {'21/5/2015', '4/4/2015', '15/6/2015', '30/1/2015', '19/3/2015', '25/2/2015', '25/5/2015', '8/2/2015', '6/6/2015', '15/3/2015', '15/1/2015', '30/5/2015'} 

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

то, что я есть сейчас это:

def dates(data, dates1): 
    lines = data.split("\n") 
    dict_days = {} 
    for day in dates1: 
     count = 0 
     for line in lines: 
      if day in line: 
       count += 1 
     dict_days[day] = count 

    newA = heapq.nlargest(5, dict_days, key=dict_days.get) 

    print(newA) 

Я расколоть текс в строках, создать Dict для каждой даты в списке это выглядит для него в каждой строке, и если он находит это добавляет 1 к сосчитать.

это прекрасно работает, НО он использует время, использующее этот метод.

Так что я прошу, если кто-то знает более эффективный способ сделать точно такой же

Любая помощь будет очень ценна

Редактировать

я попытаюсь каждый ответ и дайте знать, заранее заблаговременно

+3

Предупреждение: 'если день в строке:' является опасным, потому что если 'день == '1/1/2015'' будет в строке, которая равна '21/1/2015 «'. – DSM

+0

Используйте регулярные выражения вместо 'if day in line' и окружайте маркеры буквой' \ b', если они будут встречаться как целые слова. – mpcabd

+0

фантастический улов @DSM – taesu

ответ

7

Пронумеруйте строки один раз, извлекая любую дату, проверьте, установлена ​​ли дата в наборе, если поэтому увеличивает счетчик с помощью Counter Dict для подсчетов, на Counter.most_common конца вызова, чтобы получить 5 самых общие даты:

dates1 = {'21/5/2015', '4/4/2015', '15/6/2015', '30/1/2015', '19/3/2015', '25/2/2015', '25/5/2015', '8/2/2015', '6/6/2015', '15/3/2015', '15/1/2015', '30/5/2015'} 


from collections import Counter 
import re 

def dates(data, dates1): 
    lines = data.split("\n") 
    dict_days = Counter() 
    r = re.compile("\d+/\d+/\d+") 
    for line in lines: 
     match = r.search(line) 
     if match: 
      dte = match.group() 
      if dte in dates1: 
       dict_days[dte] += 1 
    return dict_days.most_common(5) 

Это делает один проход над списком строк, в отличие от одного прохода для все даты в датах1.

Для 100k строк с датой строки в конце строки с 200+ символов:

In [9]: from random import choice 

In [10]: dates1 = {'21/5/2015', '4/4/2015', '15/6/2015', '30/1/2015', '19/3/2015', '25/2/2015', '25/5/2015', '8/2/2015', '6/6/2015', '15/3/2015', '15/1/2015', '30/5/2015'} 

In [11]: dtes = list(dates1) 

In [12]: s = "the same dates appear in a text ('data' from now on). It's a pretty long text. I want to loop over the text and get the number of times each date appear in the text, then i print the 5 dates with more occurances. " 

In [13]: data = "\n".join([s+ choice(dtes) for _ in range(100000)]) 

In [14]: timeit dates(data,dates1) 
1 loops, best of 3: 662 ms per loop 

Если более чем одна дата может появиться в каждой строке можно использовать FindAll:

def dates(data, dates1): 
    lines = data.split("\n") 
    r = re.compile("\d+/\d+/\d+") 
    dict_days = Counter(dt for line in lines 
         for dt in r.findall(line) if dt in dates1) 
    return dict_days.most_common(5) 

Если данные фактически не являются файлом, подобным объекту, и представляют собой одну строку, просто выполните поиск самой строки:

def dates(data, dates1): 
    r = re.compile("\d+/\d+/\d+") 
    dict_days = Counter((dt for dt in r.findall(data) if dt in dates1)) 
    return dict_days.most_common(5) 

компилирования даты на тестовых данных, как представляется, самый быстрый подход, разделив каждую подстроку довольно близко к осуществлению поиска:

def dates_split(data, dates1): 
    lines = data.split("\n") 
    dict_days = Counter(dt for line in lines 
         for dt in line.split() if dt in dates1) 
    return dict_days.most_common(5) 

def dates_comp_date1(data, dates1): 
    lines = data.split("\n") 
    r = re.compile("|".join(dates1)) 
    dict_days = Counter(dt for line in lines for dt in r.findall(line)) 
    return dict_days.most_common(5) 

Используя функции выше:

In [63]: timeit dates(data, dates1) 
1 loops, best of 3: 640 ms per loop 

In [64]: timeit dates_split(data, dates1) 
1 loops, best of 3: 535 ms per loop 

In [65]: timeit dates_comp_date1(data, dates1) 
1 loops, best of 3: 368 ms per loop 
+1

Отличный ответ за обычный: P –

+1

Отлично выглядит. Позвольте мне попробовать это, и я дам вам знать, сэр. – NachoMiguel

+0

Я не привык к 're', но' r = r.search (line) '? Разве это не предотвратило бы проверку всех строк, кроме первого? –

4
Counter(word for word in my_text if word in my_dates) 

Я думаю, будет работать быстро ....хорошо O (N) (МОГ)

0

Почему бы не просто сделать:

dates = {'21/5/2015':0, '4/4/2015':0, '15/6/2015':0, '30/1/2015':0, '19/3/2015':0, '25/2/2015':0, '25/5/2015':0, '8/2/2015':0, '6/6/2015':0, '15/3/2015':0, '15/1/2015':0, '30/5/2015':0} 

def processDates(data): 
    lines = data.split("\n") 
    for line in lines: 
     if line in dates: 
      dates[line] += 1 

Тогда просто отсортируйте dates по значению

1

использовать регулярное выражение для извлечения данных, а collections.Counter найти наиболее общее:

import re 
import collections 

def dates(data, dates1): 
    dates1 = '|'.join(x for x in dates1) 
    dates1 = re.findall(dates1, data) 
    dates1 = collections.Counter(dates1) 
    print dates1.most_common(5) 

dates1 = {'21/5/2015', '4/4/2015', '15/6/2015'} 
data = 'Today is 21/5/2015. Yesterday is 4/4/2015.\nMy birthday is 4/4/2015' 

dates(data, dates1) 
Смежные вопросы