Polarité vs subjectivité

L'analyse de la polarité et de la subjectivité est un outil utile pour comprendre le ton et la perspective présentés dans les données textuelles, telles que les articles de journaux. La polarité fait référence au sentiment véhiculé dans le texte, généralement classé comme positif, négatif ou neutre. Cela permet aux chercheurs de suivre l'évolution de l'opinion publique ou du ton des médias au fil du temps. La subjectivité, quant à elle, indique la part d'opinion personnelle ou de parti pris dans le texte, par opposition aux informations factuelles. En analysant ces aspects, les chercheurs en DH peuvent découvrir des biais dans la couverture médiatique, comparer les styles narratifs de différentes périodes ou explorer la manière dont certains événements sont encadrés par différentes sources.

Dans l'analyse des sentiments, le score de polarité varie souvent de -1 à 1, où -1 représente un sentiment totalement négatif, 1 un sentiment totalement positif et 0 un sentiment neutre.

En ce qui concerne la subjectivité, la note varie généralement de 0 à 1. Un score de 0 indique que le texte est totalement objectif, c'est-à-dire qu'il est basé sur des faits et ne comporte pas d'opinion personnelle ou d'émotion. Une note de 1, en revanche, indique que le texte est très subjectif, qu'il contient des opinions personnelles, des hypothèses ou des interprétations plutôt que des informations factuelles.

Bénin

Burkina Faso

Code Python

import requests
import re
import stanza
import pandas as pd
import nltk
from textblob import TextBlob
from textblob_fr import PatternTagger, PatternAnalyzer
from nltk.corpus import stopwords
from tqdm import tqdm
from plotly.offline import plot
import plotly.express as px
# Download necessary resources
nltk.download('stopwords')
nltk.download('punkt')
# Load French stop words
french_stopwords = set(stopwords.words('french')) | {'El', '000', '%'}
french_stopwords = set(word.lower() for word in french_stopwords)
# Initialize Stanza French model
nlp = stanza.Pipeline(lang='fr', processors='tokenize,mwt,pos,lemma')
# Compile regular expressions for text cleaning
newline_re = re.compile(r'\n')
apostrophe_re = re.compile(r"’")
whitespace_re = re.compile(r"\s+")
oe_re = re.compile(r"œ")
def fetch_items_from_set(item_set_ids):
base_url = "https://iwac.frederickmadore.com/api/items"
items = []
for set_id in tqdm(item_set_ids, desc="Fetching item sets"):
page = 1
while True:
response = requests.get(f"{base_url}?item_set_id={set_id}&page={page}")
data = response.json()
if not data:
break
items.extend(data)
page += 1
return items
def extract_texts_and_dates(items):
texts = []
dates = []
for item in tqdm(items, desc="Extracting texts and dates"):
date_content = next((content.get('@value', '') for content in item.get('dcterms:date', []) if content.get('is_public', True)), None)
if date_content: # Ensure there is a date before adding the text
if "bibo:content" in item:
content_blocks = item["bibo:content"]
for content in content_blocks:
if content.get('property_label') == 'content' and content.get('is_public', True):
text_content = content.get('@value', '')
if text_content: # Ensure there is text content
texts.append(text_content)
dates.append(date_content) # Only add date if there's a corresponding text
return texts, dates
def preprocess_texts(texts):
processed_texts = []
for text in tqdm(texts, desc="Preprocessing texts"):
text = newline_re.sub(' ', text)
text = apostrophe_re.sub("'", text)
text = whitespace_re.sub(" ", text)
text = oe_re.sub("oe", text)
text = text.strip().lower() # Convert to lower case before processing
# Process the cleaned text with Stanza
doc = nlp(text)
tokens = [word.lemma.lower() for sent in doc.sentences for word in sent.words
if word.upos not in ['PUNCT', 'SYM', 'X'] and word.lemma.lower() not in french_stopwords]
processed_text = ' '.join(tokens)
processed_texts.append(processed_text)
return processed_texts
def analyze_sentiments(texts):
sentiments = []
for text in tqdm(texts, desc="Analyzing sentiments"):
blob = TextBlob(text, pos_tagger=PatternTagger(), analyzer=PatternAnalyzer())
polarity = blob.sentiment[0]
sentiments.append(polarity)
return sentiments
def create_polarity_time_series(sentiments, dates, file_name):
df = pd.DataFrame({'Date': dates, 'Polarity': sentiments})
# Convert date strings to datetime objects, handling different formats
df['Date'] = pd.to_datetime(df['Date'], errors='coerce', infer_datetime_format=True)
# Remove any rows where dates could not be converted (if any)
df = df.dropna(subset=['Date'])
# Group by Date and calculate mean Polarity
df = df.groupby('Date').mean().reset_index()
# Create the figure with a range slider
fig = px.line(df, x='Date', y='Polarity', title="Mean polarity over time")
fig.update_layout(
xaxis=dict(
rangeselector=dict(
buttons=list([
dict(count=1, label="1Y", step="year", stepmode="backward"),
dict(count=5, label="5Y", step="year", stepmode="backward"),
dict(count=10, label="10Y", step="year", stepmode="backward"),
dict(step="all")
])
),
rangeslider=dict(
visible=True
),
type="date"
)
)
plot(fig, filename=file_name)
def main():
benin_item_sets = [2187, 2188, 2189]
burkina_faso_item_sets = [2200, 2215, 2214, 2207, 2201]
benin_items = fetch_items_from_set(benin_item_sets)
burkina_faso_items = fetch_items_from_set(burkina_faso_item_sets)
benin_texts, benin_dates = extract_texts_and_dates(benin_items)
burkina_faso_texts, burkina_faso_dates = extract_texts_and_dates(burkina_faso_items)
benin_processed = preprocess_texts(benin_texts)
burkina_faso_processed = preprocess_texts(burkina_faso_texts)
benin_sentiments = analyze_sentiments(benin_processed)
burkina_faso_sentiments = analyze_sentiments(burkina_faso_processed)
create_polarity_time_series(benin_sentiments, benin_dates, 'polarity_time_series_benin.html')
create_polarity_time_series(burkina_faso_sentiments, burkina_faso_dates, 'polarity_time_series_burkina_faso.html')
if __name__ == "__main__":
main()