In [1]:
import re
import pandas as pd
/usr/lib/python3.6/importlib/_bootstrap.py:219: RuntimeWarning: numpy.dtype size changed, may indicate binary incompatibility. Expected 96, got 88
  return f(*args, **kwds)

sublime text

https://docs.python.org/3.5/library/re.html

  • re.match()
  • re.search()
  • re.findall()
  • re.split()
  • re.sub()
  • re.compile()

Пример использования регулярных выражений

In [2]:
text = """Sed ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur? At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat, facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat."""
In [3]:
re.split(r'\W+', text)
Out[3]:
['Sed',
 'ut',
 'perspiciatis',
 'unde',
 'omnis',
 'iste',
 'natus',
 'error',
 'sit',
 'voluptatem',
 'accusantium',
 'doloremque',
 'laudantium',
 'totam',
 'rem',
 'aperiam',
 'eaque',
 'ipsa',
 'quae',
 'ab',
 'illo',
 'inventore',
 'veritatis',
 'et',
 'quasi',
 'architecto',
 'beatae',
 'vitae',
 'dicta',
 'sunt',
 'explicabo',
 'Nemo',
 'enim',
 'ipsam',
 'voluptatem',
 'quia',
 'voluptas',
 'sit',
 'aspernatur',
 'aut',
 'odit',
 'aut',
 'fugit',
 'sed',
 'quia',
 'consequuntur',
 'magni',
 'dolores',
 'eos',
 'qui',
 'ratione',
 'voluptatem',
 'sequi',
 'nesciunt',
 'neque',
 'porro',
 'quisquam',
 'est',
 'qui',
 'dolorem',
 'ipsum',
 'quia',
 'dolor',
 'sit',
 'amet',
 'consectetur',
 'adipisci',
 'velit',
 'sed',
 'quia',
 'non',
 'numquam',
 'eius',
 'modi',
 'tempora',
 'incidunt',
 'ut',
 'labore',
 'et',
 'dolore',
 'magnam',
 'aliquam',
 'quaerat',
 'voluptatem',
 'Ut',
 'enim',
 'ad',
 'minima',
 'veniam',
 'quis',
 'nostrum',
 'exercitationem',
 'ullam',
 'corporis',
 'suscipit',
 'laboriosam',
 'nisi',
 'ut',
 'aliquid',
 'ex',
 'ea',
 'commodi',
 'consequatur',
 'Quis',
 'autem',
 'vel',
 'eum',
 'iure',
 'reprehenderit',
 'qui',
 'in',
 'ea',
 'voluptate',
 'velit',
 'esse',
 'quam',
 'nihil',
 'molestiae',
 'consequatur',
 'vel',
 'illum',
 'qui',
 'dolorem',
 'eum',
 'fugiat',
 'quo',
 'voluptas',
 'nulla',
 'pariatur',
 'At',
 'vero',
 'eos',
 'et',
 'accusamus',
 'et',
 'iusto',
 'odio',
 'dignissimos',
 'ducimus',
 'qui',
 'blanditiis',
 'praesentium',
 'voluptatum',
 'deleniti',
 'atque',
 'corrupti',
 'quos',
 'dolores',
 'et',
 'quas',
 'molestias',
 'excepturi',
 'sint',
 'obcaecati',
 'cupiditate',
 'non',
 'provident',
 'similique',
 'sunt',
 'in',
 'culpa',
 'qui',
 'officia',
 'deserunt',
 'mollitia',
 'animi',
 'id',
 'est',
 'laborum',
 'et',
 'dolorum',
 'fuga',
 'Et',
 'harum',
 'quidem',
 'rerum',
 'facilis',
 'est',
 'et',
 'expedita',
 'distinctio',
 'Nam',
 'libero',
 'tempore',
 'cum',
 'soluta',
 'nobis',
 'est',
 'eligendi',
 'optio',
 'cumque',
 'nihil',
 'impedit',
 'quo',
 'minus',
 'id',
 'quod',
 'maxime',
 'placeat',
 'facere',
 'possimus',
 'omnis',
 'voluptas',
 'assumenda',
 'est',
 'omnis',
 'dolor',
 'repellendus',
 'Temporibus',
 'autem',
 'quibusdam',
 'et',
 'aut',
 'officiis',
 'debitis',
 'aut',
 'rerum',
 'necessitatibus',
 'saepe',
 'eveniet',
 'ut',
 'et',
 'voluptates',
 'repudiandae',
 'sint',
 'et',
 'molestiae',
 'non',
 'recusandae',
 'Itaque',
 'earum',
 'rerum',
 'hic',
 'tenetur',
 'a',
 'sapiente',
 'delectus',
 'ut',
 'aut',
 'reiciendis',
 'voluptatibus',
 'maiores',
 'alias',
 'consequatur',
 'aut',
 'perferendis',
 'doloribus',
 'asperiores',
 'repellat',
 '']
