В этом исследовании более подробно рассматриваются исторические текстовые сообщения с применением различных методов машинного обучения. Целью исследования является применение методов обработки естественного языка (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())
Технология анализа настроений находится в зачаточном состоянии. Таким образом, прогнозы должны оцениваться соответствующим образом и с участием человека. Но с большим количеством чистых и размеченных данных достаточно большие нейронные сети будут становиться все более точными и станут отличным инструментом в долгосрочной перспективе.