A quick demonstration of how to analyze moral news framing via GDELT's
implementation of the extended Moral Foundations Dictionary
.
Frederic R. Hopp
Media Neuroscience Lab - UC Santa Barbara
# Load modules
import seaborn as sns
import pandas as pd
from matplotlib import pyplot as plt
import numpy as np
plt.style.use("seaborn-poster")
Below we analyze the Television data for CNN, MSNBC, and Fox News.
cnn = pd.read_csv('2020-tvnewsngrams-emfd-cnn.csv')
msnbc = pd.read_csv('2020-tvnewsngrams-emfd-msnbc.csv')
fox = pd.read_csv('2020-tvnewsngrams-emfd-foxnews.csv')
# Define column names
foundations = ['care','harm','fairness','cheating','loyalty','betrayal','authority','subversion','sanctity','degradation']
virtues = ['care','fairness','loyalty','authority','sanctity']
vices = ['harm','cheating','betrayal','subversion','degradation']
base_f = ['care_p','fairness_p','loyalty_p','authority_p','sanctity_p']
sents = ['care_sent','fairness_sent','loyalty_sent','authority_sent','sanctity_sent']
Format the date column to be a python datetime object and set it to the index of the dataframe.
cnn['DATE'] = cnn['DATE'].astype(str)
cnn['DATE'] = cnn['DATE'].apply(pd.to_datetime, format='%Y/%m/%d')
cnn = cnn.set_index('DATE')
msnbc['DATE'] = msnbc['DATE'].astype(str)
msnbc['DATE'] = msnbc['DATE'].apply(pd.to_datetime, format='%Y/%m/%d')
msnbc = msnbc.set_index('DATE')
fox['DATE'] = fox['DATE'].astype(str)
fox['DATE'] = fox['DATE'].apply(pd.to_datetime, format='%Y/%m/%d')
fox = fox.set_index('DATE')
Below we define a function that will calculcate the degree to which a given day of news coverage reflects moral virtues (e.g., care, fairness, loyalty, authority, and subversion) and moral vices (e.g., harm, cheating, betrayal, subversion, degradation).
In simple terms, the function multiples the average moral foundation probabilitiy of a given day with the average foundation sentiment of that day. For example, mulitplying the probabily for care (care_p
) with the sentiment for care (care_sent
) yields a score that highlights care
if the product is positive or harm
if the product is negative. Depending on the sign of the product (+/-), the absolute value of the product is added to either a virtue foundation (e.g., + => care) or a vice foundation (e.g., - => harm).
def vice_virtue(df):
df['care_prod'] = df['care_p'] * df['care_sent']
df['fair_prod'] = df['fairness_p'] * df['fairness_sent']
df['loy_prod'] = df['loyalty_p'] * df['loyalty_sent']
df['auth_prod'] = df['authority_p'] * df['authority_sent']
df['sanct_prod'] = df['sanctity_p'] * df['sanctity_sent']
for f in foundations:
df[f] = 0.0
for i,row in df.iterrows():
if row['care_prod'] < 0:
df.at[i, 'harm'] = np.abs(row['care_prod'])
else:
df.at[i, 'care'] = np.abs(row['care_prod'])
if row['fair_prod'] < 0:
df.at[i, 'cheating'] = np.abs(row['fair_prod'])
else:
df.at[i, 'fairness'] = np.abs(row['fair_prod'])
if row['loy_prod'] < 0:
df.at[i, 'betrayal'] = np.abs(row['loy_prod'])
else:
df.at[i, 'loyalty'] = np.abs(row['loy_prod'])
if row['auth_prod'] < 0:
df.at[i, 'subversion'] = np.abs(row['auth_prod'])
else:
df.at[i, 'authority'] = np.abs(row['auth_prod'])
if row['sanct_prod'] < 0:
df.at[i, 'degradation'] = np.abs(row['sanct_prod'])
else:
df.at[i, 'sanctity'] = np.abs(row['sanct_prod'])
return df
Before we apply the above function to our dataframe, we normalize (z-transform) the foundation probabilities and sentiment scores for each dataframe.
for f in base_f:
cnn[f] = (cnn[f] - cnn[f].mean()) / cnn[f].std()
for f in base_f:
msnbc[f] = (msnbc[f] - msnbc[f].mean()) / msnbc[f].std()
for f in base_f:
fox[f] = (fox[f] - fox[f].mean()) / fox[f].std()
for s in sents:
cnn[s] = (cnn[s] - cnn[s].mean()) / cnn[s].std()
for s in sents:
msnbc[s] = (msnbc[s] - msnbc[s].mean()) / msnbc[s].std()
for s in sents:
fox[s] = (fox[s] - fox[s].mean()) / fox[s].std()
# Apply the function
cnn = vice_virtue(cnn)
msnbc = vice_virtue(msnbc)
fox = vice_virtue(fox)
fig, ax = plt.subplots(1,2, figsize=(30,8))
cnn[virtues].rolling(120).mean().plot(ax=ax[0])
sns.despine(offset=10)
ax[0].legend(loc='upper left', labels=['Care','Fairness','Loyalty','Authority','Sanctity'])
ax[0].set_title("CNN -- Virtues")
ax[0].set_ylabel('Moral Foundation Prevalence')
cnn[vices].rolling(120).mean().plot(ax=ax[1])
sns.despine(offset=10)
ax[1].legend(loc='upper right', labels=['Harm','Cheating','Betrayal','Subversion','Degradation'])
ax[1].set_title("CNN -- Vices")
plt.ylabel('Moral Foundation Prevalence')
plt.tight_layout()
plt.show()
fig, ax = plt.subplots(1,2, figsize=(30,8))
msnbc[virtues].rolling(120).mean().plot(ax=ax[0])
sns.despine(offset=10)
ax[0].legend(loc='upper left', labels=['Care','Fairness','Loyalty','Authority','Sanctity'])
ax[0].set_title("MSNBC -- Virtues")
ax[0].set_ylabel('Moral Foundation Prevalence')
msnbc[vices].rolling(120).mean().plot(ax=ax[1])
sns.despine(offset=10)
ax[1].legend(loc='upper right', labels=['Harm','Cheating','Betrayal','Subversion','Degradation'])
ax[1].set_title("MSNBC -- Vices")
plt.ylabel('Moral Foundation Prevalence')
plt.tight_layout()
plt.show()
fig, ax = plt.subplots(1,2, figsize=(30,8))
fox[virtues].rolling(120).mean().plot(ax=ax[0])
sns.despine(offset=10)
ax[0].legend(loc='upper left', labels=['Care','Fairness','Loyalty','Authority','Sanctity'])
ax[0].set_title("FOX News -- Virtues")
ax[0].set_ylabel('Moral Foundation Prevalence')
fox[vices].rolling(120).mean().plot(ax=ax[1])
sns.despine(offset=10)
ax[1].legend(loc='upper right', labels=['Harm','Cheating','Betrayal','Subversion','Degradation'])
ax[1].set_title("FOX News -- Vices")
plt.ylabel('Moral Foundation Prevalence')
plt.tight_layout()
plt.show()