В этом исследовании более подробно рассматриваются исторические текстовые сообщения с применением различных методов машинного обучения. Целью исследования является применение методов обработки естественного языка (NLP) для выявления коммуникативных тенденций, оценки эффективности существующего процесса и получения информации из исторических данных.

Методология

Для анализа данных я использовал Python в качестве языка программирования вместе с многочисленными программными библиотеками, такими как pandas, math, numpy, sklearn, matplotlib и т. д. Кроме того, я использовал обработку естественного языка (NLP), которая представляет собой машинное обучение. (ML) методы, которые помогают понимать, интерпретировать и манипулировать человеческим языком с помощью искусственного интеллекта.

Исследование данных

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

Выявление тенденций входящих и исходящих сообщений весной и летом

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

df_text=pd.read_csv('../Data/clean_text.csv')
df_text.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 62513 entries, 0 to 62512
Data columns (total 7 columns):
message_type    62513 non-null object
text            62513 non-null object
time            62513 non-null object
ID              62513 non-null object
group_list      62513 non-null object
term            62513 non-null object
sent_by         62513 non-null object
dtypes: object(7)
memory usage: 3.3+ MB
#Extracting year and month from 'time' column and append the dataframe with new columns.
df_text['year'] = pd.DatetimeIndex(df_text['time']).year
df_text['month'] = pd.DatetimeIndex(df_text['time']).month
df_text.head()

Теперь мы можем подсчитать количество входящих и исходящих сообщений по месяцам.

# Getting the message count by message type and month
# reset_index() gives a column for counting, after groupby uses year and category
df_cnt = (df_text.reset_index()
          .groupby(['month','message_type'], as_index=False)
          .count()
          # rename isn't strictly necessary here, it's just for readability
          .rename(columns={'index':'count'})
       )
#sorting the values by month
df_cnt.sort_values(by = 'month', ascending = True)

Теперь пришло время использовать matplotlib для представления данных.

import matplotlib.pyplot as plt
df_all_cnt =df_cnt
#adding month name for better presentation
df_all_cnt['month_t'] = df_all_cnt['month'].apply(lambda x: calendar.month_abbr[x])
fig, ax = plt.subplots(figsize=(5, 3), dpi=150)
# key gives the group name (i.e. category), data gives the actual values
for key, data in df_all_cnt.groupby('message_type'):
    data.plot(x='month_t', y='count', ax=ax, label=key)
    
# Hide the right and top spines
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
#draw the grid
ax.grid( linestyle='-', linewidth=0.2)     
ax.legend()
ax.set_xlabel('Month')
ax.set_ylabel('Nu. Of Messages')
ax.set_title('In and Out Messages By Month - Both Programs')

Таким же образом мы можем использовать другие столбцы в наборе данных в качестве осей x и y для заполнения различных типов графиков, чтобы показать различные аспекты одного и того же набора данных.

Анализ текста с помощью НЛП

НЛП — отличный метод для анализа и интерпретации текстовых данных, таких как ответы учащихся. Это метод машинного обучения, который машины используют для понимания человеческого языка, такого как текст, речь, смайлики, и широко используемый в отрасли в наши дни (например, Siri и Alexa).

Очистка, удаление стоп-слов и стемминга

Важно очистить ваши данные, удалив все ненужные слова, символы и любой текст, который не поможет вам провести осмысленный анализ. Приведенные ниже функции помогут нам с очисткой, удалением стоп-слов, но нам также понадобится несколько библиотек, чтобы помочь с этим.

from nltk.corpus import stopwords
nltk.download('stopwords')
nltk.download('punkt')
from gensim.parsing.preprocessing import STOPWORDS
from gensim.parsing.preprocessing import remove_stopwords
set(stopwords.words('english'))
from nltk.tokenize import word_tokenize
from contractions import contractions_dict
import unicodedata
def remove_punctuation(text):
    text = ''.join([i for i in text if not i.isdigit()])
    return re.sub("[!@#$+%*:()/|,;:'-]", ' ', text)