In [4]:
result = re.search('[abcdefg]+', text)
In [5]:
result.group(0)
Out[5]:
'ed'
  • id: уникальный номер сообщения в системе twitter;
  • tdate: дата публикации сообщения (твита);
  • tmane: имя пользователя, опубликовавшего сообщение;
  • ttext: текст сообщения (твита);
  • ttype: поле в котором в дальнейшем будет указано к кому классу относится твит (положительный, отрицательный, нейтральный);
  • trep: количество реплаев к данному сообщению. В настоящий момент API твиттера не отдает эту информацию;
  • tfav: число сколько раз данное сообщение было добавлено в избранное другими пользователями;
  • tstcount: число всех сообщений пользователя в сети twitter;
  • tfol: количество фоловеров пользователя (тех людей, которые читают пользователя);
  • tfrien: количество друзей пользователя (те люди, которых читает пользователь);
  • listcount: количество листов-подписок в которые добавлен твиттер-пользователь.

Примеры работы с текстом в Pandas

In [6]:
%matplotlib inline
In [7]:
positive = pd.read_csv('positive.csv', sep=";", header=None)

negative = pd.read_csv('negative.csv', sep=";", header=None)
In [8]:
positive.shape, negative.shape
Out[8]:
((114911, 12), (111923, 12))
In [9]:
text_data = pd.concat([positive[[3,4]], negative[[3,4]]], ignore_index=True)
In [10]:
# pd.merge(df1, df2, how='left', left_on='value_type', right_index=True)
In [11]:
text_data.columns = ['tweet', 'mood']
In [12]:
text_data.tweet = text_data.tweet.str.lower()
In [13]:
%matplotlib inline
In [14]:
text_data.tweet.str.len()\
        .value_counts()\
        .sort_index()
Out[14]:
8         1
21        1
41     2402
42     2438
43     2377
44     2356
45     2328
46     2299
47     2332
48     2131
49     2255
50     2073
51     4847
52     4633
53     4628
54     4364
55     4490
56     4331
57     4221
58     4133
59     4077
60     3975
61     3896
62     3813
63     3793
64     3655
65     3584
66     3424
67     3447
68     3361
       ... 
125    1099
126    1094
127    1028
128     950
129     995
130     970
131    1035
132    1088
133    1028
134    1069
135    1237
136    1299
137    1349
138    1745
139    2025
140    6627
141      19
142      16
143      36
144      16
145       3
146      12
147       1
148       2
149       4
156       1
160      70
166       1
179       1
189       1
Name: tweet, Length: 116, dtype: int64
In [15]:
text_data = text_data[text_data.tweet.str.len().between(41,140)]
In [16]:
text_data.tweet.str.len()\
        .value_counts()\
        .sort_index()
