Предварительная обработка 400 миллионов твитов в Python - быстрее - PullRequest
5 голосов
/ 07 января 2012

У меня 400 миллионов твитов (на самом деле я думаю, что это почти как 450, но не берите в голову), в форме:

T    "timestamp"
U    "username"
W    "actual tweet"

Я хочу сначала записать их в файл в форме "username \ t"твит", а затем загрузить в БД.Проблема в том, что перед загрузкой в ​​БД необходимо сделать несколько вещей: 1. Предварительно обработать твит, чтобы удалить RT @ [names] и URL-адреса. 2. Извлечь имя пользователя из "http://twitter.com/username".

* 1006.* Я использую Python, и это код. Пожалуйста, дайте мне знать, как это можно сделать быстрее :)
'''The aim is  to take all the tweets of a user and store them in a table.  Do this for all the users and then lets see what we can do with it 
   What you wanna do is that you want to get enough information about a user so that you can profile them better. So , lets get started 
'''
def regexSub(line):
    line = re.sub(regRT,'',line)
    line = re.sub(regAt,'',line)
    line = line.lstrip(' ')
    line = re.sub(regHttp,'',line)
    return line
def userName(line):
    return line.split('http://twitter.com/')[1]


import sys,os,itertools,re
data = open(sys.argv[1],'r')
processed = open(sys.argv[2],'w')
global regRT 
regRT = 'RT'
global regHttp 
regHttp = re.compile('(http://)[a-zA-Z0-9]*.[a-zA-Z0-9/]*(.[a-zA-Z0-9]*)?')
global regAt 
regAt = re.compile('@([a-zA-Z0-9]*[*_/&%#@$]*)*[a-zA-Z0-9]*')

for line1,line2,line3 in itertools.izip_longest(*[data]*3):
    line1 = line1.split('\t')[1]
    line2 = line2.split('\t')[1]
    line3 = line3.split('\t')[1]

    #print 'line1',line1
    #print 'line2=',line2
    #print 'line3=',line3
    #print 'line3 before preprocessing',line3
    try:
        tweet=regexSub(line3)
        user = userName(line2)
    except:
        print 'Line2 is ',line2
        print 'Line3 is',line3

    #print 'line3 after processig',line3
    processed.write(user.strip("\n")+"\t"+tweet)

Я запустил код следующим образом:

python -m cProfile -o profile_dump TwitterScripts/Preprocessing.py DATA/Twitter/t082.txt DATA/Twitter/preprocessed083.txt

Это вывод, который я получаю: (Предупреждение: он довольно большой, и я не отфильтровывал маленькие значения, думая, что они также могут иметь какое-то значение)

Sat Jan  7 03:28:51 2012    profile_dump

         3040835560 function calls (3040835523 primitive calls) in 2500.613 CPU seconds

   Ordered by: call count

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
528840744  166.402    0.000  166.402    0.000 {method 'split' of 'str' objects}
396630560   81.300    0.000   81.300    0.000 {method 'get' of 'dict' objects}
396630560  326.349    0.000  439.737    0.000 /usr/lib64/python2.7/re.py:229(_compile)
396630558  255.662    0.000 1297.705    0.000 /usr/lib64/python2.7/re.py:144(sub)
396630558  602.307    0.000  602.307    0.000 {built-in method sub}
264420442   32.087    0.000   32.087    0.000 {isinstance}
132210186   34.700    0.000   34.700    0.000 {method 'lstrip' of 'str' objects}
132210186   27.296    0.000   27.296    0.000 {method 'strip' of 'str' objects}
132210186  181.287    0.000 1513.691    0.000 TwitterScripts/Preprocessing.py:4(regexSub)
132210186   79.950    0.000   79.950    0.000 {method 'write' of 'file' objects}
132210186   55.900    0.000  113.960    0.000 TwitterScripts/Preprocessing.py:10(userName)
  313/304    0.000    0.000    0.000    0.000 {len}

Удалены те, которые были действительно низкими (например,1, 3 и т. Д.)

Скажите, пожалуйста, какие еще изменения можно сделать. Спасибо!

Ответы [ 4 ]

7 голосов
/ 07 января 2012

Это то, для чего многопроцессорная .

У вас есть конвейер, который можно разбить на большое количество маленьких шагов. Каждый шаг - это Process, который позволяет получить элемент из трубы, выполняет небольшое преобразование и помещает промежуточный результат в следующую трубу.

У вас будет Process, который читает сырой файл по три строки за раз, и помещает эти три строки в трубу. Вот и все.

У вас будет Process, который получает тройку (T, U, W) из канала, очищает линию пользователя и помещает ее в следующий канал.

и т. Д. И т. Д.

Не создавайте слишком много шагов для начала. Чтение - преобразование - запись - хорошее начало, чтобы убедиться, что вы понимаете модуль multiprocessing. После этого эмпирическим исследованием выясняется, каково оптимальное сочетание этапов обработки.

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

Как правило, больше процессов, работающих одновременно, быстрее. В конечном итоге вы достигнете предела из-за накладных расходов ОС и ограничений памяти.

3 голосов
/ 07 января 2012

str.lstrip, вероятно, не делает то, что вы ожидали:

>>> 'http://twitter.com/twitty'.lstrip('http://twitter.com/')
'y'

из документов:

S.lstrip([chars]) -> string or unicode

Return a copy of the string S with leading whitespace removed.
If chars is given and not None, remove characters in chars instead.
If chars is unicode, S will be converted to unicode before stripping
3 голосов
/ 07 января 2012

Пока вы не запустите его через профилировщик, трудно понять, что нужно изменить.Тем не менее, я бы предположил, что наиболее вероятные замедления происходят, когда вы создаете и запускаете регулярные выражения.

Поскольку ваш файл соответствует определенному формату, вы можете увидеть значительное увеличение скорости при использовании комбинации lex + yacc.Если вы используете python lex + yacc , вы не увидите значительного увеличения скорости, но вам не нужно разбираться с кодом C.

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

Опять же, профилирование покажет, что на самом деле вызывает узкое место.Сначала выясните это, а затем посмотрите, помогут ли эти варианты решить проблему.

1 голос
/ 07 января 2012

Глядя на информацию о профилировании, вы проводите много времени в regexSub. Вы можете обнаружить, что можете объединить свои регулярные выражения в одну и сделать одну замену.

Что-то вроде:

regAll = re.compile(r'RT|(^[ \t]+)|((http://)[a-zA-Z0-9]*.[a-zA-Z0-9/]*(.[a-zA-Z0-9]*)?)|...')

(Цель этого состоит в том, чтобы заменить не только все, что вы делаете с помощью re.sub, но и полосу). Я закончил шаблон с ...: вам придется заполнить детали самостоятельно.

Затем замените regexSub просто:

line = regAll.sub(line)

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...