Habraiting: construindo uma nuvem de palavras que falam russo no exemplo dos cabeçalhos Habra

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)))] # Remove some unicode symbols def unicode2str(s): try: return s.replace(u'\u2014', u'-').replace(u'\u2013', u'-').replace(u'\u2026', u'...').replace(u'\xab', u"'").replace(u'\xbb', u"'") except: return s titles = df["title"].map(unicode2str, na_action=None) 

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): # English word or digit n = normal_eng(w) c_dict[n] += 1 else: # Russian word n = normal_rus(w) if n is not None: c_dict[n] += 1 

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;)

Source: https://habr.com/ru/post/pt442626/


All Articles