Out[16]:
41     2402
42     2438
43     2377
44     2356
45     2328
46     2299
47     2332
48     2131
49     2255
50     2073
51     4847
52     4633
53     4628
54     4364
55     4490
56     4331
57     4221
58     4133
59     4077
60     3975
61     3896
62     3813
63     3793
64     3655
65     3584
66     3424
67     3447
68     3361
69     3261
70     3131
       ... 
111    1287
112    1272
113    1276
114    1305
115    1307
116    1170
117    1148
118    1138
119    1168
120    1274
121    1919
122    1056
123    1074
124    1036
125    1099
126    1094
127    1028
128     950
129     995
130     970
131    1035
132    1088
133    1028
134    1069
135    1237
136    1299
137    1349
138    1745
139    2025
140    6627
Name: tweet, Length: 100, dtype: int64
In [17]:
text_data.tweet.str.len().hist(bins=30)
Out[17]:
<matplotlib.axes._subplots.AxesSubplot at 0x7ff2390386d8>
In [18]:
text_data.tweet.str.findall('\w+').apply(len).value_counts().sort_index()
Out[18]:
1        14
2        52
3       171
4       790
5      3185
6      7890
7     14714
8     19989
9     21820
10    21045
11    18962
12    17125
13    14840
14    12842
15    11087
16    10142
17     9160
18     8421
19     7487
20     6759
21     5656
22     4511
23     3647
24     2697
25     1753
26      986
27      504
28      233
29      109
30       37
31       13
32        5
33        2
36        1
Name: tweet, dtype: int64
In [19]:
text_data.tweet.str[3:5]
Out[19]:
0         rs
1          в
2         @k
3         @d
4         in
5         лю
6         @s
7         @v
8         ри
9         ер
10        лю
11        @d
12        sr
13        ug
14        ex
15        li
16        @a
17        ri
18        es
19        sc
20        ас
21        nj
22        al
23        @l
24        ри
25         ж
26        ь 
27        по
28        н,
29        li
          ..
226804    ul
226805    да
226806    сщ
226807    то
226808    о 
226809    да
226810    ri
226811    ем
226812     д
226813    ri
226814    у 
226815     я
226816    p:
226817    ра
226818    , 
226819    оч
226820     м
226821    di
226822    @r
226823    gi
226824    an
226825    да
226826    @q
226827    ас
226828     с
226829    не
226830    ча
226831     и
226832    @_
226833    си
Name: tweet, Length: 226649, dtype: object
In [20]:
positive[3].str.findall('\w+').apply(len).hist(bins=36)
Out[20]:
<matplotlib.axes._subplots.AxesSubplot at 0x7ff227a9da20>
In [21]:
positive.describe()
Out[21]:
0 1 4 5 6 7 8 9 10 11
count 1.149110e+05 1.149110e+05 114911.0 114911.0 114911.000000 114911.000000 1.149110e+05 1.149110e+05 114911.000000 114911.000000
mean 4.101934e+17 1.386633e+09 1.0 0.0 4.166494 0.001157 7.613871e+03 7.856044e+02 382.963702 11.170854
std 6.789925e+14 1.618844e+05 0.0 0.0 145.232798 0.034256 1.875076e+04 1.079046e+04 1733.551910 110.289318
min 4.089067e+17 1.386326e+09 1.0 0.0 0.000000 0.000000 0.000000e+00 0.000000e+00 0.000000 0.000000
25% 4.096183e+17 1.386496e+09 1.0 0.0 0.000000 0.000000 4.930000e+02 3.200000e+01 32.000000 0.000000
50% 4.101237e+17 1.386616e+09 1.0 0.0 0.000000 0.000000 2.140000e+03 9.700000e+01 83.000000 0.000000
75% 4.108024e+17 1.386778e+09 1.0 0.0 0.000000 0.000000 7.778500e+03 2.820000e+02 212.000000 2.000000
max 4.113689e+17 1.386913e+09 1.0 0.0 13817.000000 2.000000 1.130418e+06 1.582807e+06 175824.000000 16915.000000