def removebrackets(text):
    return re.sub('[\(\[].*?[\)\]]', ' ', text)
def remove_accented_chars(text):
    return unicodedata.normalize('NFKD', text).encode('ascii', 'ignore').decode('utf-8', 'ignore')
def remove_special_chars(text, remove_digits=False):
    pattern = r'[^a-zA-Z0-9\s]' if not remove_digits else r'[^a-zA-Z\s]'
    return re.sub(pattern, '', text)
def remove_stopwords(text):
   # text_tokens = word_tokenize(text)
   # text = remove_stopwords(text)
      
    from gensim.parsing.preprocessing import STOPWORDS
all_stopwords_gensim = STOPWORDS.union(set(['thank', 'need', 'yes', 'okay']))
text_tokens = word_tokenize(text)
    tokens_without_sw = ' '.join([word for word in text_tokens if not word in all_stopwords_gensim])
    
    return tokens_without_sw
def stemming (text):
    
   ps = nltk.porter.PorterStemmer()
   return ' '.join([ps.stem(word) for word in text.split()])
    
  stopword_list = stopwords.words('english')
  tokens = nltk.word_tokenize(text)
  tokens = [token.strip() for token in tokens]
  return ' '.join([token for token in tokens if token not in stopword_list])
def lemmatize(text):
    text = nlp(text)
    return ' '.join([word.lemma_ if word.lemma_ != '-PRON-' else word.text for word in text])
def expand_contractions(text, contraction_mapping=contractions_dict):
    
    contractions_pattern = re.compile('({})'.format('|'.join(contraction_mapping.keys())), 
                                      flags=re.IGNORECASE|re.DOTALL)
    def expand_match(contraction):
        match = contraction.group(0)
        first_char = match[0]
        expanded_contraction = contraction_mapping.get(match)\
                                if contraction_mapping.get(match)\
                                else contraction_mapping.get(match.lower())                       
        expanded_contraction = first_char+expanded_contraction[1:]
        return expanded_contraction
        
    expanded_text = contractions_pattern.sub(expand_match, text)
    return re.sub("'", "", expanded_text)

После очистки наших данных мы можем заполнить облако слов для нашего анализа. Облако слов — один из наиболее эффективных способов представления этих данных, который указывает на важность текста размером, глубиной цвета и жирностью слова.

from wordcloud import WordCloud, STOPWORDS, ImageColorGenerator
wc = WordCloud(stopwords=STOPWORDS,max_font_size=200, max_words=1000000, background_color="white", width=1000, height=1000).generate(' '.join(df_filtered['text_clean']))
plt.figure(figsize=(20,20))
plt.imshow(wc)
plt.axis("off")
plt.show()

После заполнения облака слов вы можете определить любые другие тексты, которые вам не нужны для анализа. Вы можете просто расширить текущую библиотеку стоп-слов, добавив эти идентифицированные слова следующим образом.

def clean_text_again(text):
from gensim.parsing.preprocessing import STOPWORDS
all_stopwords_gensim = STOPWORDS.union(set(['thank', 'need', 'yes', 'okay','ok','thanks','morning','hello','sure','hi', 'know', 'got','yesterday']))
text_tokens = word_tokenize(text)
    tokens_without_sw = [word for word in text_tokens if not word in all_stopwords_gensim]
return ' '.join([word for word in text_tokens if not word in all_stopwords_gensim])

Расширенная версия Word Cloud — изучение Ngram

Nграммы — это просто непрерывные последовательности из n слов. Глядя на наиболее часто встречающиеся n-граммы, мы можем лучше понять контекст.

Приведенная ниже функция представляет наиболее часто встречающиеся непрерываемые слова в гистограмме.

import seaborn as sns
def plot_top_non_stopwords_barchart(text):
     
    new= text.str.split()
    new=new.values.tolist()
    corpus=[word for i in new for word in i]
