LINUX.ORG.RU

Посчитать количество вхождений слов и занести в БД

 ,


0

3

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

#!/usr/bin/python

import pymysql, os, sys, atexit, re

#MariaDB [words]> describe words;
#+-------+------------------+------+-----+---------+----------------+
#| Field | Type             | Null | Key | Default | Extra          |
#+-------+------------------+------+-----+---------+----------------+
#| id    | int(11) unsigned | NO   | PRI | NULL    | auto_increment |
#| word  | char(64)         | NO   | UNI | NULL    |                |
#| count | int(11)          | NO   |     | 0       |                |
#+-------+------------------+------+-----+---------+----------------+

sql = """INSERT INTO `words` (`word`, `count`)
        VALUES (%s, %s) ON DUPLICATE KEY 
        UPDATE `count` = `count`+VALUES(`count`) """

child_pid = 0

def pushbuf():
    print('words:', len(wbuf))
    #print('size:', sys.getsizeof(wbuf))
    global child_pid
    if child_pid != 0: os.wait()
    child_pid = os.fork()
    if child_pid != 0: 
        wbuf.clear()
        return
    with db.cursor() as cursor:
        try:
            db.begin()
            cursor.executemany(sql, wbuf.items())
            cursor.close()
            db.commit()
        except:
            db.rollback()
            raise(print('wtf1?!'))
    sys.exit(0)

def end():
    if child_pid != 0:
        f.close()
        os.wait()
        db.close()

atexit.register(end)

db = pymysql.connect("localhost","root","paSSword","words", charset="utf8")
with db.cursor() as cursor:
    try:
        db.autocommit(False)
        cursor.execute("TRUNCATE `words`")
        cursor.close()
        db.commit()
    except:
        raise(print('wtf0?!'))


wbuf={}
wbuflen=10000
f=open("warpeace.txt","r")

for line in f:
    for word in re.findall(r'[^\W\d]+', line):
        newword=word.lower()
        #wbuf[newword] = wbuf.get(newword,0)+1
        if newword in wbuf:
            wbuf[newword] += 1
        else:
            wbuf[newword] = 1
            if len(wbuf) >= wbuflen: pushbuf()
pushbuf()
Посоветуйте, в какую сторону копать для оптимального решения?

И насколько оправдано использование executemany(), особенно с учетом того, что предварительно словарь конвертируется в список? Может executemany вообще не дает преимуществ, кроме краткости, правильнее делать цикл с execute()?

#!/usr/bin/python

import os, sys, re

wbuf={}
wbuflen=10000
f=open("warpeace.txt","r")

for line in f:
    for word in re.findall(r'[^\W\d]+', line):
        newword=word.lower()
        #wbuf[newword] = wbuf.get(newword,0)+1
        if newword in wbuf:
            wbuf[newword] += 1
        else:
            wbuf[newword] = 1
            if len(wbuf) >= wbuflen: pass
$ time ./warpeace.py 

real    0m0,780s
user    0m0,772s
sys     0m0,008s
time cat warpeace.txt | xargs -n1 | sort | uniq -c
real    0m0,006s
user    0m0,006s
sys     0m0,003s

Ну ты понял :)

fluorite ★★★★★ ()
Последнее исправление: fluorite (всего исправлений: 1)
Ответ на: комментарий от fluorite
from functools import reduce

def append(a, c):
    a[c] = a.get(c, 0) + 1
    return a

result = {}
with open('text.txt', 'r') as f:
    for line in f:
        reduce (append, line.split(), result)
print(len(result))
$ time python text.py 
449

real    0m0,098s
user    0m0,060s
sys     0m0,041s
$ time cat text.txt | xargs -n1 | sort | uniq -c | wc -l
449

real    0m4,222s
user    0m1,170s
sys     0m2,551s
vvn_black ★★★★★ ()

Вангую что ты хочешь set а не dict.

NeverLoved ★★★★★ ()
Ответ на: комментарий от fluorite

Ну ты понял :)

Оно справится, если файл и количество слов не влазят в память?

wbuflen=10000 просто для тестов, по задумке должно согласовываться с оценкой потребления.

docpro ()

На выборке больших данных pymysql (pure python) в десятки раз сливает mysqlclient. Как на вставке, не знаю, не проверял.

vvn_black ★★★★★ ()
Ответ на: комментарий от vvn_black

На выборке больших данных pymysql (pure python) в десятки раз сливает mysqlclient. Как на вставке, не знаю, не проверял.

Я хотел, что бы работало и с pypy и cython, а c mysqlclient вроде бы не совместимо. Получается, что-то придётся вычеркнуть.

docpro ()
Ответ на: комментарий от fluorite

time cat warpeace.txt | xargs -n1 | sort | uniq -c

Кстати, вообще не дождался результата на тестовом файле, менее 2МБ. Может быть ошибка в приведенном скрипте? И он точно ли обрабатывает все сепораторы, среди которых слова?

docpro ()
Ответ на: комментарий от docpro

Это типо шутка была. Похоже, надо объяснять: 1. на баше решение можно накатать за две секунды (это типа решение «быстрее» в плане затрат времени пишущего); 2. наколеночная поделка быстрее твоего питоновского скрипта (т.е. ну вот хотя бы для начала тормозной баш победи); 3. для такой задачи давно есть типовые решения и алгоритмы, их просто надо объединить.

fluorite ★★★★★ ()
Ответ на: комментарий от fluorite

1. на баше решение можно накатать за две секунды (это типа решение «быстрее» в плане затрат времени пишущего);

Хотелось бы, чтобы оно работало.

2. наколеночная поделка быстрее твоего питоновского скрипта (т.е. ну вот хотя бы для начала тормозной баш победи);

Поделка не удовлетворяет условиям задачи.

3. для такой задачи давно есть типовые решения и алгоритмы, их просто надо объединить.

А где есть? Я понимаю, что задача типовая, но где реально?

docpro ()
Ответ на: комментарий от docpro

А где есть? Я понимаю, что задача типовая, но где реально?

Биоинформатики используют такие алгоритмы, всё уже давно настроено и работает как часы.

ZERG ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.