Представления текста для машинного обучения в Python

TFIDF

CountVectorizer (Term Frequency)

In [22]:
from time import time
from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer
from sklearn.decomposition import NMF, LatentDirichletAllocation
from sklearn.datasets import fetch_20newsgroups

n_samples = 2000
n_features = 1000
n_components = 10
n_top_words = 20
In [23]:
def print_top_words(model, feature_names, n_top_words):
    for topic_idx, topic in enumerate(model.components_):
        message = "Topic #%d: " % topic_idx
        message += " ".join([feature_names[i]
                             for i in topic.argsort()[:-n_top_words - 1:-1]])
        print(message)
    print()
In [24]:
print("Loading dataset...")
t0 = time()
dataset = fetch_20newsgroups(shuffle=True, random_state=1,
                             remove=('headers', 'footers', 'quotes'))
data_samples = dataset.data[:n_samples]
print("done in %0.3fs." % (time() - t0))
Loading dataset...
done in 1.062s.
In [25]:
# Use tf-idf features for NMF.
print("Extracting tf-idf features for NMF...")
tfidf_vectorizer = TfidfVectorizer(max_df=0.95, min_df=2,
                                   max_features=n_features,
                                   stop_words='english')
t0 = time()
tfidf = tfidf_vectorizer.fit_transform(data_samples)
print("done in %0.3fs." % (time() - t0))

# Use tf (raw term count) features for LDA.
print("Extracting tf features for LDA...")
tf_vectorizer = CountVectorizer(max_df=0.95, min_df=2,
                                max_features=n_features,
                                stop_words='english')
t0 = time()
tf = tf_vectorizer.fit_transform(data_samples)
print("done in %0.3fs." % (time() - t0))
print()
Extracting tf-idf features for NMF...
done in 0.266s.
Extracting tf features for LDA...
done in 0.250s.

Тематическое моделирование

  • LSA
  • PLSA
  • LDA

Лемматизация

mystem

https://tech.yandex.ru/mystem/

In [26]:
import pymystem3
from functools import lru_cache
mystem = pymystem3.Mystem()
In [27]:
import pymorphy2

morph = pymorphy2.MorphAnalyzer()

morph.parse('Собаками')
Out[27]:
[Parse(word='собаками', tag=OpencorporaTag('NOUN,anim,femn plur,ablt'), normal_form='собака', score=1.0, methods_stack=((<DictionaryAnalyzer>, 'собаками', 403, 11),))]
In [28]:
morph.parse('стали')
Out[28]:
[Parse(word='стали', tag=OpencorporaTag('VERB,perf,intr plur,past,indc'), normal_form='стать', score=0.984662, methods_stack=((<DictionaryAnalyzer>, 'стали', 904, 4),)),
 Parse(word='стали', tag=OpencorporaTag('NOUN,inan,femn sing,gent'), normal_form='сталь', score=0.003067, methods_stack=((<DictionaryAnalyzer>, 'стали', 13, 1),)),
 Parse(word='стали', tag=OpencorporaTag('NOUN,inan,femn sing,datv'), normal_form='сталь', score=0.003067, methods_stack=((<DictionaryAnalyzer>, 'стали', 13, 2),)),
 Parse(word='стали', tag=OpencorporaTag('NOUN,inan,femn sing,loct'), normal_form='сталь', score=0.003067, methods_stack=((<DictionaryAnalyzer>, 'стали', 13, 5),)),
 Parse(word='стали', tag=OpencorporaTag('NOUN,inan,femn plur,nomn'), normal_form='сталь', score=0.003067, methods_stack=((<DictionaryAnalyzer>, 'стали', 13, 6),)),
 Parse(word='стали', tag=OpencorporaTag('NOUN,inan,femn plur,accs'), normal_form='сталь', score=0.003067, methods_stack=((<DictionaryAnalyzer>, 'стали', 13, 9),))]