counter=Counter(corpus)
    most=counter.most_common()
    x, y=[], []
    for word,count in most[:40]:
        if (word not in stop):
            x.append(word)
            y.append(count)
      
    plt.figure(figsize=(15,8))
    sns.barplot(x=y,y=x,palette="colorblind")

Однако, чтобы понять контекст непрерывных слов и их взаимосвязей; нам нужно заполнить график словами, которые, как правило, следуют сразу за ними. Я изменил приведенную выше функцию, чтобы использовать 3 непосредственных слова для рисования триграммы следующим образом.

def plot_top_ngrams_barchart(text, n=3): #n is the number of immediate words you need.
      
    new= text.str.split()
    new=new.values.tolist()
    corpus=[word for i in new for word in i]
def _get_top_ngram(corpus, n=None):
        vec = CountVectorizer(ngram_range=(n, n)).fit(corpus)
        bag_of_words = vec.transform(corpus)
        sum_words = bag_of_words.sum(axis=0) 
        words_freq = [(word, sum_words[0, idx]) 
                      for word, idx in vec.vocabulary_.items()]
        words_freq =sorted(words_freq, key = lambda x: x[1], reverse=True)
        return words_freq[:10]
top_n_bigrams=_get_top_ngram(text,n)[:10]
    x,y=map(list,zip(*top_n_bigrams))
    sns.barplot(x=y,y=x)

Анализ настроений

Анализ тональности — это широко используемый инструмент с набором инструментов NLP, который автоматически классифицирует тональность текстовых данных, таких как ответы учащихся. Во-первых, мы можем найти распределение настроений, используя гистограмму полярности. Полярность — это число с плавающей запятой, лежащее в диапазоне от -1 до 1, где 1 означает положительный оператор, а -1 означает отрицательный.

from textblob import TextBlob
    
def plot_polarity_histogram(text):
    
    def polarity(text):
        return TextBlob(text).sentiment.polarity
    
    df_x = pd.DataFrame() 
    polarity_score =text.apply(lambda x : polarity(x))
    df_filtered['polarity_score']=df_filtered['text'].\
       apply(lambda x : polarity(x))
    polarity_score.hist()

Мы можем дополнительно проанализировать настроения, чтобы увидеть распределение положительных, отрицательных и нейтральных настроений. Приведенная ниже функция классифицирует настроения и представляет гистограмму.

from textblob import TextBlob
import matplotlib.pyplot as plt
from nltk.sentiment.vader import SentimentIntensityAnalyzer
import nltk
def sentiment_vader(text, sid):
    ss = sid.polarity_scores(text)
    ss.pop('compound')
    return max(ss, key=ss.get)
def sentiment_textblob(text):
        x = TextBlob(text).sentiment.polarity
        
        if x<0:
            return 'Negative'
        elif x==0:
            return 'Neutral'
        else:
            return 'Positive'
        
def sentiment_x(x):
    if x<0:
        return 'Negative'
    elif x==0:
        return 'Neutral'
    else:
        return 'Positive' 
        
def plot_sentiment_barchart(text, method):
    if method == 'TextBlob':
        sentiment = text.map(lambda x: sentiment_textblob(x))
    elif method == 'Vader':
        nltk.download('vader_lexicon')
        sid = SentimentIntensityAnalyzer()
        sentiment = text.map(lambda x: sentiment_vader(x, sid=sid))
    else:
        raise ValueError('Textblob or Vader')
    
    df_filtered['polarity']=df_filtered['polarity_score'].\
       apply(lambda x : sentiment_x(x))
  
    plt.figure(figsize=(5, 2), dpi=100)
    plt.bar(sentiment.value_counts().index,
            sentiment.value_counts())

Технология анализа настроений находится в зачаточном состоянии. Таким образом, прогнозы должны оцениваться соответствующим образом и с участием человека. Но с большим количеством чистых и размеченных данных достаточно большие нейронные сети будут становиться все более точными и станут отличным инструментом в долгосрочной перспективе.