Représenter visuellement le contenu d’un document texte est l’une des tâches les plus importantes dans le domaine de l’exploration de texte (aussi dit analyse exploratoire de textes). En tant que data scientist ou spécialiste du NLP, non seulement nous explorons le contenu des documents sous différents aspects et à différents niveaux de détails, mais nous résumons également un seul document, montrons les mots et les sujets, détectons les événements et créons des scénarios.
Cependant, il existe des écarts entre la visualisation des données non structurées (texte) et des données structurées. Par exemple, de nombreuses visualisations de texte ne représentent pas le texte directement, elles représentent une sortie d’un modèle de langage (nombre de mots, longueur des caractères, séquences de mots, etc.).
Dans cet article, nous utiliserons l’ensemble de données Womens Clothing E-Commerce Reviews et essaierons d’explorer et de visualiser autant que possible, en utilisant la bibliothèque graphique Python de Plotly et la bibliothèque de visualisation Bokeh. Non seulement nous allons explorer les données textuelles, mais nous allons également visualiser les caractéristiques numériques et catégorielles.
Contenus
ToggleAnalyse exploratoire de textes : les données
Après une brève inspection des données, nous avons constaté qu’il y a une série de prétraitements de données que nous devons effectuer.
Supprimez la fonction « Titre ».
Supprimez les lignes où « Review Text » manquait.
Nettoyez la colonne « Texte de révision ».
Utilisation de TextBlob pour calculer la polarité des sentiments qui se situe dans la plage de [-1,1] où 1 signifie un sentiment positif et -1 signifie un sentiment négatif.
Créer une nouvelle fonctionnalité pour la durée de l’examen.
Créer une nouvelle fonctionnalité pour le nombre de mots de l’examen.
Pour prévisualiser si le score de polarité des sentiments fonctionne, nous sélectionnons au hasard 5 avis avec le score de polarité des sentiments le plus élevé (1) :
print('5 random reviews with the highest positive sentiment polarity: \n')
cl = df.loc[df.polarity == 1, ['Review Text']].sample(5).values
for c in cl:
print(c[0])
Sélectionnez ensuite au hasard 5 avis avec le score de polarité des sentiments le plus neutre (zéro) :
print('5 random reviews with the most neutral sentiment(zero) polarity: \n')
cl = df.loc[df.polarity == 0, ['Review Text']].sample(5).values
for c in cl:
print(c[0])
Il n’y avait que 2 avis avec le score de polarité des sentiments le plus négatif :
print('2 reviews with the most negative polarity: \n')
cl = df.loc[df.polarity == -0.97500000000000009, ['Review Text']].sample(2).values
for c in cl:
print(c[0])
Cela semble fonctionner
Visualisation univariée
La visualisation à variable unique ou univariée est le type de visualisation le plus simple qui consiste en des observations sur une seule caractéristique ou un seul attribut. La visualisation univariée comprend un histogramme, des diagrammes à barres et des graphiques linéaires.
La grande majorité des scores de polarité des sentiments sont supérieurs à zéro, ce qui signifie que la plupart d’entre eux sont plutôt positifs.
Les notes sont alignées sur le score de polarité, c’est-à-dire que la plupart des notes sont assez élevées à 4 ou 5 plages.
Il est possible de faire de même avec l’âge des reviewers, le nombre de caractères par reviews et le nombres de mots par reviews mais cela n’est pas le coeur de ce tutoriel.
Les n-grammes
Nous arrivons maintenant à la fonctionnalité qui nous intéresse, avant d’explorer cette fonctionnalité, nous devons extraire les fonctionnalités N-Gram. Les N-grammes sont utilisés pour décrire le nombre de mots utilisés comme points d’observation, par exemple, unigramme signifie un seul mot, bigramme signifie une phrase à 2 mots et trigramme signifie une phrase à 3 mots. Pour ce faire, nous utilisons la fonction CountVectorizer de scikit-learn.
Pour faire l’analyse des unigrammes, il est très important de nettoyer le texte des stopwords.
def get_top_n_words(corpus, n=None): | |
vec = CountVectorizer(stop_words = ‘english’).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[:n] | |
common_words = get_top_n_words(df[‘Review Text’], 20) | |
for word, freq in common_words: | |
print(word, freq) | |
df2 = pd.DataFrame(common_words, columns = [‘ReviewText’ , ‘count’]) | |
df2.groupby(‘ReviewText’).sum()[‘count’].sort_values(ascending=False).iplot( | |
kind=‘bar’, yTitle=‘Count’, linecolor=‘black’, title=‘Top 20 words in review after removing stop words’) |
def get_top_n_bigram(corpus, n=None): | |
vec = CountVectorizer(ngram_range=(2, 2), stop_words=‘english’).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[:n] | |
common_words = get_top_n_bigram(df[‘Review Text’], 20) | |
for word, freq in common_words: | |
print(word, freq) | |
df4 = pd.DataFrame(common_words, columns = [‘ReviewText’ , ‘count’]) | |
df4.groupby(‘ReviewText’).sum()[‘count’].sort_values(ascending=False).iplot( | |
kind=‘bar’, yTitle=‘Count’, linecolor=‘black’, title=‘Top 20 bigrams in review after removing stop words’) |
Part-of-Speech
Part-Of-Speech Tagging (POS) est un processus d’attribution de parties du discours à chaque mot, comme le nom, le verbe, l’adjectif, etc.
blob = TextBlob(str(df[‘Review Text’])) | |
pos_df = pd.DataFrame(blob.tags, columns = [‘word’ , ‘pos’]) | |
pos_df = pos_df.pos.value_counts()[:20] | |
pos_df.iplot( | |
kind=‘bar’, | |
xTitle=‘POS’, | |
yTitle=‘count’, | |
title=‘Top 20 Part-of-speech tagging for review corpus’) |
Analyse par classe
La boîte à moustaches est utilisée pour comparer le score de polarité des sentiments, la notation, la longueur des textes de révision de chaque département ou division du magasin de commerce électronique.
Le score de polarité des sentiments le plus élevé a été obtenu par l’ensemble des six départements, à l’exception du département Tendance, et le score de polarité des sentiments le plus bas a été obtenu par le département Tops. Et le département Trend a le score de polarité médian le plus bas. Si vous vous souvenez, le département Trend a le moins d’avis. Cela explique pourquoi il n’a pas une aussi grande variété de distribution des scores que les autres départements.
À l’exception du département Tendance, la note médiane de tous les autres départements était de 5. Dans l’ensemble, les notes sont élevées et le sentiment est positif dans cet ensemble de données d’examen.
Et ainsi de suite.
Analyse bivariée
La visualisation bivariée est un type de visualisation qui consiste en deux caractéristiques à la fois. Il décrit l’association ou la relation entre deux caractéristiques.
Regardons l’analyse des sentiments en fonction de si la personne recommande ou non le produit.
x1 = df.loc[df[‘Recommended IND’] == 1, ‘polarity’] | |
x0 = df.loc[df[‘Recommended IND’] == 0, ‘polarity’] | |
trace1 = go.Histogram( | |
x=x0, name=‘Not recommended’, | |
opacity=0.75 | |
) | |
trace2 = go.Histogram( | |
x=x1, name = ‘Recommended’, | |
opacity=0.75 | |
) | |
data = [trace1, trace2] | |
layout = go.Layout(barmode=‘overlay’, title=‘Distribution of Sentiment polarity of reviews based on Recommendation’) | |
fig = go.Figure(data=data, layout=layout) | |
iplot(fig, filename=‘overlaid histogram’) |
trace1 = go.Scatter( | |
x=df[‘polarity’], y=df[‘Rating’], mode=‘markers’, name=‘points’, | |
marker=dict(color=‘rgb(102,0,0)’, size=2, opacity=0.4) | |
) | |
trace2 = go.Histogram2dContour( | |
x=df[‘polarity’], y=df[‘Rating’], name=‘density’, ncontours=20, | |
colorscale=‘Hot’, reversescale=True, showscale=False | |
) | |
trace3 = go.Histogram( | |
x=df[‘polarity’], name=‘Sentiment polarity density’, | |
marker=dict(color=‘rgb(102,0,0)’), | |
yaxis=‘y2’ | |
) | |
trace4 = go.Histogram( | |
y=df[‘Rating’], name=‘Rating density’, marker=dict(color=‘rgb(102,0,0)’), | |
xaxis=‘x2’ | |
) | |
data = [trace1, trace2, trace3, trace4] | |
layout = go.Layout( | |
showlegend=False, | |
autosize=False, | |
width=600, | |
height=550, | |
xaxis=dict( | |
domain=[0, 0.85], | |
showgrid=False, | |
zeroline=False | |
), | |
yaxis=dict( | |
domain=[0, 0.85], | |
showgrid=False, | |
zeroline=False | |
), | |
margin=dict( | |
t=50 | |
), | |
hovermode=‘closest’, | |
bargap=0, | |
xaxis2=dict( | |
domain=[0.85, 1], | |
showgrid=False, | |
zeroline=False | |
), | |
yaxis2=dict( | |
domain=[0.85, 1], | |
showgrid=False, | |
zeroline=False | |
) | |
) | |
fig = go.Figure(data=data, layout=layout) | |
iplot(fig, filename=‘2dhistogram-2d-density-plot-subplots’) |
Modélisation du contenu
Enfin, nous voulons explorer l’algorithme de modélisation de sujet pour cet ensemble de données, pour voir s’il apporterait des avantages et cadrerait avec ce que nous faisons pour notre fonctionnalité de texte de révision.
Nous expérimenterons la technique d’analyse sémantique latente (LSA) dans la modélisation thématique.
- Génération de notre matrice de termes de document à partir du texte de révision vers une matrice de fonctionnalités TF-IDF.
- Le modèle LSA remplace les décomptes bruts dans la matrice de termes de document par un score TF-IDF.
- Effectuez une réduction de dimensionnalité sur la matrice de termes de document à l’aide d’un SVD tronqué.
- Comme le nombre de départements est 6, nous définissons n_topics=6.
- Prendre l’argmax de chaque texte de révision dans cette matrice de sujets donnera les sujets prédits de chaque texte de révision dans les données. Nous pouvons ensuite les trier en nombre de chaque sujet.
- Pour mieux comprendre chaque sujet, nous allons retrouver les trois mots les plus fréquents dans chaque sujet.
reindexed_data = df[‘Review Text’] | |
tfidf_vectorizer = TfidfVectorizer(stop_words=‘english’, use_idf=True, smooth_idf=True) | |
reindexed_data = reindexed_data.values | |
document_term_matrix = tfidf_vectorizer.fit_transform(reindexed_data) | |
n_topics = 6 | |
lsa_model = TruncatedSVD(n_components=n_topics) | |
lsa_topic_matrix = lsa_model.fit_transform(document_term_matrix) | |
def get_keys(topic_matrix): | |
»’ | |
returns an integer list of predicted topic | |
categories for a given topic matrix | |
»’ | |
keys = topic_matrix.argmax(axis=1).tolist() | |
return keys | |
def keys_to_counts(keys): | |
»’ | |
returns a tuple of topic categories and their | |
accompanying magnitudes for a given list of keys | |
»’ | |
count_pairs = Counter(keys).items() | |
categories = [pair[0] for pair in count_pairs] | |
counts = [pair[1] for pair in count_pairs] | |
return (categories, counts) | |
lsa_keys = get_keys(lsa_topic_matrix) | |
lsa_categories, lsa_counts = keys_to_counts(lsa_keys) | |
def get_top_n_words(n, keys, document_term_matrix, tfidf_vectorizer): | |
»’ | |
returns a list of n_topic strings, where each string contains the n most common | |
words in a predicted category, in order | |
»’ | |
top_word_indices = [] | |
for topic in range(n_topics): | |
temp_vector_sum = 0 | |
for i in range(len(keys)): | |
if keys[i] == topic: | |
temp_vector_sum += document_term_matrix[i] | |
temp_vector_sum = temp_vector_sum.toarray() | |
top_n_word_indices = np.flip(np.argsort(temp_vector_sum)[0][–n:],0) | |
top_word_indices.append(top_n_word_indices) | |
top_words = [] | |
for topic in top_word_indices: | |
topic_words = [] | |
for index in topic: | |
temp_word_vector = np.zeros((1,document_term_matrix.shape[1])) | |
temp_word_vector[:,index] = 1 | |
the_word = tfidf_vectorizer.inverse_transform(temp_word_vector)[0][0] | |
topic_words.append(the_word.encode(‘ascii’).decode(‘utf-8’)) | |
top_words.append( » « .join(topic_words)) | |
return top_words | |
top_n_words_lsa = get_top_n_words(3, lsa_keys, document_term_matrix, tfidf_vectorizer) | |
for i in range(len(top_n_words_lsa)): | |
print(« Topic {}: « .format(i+1), top_n_words_lsa[i]) |
top_3_words = get_top_n_words(3, lsa_keys, document_term_matrix, tfidf_vectorizer)
labels = ['Topic {}: \n'.format(i) + top_3_words[i] for i in lsa_categories]fig, ax = plt.subplots(figsize=(16,8))
ax.bar(lsa_categories, lsa_counts);
ax.set_xticks(lsa_categories);
ax.set_xticklabels(labels);
ax.set_ylabel('Number of review text');
ax.set_title('LSA topic counts');
plt.show();
En examinant les mots les plus fréquents dans chaque sujet, nous avons le sentiment que nous n’atteindrons peut-être aucun degré de séparation entre les catégories de sujets. En d’autres termes, nous ne pouvions pas séparer les textes d’examen par département à l’aide de techniques de modélisation thématique.
Les techniques de modélisation thématique présentent un certain nombre de limitations importantes. Pour commencer, le terme « sujet » est quelque peu ambigu, et il est peut-être maintenant clair que les modèles de sujet ne produiront pas une classification très nuancée des textes pour nos données.