2017-01-06 2 views
0

Имейте список из более чем 15 MM-записей в Pandas Data Frame и пытаюсь определить число уникальных действительных английских слов, содержащихся в этом поле.Python Pandas Column (магазины) Сравнить с номером словаря и номером возврата

Как я могу ускорить это? Сравнение. Я использую set.intersect (..), но он занимает больше часа. Код и примеры данных ниже.

df.info() 
sys_id  float64 
grp_id  float64 
set_id  float64 
desc  object 
unique_set object 

Они уникальным ключом для этих записей являются поля «* id». Desc является Определено пользователем Описание

Я создаю unique_set используя код ниже:

df['unique_set'] = df.data_desc.apply(lambda x: detSet(x)) 

Где detSet определяется:

def detSet(strDesc): 
    if len(str(strDesc)) <= 1: 
     # Return an empty set 
     return set() 
    else: 
     # Remove all punctation 
     strDesc = strDesc.translate(replace_punctuation).lower() 
     # Remove all Non Alphabetic Characters (including Numbers) 
     strDesc = re.sub(r'[^a-zA-Z ]', ' ', strDesc) 
     # Remove all words less than 4 characters long 
     strDesc = re.sub(r'\b\w{1,3}\b','', strDesc) 
     # Remove all the extra spaces 
     strDesc = ' '.join(strDesc.split()) 
     # 
     return set(strDesc.split()) 

Затем я прочитал копию словаря английского языка в от http://invpy.com/dictionary.txt

ENGLISH_WORDS = open('Dictionary.txt').read().splitlines() 
ENGLISH_WORDS = [e.lower() for e in ENGLISH_WORDS] 

df['num_english'] = df.unique_set.apply(lambda x: detNumEnglish(x)).astype(np.int16) 

def detNumEnglish(setDesc): 
    if len(setDesc) == 0: 
     return -1 
    else: 
     return len(setDesc.intersection(ENGLISH_WORDS)) 

Некоторые данные образца:

141 9437 13522 {jelly, beans, pudding, cake, fruitc} 
787 29575 5915 {ingerbread, sugar, plum, powder, jelly} 
842 22909 28065 {pudding, bear, claw, sesame, snaps, m} 
484 36065 25069 {isu, cake, candy, canes, ca} 
897 54587 48574 {tart, fruitcake, dessert, bisc} 
123 48335 36038 {chocolate, icing, marzipan, macaroon, apple} 
293 36779 12239 {ars, sugar, plum, cupcake, danish, tiramis} 
115 18478 43114 {e, pudding, gummies, chocola} 
183 13346 33084 {roll, caramels, candy, fruitcak} 
501 94397 47227 {cake, candy, canes, cake} 
473 52269 44396 {e, gummi, bears, tiramisu, cake, candy} 
+0

Я начинаю задаваться вопросом, не будет ли это лучше для обзора кода ... –

ответ

0

Во-первых, вы должны создать ENGLISH_WORDS как set ускорить пересечение и удалить короткие слова, поскольку они отфильтрованы из вашего другого списка:

ENGLISH_WORDS = {e.lower() for e in ENGLISH_WORDS if len(e)>3} 

Тогда, кажется, что метод detSet называется много, и он делает много манипуляций с строками, что дорого.

Давайте исходный фрагмент кода:

def detSet(strDesc): 
    ... 
    else: 
     # Remove all punctation 
     strDesc = strDesc.translate(replace_punctuation).lower() 
     # Remove all Non Alphabetic Characters (including Numbers) 
     strDesc = re.sub(r'[^a-zA-Z ]', ' ', strDesc) 

(Это был ваш оригинальный фрагмент кода)

Обе линии выше в основном делает то же самое. Вы бы просто добавить перевод цифр (и, может быть, другие персонажи, которые будут уточнены), выполнив:

replace_punctuation.update({i:' ' for i in range(ord('0'),ord('9'))}) 

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

Как только вы это сделали, у вас уже есть строка слов, разделенных пробелами. Вы должны разделить в этот момент и отфильтровать небольшие слова. И 3 следующие строки:

# Remove all words less than 4 characters long 
    strDesc = re.sub(r'\b\w{1,3}\b','', strDesc) 
    # Remove all the extra spaces 
    strDesc = ' '.join(strDesc.split()) 
    # 
    return set(strDesc.split()) 

могут быть записаны непосредственно в качестве набора понимания (1 или много пространства: не имеет значения, если раскол вызываются без каких-либо аргументов), чтобы не создавать списки временных (создать многие из них в вашем коде)

return {s for s in strDesc.split() if len(s) > 4} 

Это должны быть намного быстрее, так как есть много меньше строки и манипуляция регулярного выражения.

Так, чтобы подвести итог, вот как ускорилось процедура будет выглядеть так:

def detSet(strDesc): 
    if len(str(strDesc)) <= 1: 
     # Return an empty set 
     return set() 
    else: 
     # Remove all punctation & replace digits by spaces (using the new replace_punctuation table) 
     strDesc = strDesc.translate(replace_punctuation).lower() 
     # split, filter out small words, and create set comprehension directly 
     return {s for s in strDesc.split() if len(s) > 4} 

Помимо lambda является излишеством здесь:

df.data_desc.apply(lambda x: detSet(x)) 

может быть просто (немного быстрее, тоже):

df.data_desc.apply(detSet) 

(то же самое касается df.unique_set.apply(lambda x: detNumEnglish(x)))

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