In [29]:
@lru_cache(maxsize=None)
def lemma_one_word(x):
    try:
        return mystem.lemmatize(x)[0]
    except:
        print(x)
        return " "

def lemma_one_sentence(x):
    return " ".join([lemma_one_word(y) for y in x.split(' ')])


def make_lemma(text, mystem):
    lemmas = text.str.findall('(\w+)').apply(lambda x: ' '.join(x)).apply(lemma_one_sentence)
    lemmas = lemmas.reset_index(drop=True)
    return lemmas
In [30]:
from functools import lru_cache
In [31]:
@lru_cache(maxsize=None)
def lemmatize_mystem(w):
    return mystem.lemmatize(w)[0]
In [32]:
@lru_cache(maxsize=None)
def lemmatize_pymorphy(w):
    try:
        return morph.parse(w)[0].normal_form
    except:
        return ' '
In [33]:
lemmatize_mystem('стали'), lemmatize_pymorphy('стали')
Out[33]:
('становиться', 'стать')
In [34]:
sample = text_data.tweet.str.findall('\w+').head(50000)
In [35]:
%%time
text_data['mystem_lemma'] = text_data.tweet.str.findall('\w+')\
            .apply(lambda x: [lemmatize_mystem(y) for y in x])
CPU times: user 9.15 s, sys: 1.83 s, total: 11 s
Wall time: 17.8 s
In [36]:
%%time
text_data['pymorphy_lemma'] = text_data.tweet.str.findall('\w+')\
            .apply(lambda x: [lemmatize_pymorphy(y) for y in x])
CPU times: user 44.5 s, sys: 169 ms, total: 44.6 s
Wall time: 44.7 s
In [37]:
text_data = text_data.sample(10000)
In [38]:
text_data.mystem_lemma = text_data.mystem_lemma.apply(lambda x: ' '.join(x))
In [39]:
text_data.mystem_lemma = text_data.mystem_lemma.str.replace('\n', '')
In [40]:
text_data.pymorphy_lemma = text_data.pymorphy_lemma.apply(lambda x: ' '.join(x))
In [41]:
tf = CountVectorizer()
tfidf = TfidfVectorizer()
In [42]:
mystem_X = tf.fit_transform(text_data.mystem_lemma)
In [43]:
pymorphy_X = tf.fit_transform(text_data.pymorphy_lemma)
In [44]:
lda = LatentDirichletAllocation(n_components=10, n_jobs=-1, learning_method='batch', batch_size=256)

Обучение LDA модели на mystem-данных

In [45]:
mystem_X_lda = lda.fit_transform(mystem_X)
In [46]:
from sklearn.neural_network import MLPClassifier
In [47]:
model = MLPClassifier(hidden_layer_sizes=(20,5), solver='adam')
In [48]:
from sklearn.model_selection import train_test_split
In [49]:
X_train, X_test, y_train, y_test = train_test_split(mystem_X_lda, text_data.mood, test_size=0.3)
In [50]:
model.fit(X_train, y_train)
Out[50]:
MLPClassifier(activation='relu', alpha=0.0001, batch_size='auto', beta_1=0.9,
       beta_2=0.999, early_stopping=False, epsilon=1e-08,
       hidden_layer_sizes=(20, 5), learning_rate='constant',
       learning_rate_init=0.001, max_iter=200, momentum=0.9,
       nesterovs_momentum=True, power_t=0.5, random_state=None,
       shuffle=True, solver='adam', tol=0.0001, validation_fraction=0.1,
       verbose=False, warm_start=False)
In [51]:
pd.DataFrame(model.predict_proba(X_train))[0].hist(bins=30)
Out[51]:
<matplotlib.axes._subplots.AxesSubplot at 0x7ff1f2748438>
In [52]:
pd.DataFrame(model.predict_proba(X_test))[0].hist(bins=30)
Out[52]:
<matplotlib.axes._subplots.AxesSubplot at 0x7ff1f24094a8>