Анализ N-грамм на основе оттиска в Python


Вот как выглядит мой пример набора данных:

Введите описание изображения здесь

Моя цель-понять, сколько впечатлений связано с одним словом, двумя словами, тремя словами, четырьмя словами, пятью словами и шестью словами. Я использовал алгоритм N-грамма, но он возвращает только количество. Это мой текущий N-граммовый код.
def find_ngrams(text, n):
    word_vectorizer = CountVectorizer(ngram_range=(n,n), analyzer='word')
    sparse_matrix = word_vectorizer.fit_transform(text)
    frequencies = sum(sparse_matrix).toarray()[0]
    ngram = 

pd.DataFrame(frequencies,index=word_vectorizer.get_feature_names(),columns=
['frequency'])
ngram = ngram.sort_values(by=['frequency'], ascending=[False])
return ngram

one = find_ngrams(df['query'],1)
bi = find_ngrams(df['query'],2)
tri = find_ngrams(df['query'],3)
quad = find_ngrams(df['query'],4)
pent = find_ngrams(df['query'],5)
hexx = find_ngrams(df['query'],6)
Я понял, что мне нужно сделать: 1. разделите запросы на одно слово до шести слов. 2. прикрепите впечатление к разделенным словам. 3. перегруппируйте все разделенные слова и сумма впечатлений. Возьмем в качестве примера второй запрос "распространенные заболевания собак и как их лечить". Его следует разделить следующим образом:
(1) 1-gram: dog, common, diseases, and, how, to, treat, them;
(2) 2-gram: dog common, common diseases, diseases and, and how, how to, to treat, treat them;
(3) 3-gram: dog common diseases, common diseases and, diseases and how, and how to, how to treat, to treat them;
(4) 4-gram: dog common diseases and, common diseases and how, diseases and how to, and how to treat, how to treat them;
(5) 5-gram: dog common diseases and how, the queries into one word, diseases and how to treat, and how to treat them;
(6) 6-gram: dog common diseases and how to, common diseases and how to treat, diseases and how to treat them;
2 2

2 ответа:

Вот метод! Не самый эффективный, но, давайте не будем оптимизировать преждевременно. Идея состоит в том, чтобы использовать apply, чтобы получить новый pd.DataFrame с новыми столбцами для всех ngrams, соединить его со старым фреймом данных и выполнить некоторую укладку и группировку.

import pandas as pd

df = pd.DataFrame({
    "squery": ["how to feed a dog", "dog habits", "to cat or not to cat", "dog owners"],
    "count": [1000, 200, 100, 150]
})

def n_grams(txt):
    grams = list()
    words = txt.split(' ')
    for i in range(len(words)):
        for k in range(1, len(words) - i + 1):
            grams.append(" ".join(words[i:i+k]))
    return pd.Series(grams)

counts = df.squery.apply(n_grams).join(df)

counts.drop("squery", axis=1).set_index("count").unstack()\
    .rename("ngram").dropna().reset_index()\
    .drop("level_0", axis=1).groupby("ngram")["count"].sum()

Это последнее выражение вернет pd.Series, как показано ниже.

    ngram
a                       1000
a dog                   1000
cat                      200
cat or                   100
cat or not               100
cat or not to            100
cat or not to cat        100
dog                     1350
dog habits               200
dog owners               150
feed                    1000
feed a                  1000
feed a dog              1000
habits                   200
how                     1000
how to                  1000
how to feed             1000
how to feed a           1000
how to feed a dog       1000
not                      100
not to                   100
not to cat               100
or                       100
or not                   100
or not to                100
or not to cat            100
owners                   150
to                      1200
to cat                   200
to cat or                100
to cat or not            100
to cat or not to         100
to cat or not to cat     100
to feed                 1000
to feed a               1000
to feed a dog           1000

Spiffy method

Этот, вероятно, немного более эффективен, но он все же материализует плотный N-граммовый вектор из CountVectorizer. Он умножает это число по каждому столбцу с количеством показов, а затем добавляет по столбцам, чтобы получить общее количество показов на ngram. Это дает тот же результат, что и выше. Следует отметить, что запрос, имеющий повторяющуюся ngram, также считается двойным.

import numpy as np
from sklearn.feature_extraction.text import CountVectorizer

cv = CountVectorizer(ngram_range=(1, 5))
ngrams = cv.fit_transform(df.squery)
mask = np.repeat(df['count'].values.reshape(-1, 1), repeats = len(cv.vocabulary_), axis = 1)
index = list(map(lambda x: x[0], sorted(cv.vocabulary_.items(), key = lambda x: x[1])))
pd.Series(np.multiply(mask, ngrams.toarray()).sum(axis = 0), name = "counts", index = index)

Как насчет чего-то вроде этого:

def find_ngrams(input, n):
    # from http://locallyoptimal.com/blog/2013/01/20/elegant-n-gram-generation-in-python/
    return zip(*[input[i:] for i in range(n)])

def impressions_by_ngrams(data, ngram_max):
    from collections import defaultdict
    result = [defaultdict(int) for n in range(ngram_max)]
    for query, impressions in data:
        words = query.split()
        for n in range(ngram_max):
           for ngram in find_ngrams(words, n + 1):
                result[n][ngram] += impressions
    return result

Пример:

>>> data = [('how to feed a dog', 10000),
...         ('see a dog run',     20000)]
>>> ngrams = impressions_by_ngrams(data, 3)
>>> ngrams[0]   # unigrams
defaultdict(<type 'int'>, {('a',): 30000, ('how',): 10000, ('run',): 20000, ('feed',): 10000, ('to',): 10000, ('see',): 20000, ('dog',): 30000})
>>> ngrams[1][('a', 'dog')]  # impressions for bigram 'a dog'
30000