Oi Habr.
Na última parte de Habraiting, foi publicado um método para construir uma nuvem de palavras para termos em inglês. Obviamente, a tarefa de analisar palavras russas é muito mais complicada, mas, como sugerido nos comentários, existem bibliotecas prontas para isso.
Vamos descobrir como criar essa imagem:

Também veremos uma nuvem de artigos de Habr por todos os anos.
Quem se importa com o que aconteceu, por favor, debaixo do gato.
Análise
O conjunto de dados inicial, como no caso anterior, é csv com os cabeçalhos dos artigos de Habr de 2006 a 2019. Se alguém estiver interessado em experimentar você mesmo, faça o download
aqui .
Para começar, carregue os dados no DataFrame do Pandas e selecione os cabeçalhos para o ano necessário.
df = pd.read_csv(log_path, sep=',', encoding='utf-8', error_bad_lines=True, quotechar='"', comment='#') if year != 0: dates = pd.to_datetime(df['datetime'], format='%Y-%m-%dT%H:%MZ') df['datetime'] = dates df = df[(df['datetime'] >= pd.Timestamp(datetime.date(year, 1, 1))) & ( df['datetime'] < pd.Timestamp(datetime.date(year + 1, 1, 1)))]
A função unicode2str é necessária para remover vários caracteres unicode complicados da saída do console, como cotações não padrão - no OSX isso também funcionou e, ao enviar para o Windows Powershell, foi emitido o erro “UnicodeEncodeError: o codec 'charmap' não pode codificar caracteres”. Era muito preguiçoso lidar com as configurações do PowerShell, então esse método acabou sendo o mais fácil.
O próximo passo é separar as palavras em russo de todas as outras. É bem simples - traduzimos os caracteres em codificação ascii e vemos o que resta. Se restarem mais de 2 caracteres, consideraremos a palavra "cheia" (a única exceção que vem à mente é o idioma Go, no entanto, quem desejar pode adicioná-lo por conta própria).
def to_ascii(s): try: s = s.replace("'", '').replace("-", '').replace("|", '') return s.decode('utf-8').encode("ascii", errors="ignore").decode() except: return '' def is_asciiword(s): ascii_word = to_ascii(s) return len(ascii_word) > 2
A próxima tarefa é normalizar a palavra - para obter uma nuvem de palavras, cada palavra deve ser exibida em um caso e declinação. Para o inglês, simplesmente removemos os "s" no final e também removemos outros caracteres ilegíveis, como colchetes. Não tenho certeza de que esse método seja cientificamente correto (e não sou um lingüista), mas para esta tarefa é suficiente.
def normal_eng(s): for sym in ("'s", '{', '}', "'", '"', '}', ';', '.', ',', '[', ']', '(', ')', '-', '/', '\\'): s = s.replace(sym, ' ') return s.lower().strip()
Agora, o mais importante, para o qual tudo foi realmente iniciado, é a análise das palavras em russo. Conforme recomendado nos comentários da parte anterior, para Python isso pode ser feito usando a biblioteca pymorphy2. Vamos ver como isso funciona.
import pymorphy2 morph = pymorphy2.MorphAnalyzer() res = morph.parse(u"") for r in res: print r.normal_form, r.tag.case
Neste exemplo, temos os seguintes resultados:
NOUN,inan,masc sing,datv datv NOUN,inan,masc sing,loc2 loc2 NOUN,inan,neut sing,datv datv NOUN,inan,masc sing,gen2 gen2
Para a palavra "mundo", MorphAnalyzer definiu "forma normal" como o substantivo "mundo" (ou "mundo", no entanto, não sei o que é), casos singulares e possíveis como dativ, genitivo ou locativo.
O uso da análise MorphAnalyzer é bastante simples - verifique se a palavra é um substantivo e obtenha sua forma normal.
morph = pymorphy2.MorphAnalyzer() def normal_rus(w): res = morph.parse(w) for r in res: if 'NOUN' in r.tag: return r.normal_form return None
Resta reunir tudo e ver o que aconteceu. O código se parece com isso (fragmentos irrelevantes removidos):
from collections import Counter c_dict = Counter() for s in titles.values: for w in s.split(): if is_asciiword(w):
Na saída, temos um dicionário de palavras e seu número de ocorrências. Derivamos os 100 primeiros e formamos deles uma nuvem de popularidade de palavras:
common = c_dict.most_common(100) wc = WordCloud(width=2600, height=2200, background_color="white", relative_scaling=1.0, collocations=False, min_font_size=10).generate_from_frequencies(dict(common)) plt.axis("off") plt.figure(figsize=(9, 6)) plt.imshow(wc, interpolation="bilinear") plt.title("%d" % year) plt.xticks([]) plt.yticks([]) plt.tight_layout() file_name = 'habr-words-%d.png' % year plt.show()
O resultado, no entanto, acabou sendo muito estranho:

Em forma de texto, ficou assim:
3958 3619 1828 840 2018 496 389 375 375
As palavras "performance", "segundo" e "século" foram lideradas por uma margem enorme. E embora, em princípio, seja possível (você pode imaginar um cabeçalho como “Pesquisando senhas a uma velocidade de 1000 vezes por segundo levará um século”), mas suspeitava-se que houvesse muitas dessas palavras. E não em vão - como a depuração mostrou, o MorphAnalyzer definiu a palavra "c" como "segundo" e a palavra "c" como "século". I.e. no cabeçalho “Using Technology ...”, o MorphAnalyzer destacou três palavras - “second”, “help”, “technology”, o que está obviamente incorreto. As seguintes palavras obscuras foram "quando" ("Ao usar ...") e "já", que foram definidas como substantivo "direto" e "já", respectivamente. A solução foi simples - ao analisar, considere apenas palavras com mais de 2 caracteres e insira uma lista de palavras de exceção no idioma russo que seriam excluídas da análise. Novamente, talvez isso não seja inteiramente científico (por exemplo, um artigo sobre "observar uma alteração de coloração por já" teria saído da análise), mas para essa tarefa já :) é suficiente.
O resultado final é mais ou menos semelhante à verdade (com exceção de Go e possíveis artigos sobre cobras). Resta salvar tudo isso em um gif (o código de geração de gif está na
parte anterior ) e obtemos um resultado animado na forma da popularidade das palavras-chave nos títulos da Habr de 2006 a 2019.

Conclusão
Como você pode ver, a análise do texto em russo com a ajuda de bibliotecas prontas acabou sendo bastante simples. É claro que, com algumas reservas, a linguagem falada é um sistema flexível, com muitas exceções e a presença de um senso de contexto dependendo do contexto, e provavelmente é impossível obter 100% de certeza aqui. Mas para a tarefa em questão, o código acima é suficiente.
O trabalho com textos cirílicos no próprio Python, a propósito, está longe de ser perfeito - pequenos problemas com a saída de caracteres no console, saída quebrada de matrizes por impressão, a necessidade de acrescentar "" u nas linhas do Python 2.7 etc. É ainda estranho que no século XXI, quando parece que todos os atavismos, como KOI8-R ou CP-1252, morreram, os problemas de codificação de string ainda permanecem relevantes.
Por fim, é interessante notar que a adição de palavras russas à nuvem de texto praticamente não aumentou o conteúdo informativo da imagem em comparação com a
versão em inglês - quase todos os termos de TI são de língua inglesa, portanto a lista de palavras em russo mudou muito menos significativamente em 10 anos. Provavelmente, para ver as alterações no idioma russo, é necessário aguardar 50-100 anos - após o tempo especificado, haverá uma ocasião para atualizar o artigo novamente;)