{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Classy words: formulas for finding category-associated terms\n", "## Jason S. Kessler @jasonkessler\n", "\n", "The Github repository for talk is at [https://github.com/JasonKessler/PuPPyTalk](https://github.com/JasonKessler/PuPPyTalk). \n", "\n", "Visualizations were made using [Scattertext](https://github.com/JasonKessler/scattertext).\n", "\n", "Please cite as: \n", "Jason S. Kessler. Scattertext: a Browser-Based Tool for Visualizing how Corpora Differ. ACL System Demonstrations. 2017." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/html": [ "" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import pandas as pd\n", "import numpy as np\n", "import scattertext as st\n", "import spacy\n", "from scipy.stats import rankdata\n", "from IPython.display import IFrame\n", "from IPython.core.display import display, HTML\n", "display(HTML(\"\"))\n", "import matplotlib.pyplot as plt\n", "%matplotlib inline " ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "ename": "TypeError", "evalue": "'>=' not supported between instances of 'list' and 'str'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32massert\u001b[0m \u001b[0mst\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mversion\u001b[0m \u001b[0;34m>=\u001b[0m \u001b[0;34m'0.0.2.22'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: '>=' not supported between instances of 'list' and 'str'" ] } ], "source": [ "assert st.version >= '0.0.2.22'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### The data\n", " \n", "Dataset consists of reviews of movies and plot descriptions. Plot descriptions are guaranteed to be from a movie which was reviewed. \n", "\n", "Data set is from http://www.cs.cornell.edu/people/pabo/movie-review-data/\n", "\n", "References:\n", "* Bo Pang, Lillian Lee, and Shivakumar Vaithyanathan, Thumbs up? Sentiment Classification using Machine Learning Techniques, Proceedings of EMNLP 2002.\n", "\n", "* Bo Pang and Lillian Lee, A Sentimental Education: Sentiment Analysis Using Subjectivity Summarization Based on Minimum Cuts, Proceedings of ACL 2004." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Positive 2455\n", "Negative 2411\n", "Plot 156\n", "Name: category_name, dtype: int64\n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
textmovie_namecategory_name
0A senior at an elite college (Katie Holmes), a...abandonPlot
1Will Lightman is a hip Londoner who one day re...about_a_boyPlot
2Warren Schmidt (Nicholson) is forced to deal w...about_schmidtPlot
3An account of screenwriter Charlie Kaufman's (...adaptationPlot
4Ali G unwittingly becomes a pawn in the evil C...ali_g_indahousePlot
\n", "
" ], "text/plain": [ " text movie_name \\\n", "0 A senior at an elite college (Katie Holmes), a... abandon \n", "1 Will Lightman is a hip Londoner who one day re... about_a_boy \n", "2 Warren Schmidt (Nicholson) is forced to deal w... about_schmidt \n", "3 An account of screenwriter Charlie Kaufman's (... adaptation \n", "4 Ali G unwittingly becomes a pawn in the evil C... ali_g_indahouse \n", "\n", " category_name \n", "0 Plot \n", "1 Plot \n", "2 Plot \n", "3 Plot \n", "4 Plot " ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rdf = st.SampleCorpora.RottenTomatoes.get_data()\n", "rdf['category_name'] = rdf['category'].apply(lambda x: {'plot': 'Plot', 'rotten': 'Negative', 'fresh': 'Positive'}[x])\n", "print(rdf.category_name.value_counts())\n", "rdf[['text', 'movie_name', 'category_name']].head()" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "corpus = (st.CorpusFromPandas(rdf, \n", " category_col='category_name', \n", " text_col='text',\n", " nlp = st.whitespace_nlp_with_sentences)\n", " .build())\n", "corpus.get_term_freq_df().to_csv('term_freqs.csv')\n", "unigram_corpus = corpus.get_unigram_corpus()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Let's visualize the corpus using Scattertext\n", "\n", "The x-axis indicates the rank of a word or bigram in the set of positive reviews, and the y-axis negative reviews.\n", "\n", "Ranks are determined using \"dense\" ranking, meaning the most frequent terms, regardless of ties, are given rank 1, the next most frequent terms, regardless of ties, are given rank 2, etc.\n", "\n", "It appears that terms more associated with a class are a further distance from the diagonal line between the lower-left and upper-right corners. Terms are colored according to this distance. We'll return to this in a bit.\n", "\n", "Scattertext selectively labels points in such a way as to prevent labels from overlapping other elements of the graph. Mouse-over points and term labels for a preview, and click for a key-word in context view.\n", "\n", "References:\n", "* Jason S. Kessler. Scattertext: a Browser-Based Tool for Visualizing how Corpora Differ. ACL System Demonstrations. 2017." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "html = st.produce_scattertext_explorer(\n", " corpus,\n", " category='Positive',\n", " not_categories=['Negative'],\n", " sort_by_dist=False,\n", " metadata=rdf['movie_name'],\n", " term_scorer=st.RankDifference(),\n", " transform=st.Scalers.percentile_dense\n", ")\n", "file_name = 'output/rotten_fresh_stdense.html'\n", "open(file_name, 'wb').write(html.encode('utf-8'))\n", "IFrame(src=file_name, width = 1300, height=700)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### We view can see more terms through breaking ties in ranking alphabetically.\n", "Lower frequency terms are more prominent in this view, and more terms can be labeled." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "html = st.produce_scattertext_explorer(\n", " corpus,\n", " category='Positive',\n", " not_categories=['Negative'],\n", " sort_by_dist=False,\n", " metadata=rdf['movie_name'],\n", " term_scorer=st.RankDifference(),\n", ")\n", "file_name = 'output/rotten_fresh_st.html'\n", "open(file_name, 'wb').write(html.encode('utf-8'))\n", "IFrame(src=file_name, width = 1300, height=700)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Examining term-category association\n", "We'll roughly follow the explanation given in Monroe et. al (2008), culminating in a version of their Log-Odds-Ratio with an Informed Dirichlet Prior score. \n", "\n", "### Naive approach 1\n", "### Frequency difference (not recommended)\n", "$$\n", "\\begin{align}\n", " \\mbox{freqdiff}(\\mbox{term}) & := \\mbox{% or words in catgory A that are term} &- \\mbox{% or words in catgory B that are term}\\\\\n", " \\mbox{freqdiff}(\\mbox{term}) & := \\frac{\\#(\\mbox{term}\\in\\mbox{category}_a)}{\\#(\\mbox{category}_a)} &- \\frac{\\#(\\mbox{term}\\in\\mbox{category}_b)}{\\#(\\mbox{category}_b)}\\\\\n", "\\end{align}\n", "$$\n", "Disporportionately favors very frequent words.\n", "\n", "References\n", "* Burt L. Monroe, Michael P. Colaresi, and Kevin M. Quinn. 2008. Fightin’ words: Lexical feature selection and evaluation for identifying the content of political conflict. Political Analysis.\n" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "class FrequencyDifference:\n", " def get_scores(self, a, b): \n", " return a/np.sum(a) - b/np.sum(b)\n", " def get_name(self): \n", " return 'Frequency Difference'\n", " \n", "html = st.produce_fightin_words_explorer(\n", " corpus,\n", " category='Positive',\n", " not_categories=['Negative'],\n", " term_scorer=FrequencyDifference(),\n", " metadata = rdf['movie_name'],\n", " grey_threshold=0\n", ")\n", "file_name = 'output/rotten_fresh_diff.html'\n", "open(file_name, 'wb').write(html.encode('utf-8'))\n", "IFrame(src=file_name, width = 1300, height=700)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Naive approach 2\n", "### tf.idf difference (not recommended)\n", "$$ \\mbox{Term Frquency}(\\mbox{term}, \\mbox{category}) = \\#(\\mbox{term}\\in\\mbox{category}) $$\n", "\n", "$$ \\mbox{Inverse Document Frquency}(\\mbox{term}) = \\log \\frac{\\mbox{# of categories}}{\\mbox{# of categories containing term}} $$\n", "\n", "$$ \\mbox{tfidf}(\\mbox{term}, \\mbox{category}) = \\mbox{Term Frquency}(\\mbox{term}, \\mbox{category}) \\times \\mbox{Inverse Document Frquency}(\\mbox{term}) $$\n", "\n", "$$ \\mbox{tfidf-difference}(\\mbox{term}, \\mbox{category}) = \\mbox{tf.idf}(\\mbox{term}, \\mbox{category}_a) - \\mbox{tf.idf}(\\mbox{term}, \\mbox{category}_b) $$\n", "\n", "Tf.idf ignores terms used in each category. Since we only consider three categories (positive, negative and plot descriptions), a large number of terms have zero (log 1) scores. The problem is Tf.idf doesn't weight how often a term is used in another category. This causes eccentric, brittle, low-frequency terms to be favored.\n", "\n", "This formulation does take into account data from a background corpus." ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/Users/kesslej/anaconda3/envs/py36/lib/python3.6/site-packages/scattertext-0.0.2.20-py3.6.egg/scattertext/TermDocMatrixFilter.py:43: RuntimeWarning: invalid value encountered in log\n", " bigram_prob[bigram] / np.product([unigram_prob[word] for word in bigram.split(' ')])\n" ] }, { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "class TFIDF:\n", " def __init__(self, corpus):\n", " self.tdf = corpus.get_term_freq_df()\n", " def get_scores(self, a, b): \n", " n_docs = self.tdf.shape[1]\n", " n_docs_with_term = (self.tdf > 0).astype(float).sum(axis=1)\n", " idf = np.log(n_docs/n_docs_with_term)/np.log(2)\n", " return a * idf - b * idf\n", " def get_name(self): \n", " return 'tf.idf difference'\n", " \n", "html = st.produce_fightin_words_explorer(\n", " corpus,\n", " category='Positive',\n", " not_categories=['Negative'],\n", " neutral_categories=['Plot'],\n", " term_scorer=TFIDF(corpus),\n", " metadata = rdf['movie_name'],\n", " grey_threshold=0,\n", " show_neutral=True\n", ")\n", "file_name = 'output/rotten_fresh_tfidf.html'\n", "open(file_name, 'wb').write(html.encode('utf-8'))\n", "IFrame(src=file_name, width = 1300, height=700)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Naive approach 3\n", "### Log-odds-ratio w/ smoothing (not preferred)\n", "\n", "$$ \\newcommand\\ddfrac[2]{\\frac{\\displaystyle #1}{\\displaystyle #2}}$$\n", "$$ \\mbox{Odds}(\\mbox{term}_i, \\mbox{category}) = \\frac{\\#(\\mbox{term}_i\\in\\mbox{category}_a)}{\\sum_{\\mbox{term}` \\neq \\mbox{term}} \\#(\\mbox{term}'\\in\\mbox{category})} = \\frac{y_{ai}}{n_{a} - y_{ai}} $$\n", "Note: this can be a very small number.\n", "\n", "The odds ratio is the ratio of the odds of a term being used in one category vs. another.\n", "$$ \\mbox{Odds-Ratio}(\\mbox{term}_i, \\mbox{category}_a, \\mbox{category}_b) = \\ddfrac{y_{ai}\\mathbin{/}(n_{a} - y_{ai})}{y_{bi}\\mathbin{/}(n_{b} - y_{bi})} $$\n", "\n", "The Log-Odds-Ratio is often used, given these quantities are often very small. For our purposes, the fact that it's normally distributed is important.\n", "\n", "$$ \\mbox{Log-Odds-Ratio}(\\mbox{term}_i, \\mbox{category}_a, \\mbox{category}_b) = \\log \\frac{y_{ai}}{n_{a} - y_{ai}} - \\log \\frac{y_{bi}}{n_{b} - y_{bi}} $$\n", "\n", "If term does not appear in $\\mbox{category}_b$ (i.e., $ y_{bi} = 0 $), the odd of term_i in category_b will be 0 resulting the odds-ratio to be undefined. We circumvent this issue by adding a pseudo-count to each term, assuming it occurs at least $\\alpha$ times. \n", "\n", "$$ \\mbox{Smoothed Log-Odds-Ratio}(\\mbox{term}_i, \\mbox{category}_a, \\mbox{category}_b) = \\log \\frac{y_{ai} + \\alpha}{n_{a} + \\alpha \\cdot |y| - y_{ai} - \\alpha} - \\log \\frac{y_{bi} + \\alpha}{n_{b} + \\alpha \\cdot |y| - y_{bi} - \\alpha} $$\n", "\n", "This has a slightly better profile than tf.idf, but still favors very low-frequency terms that are unique to a category.\n", "\n", "We can see that terms used exclusively in one category or another clump form extremely high or low-scoring clumps. These terms occur infrequently. Intuitively, we'd like to score things highly if they have a lot of evidence behind them AND they tend to be used much more in one category vs. another.\n", "\n", "Note: a common error when implementing this is to not account for other pseudocounts in the denominator.\n" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "scrolled": false }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "class LogOddsRatioSmoothed:\n", " def __init__(self, alpha):\n", " self.alpha = alpha\n", " def get_scores(self, a, b): \n", " return (np.log((a + self.alpha)/(np.sum(a) + self.alpha * len(a) - a - self.alpha)) \n", " - np.log((b + self.alpha)/(np.sum(b) + self.alpha * len(b) - b - self.alpha)))\n", " def get_name(self): \n", " return 'Smoothed Log-Odds-Ratio'\n", " \n", "html = st.produce_fightin_words_explorer(\n", " corpus,\n", " category='Positive',\n", " not_category_name='Negative',\n", " not_categories=['Negative'],\n", " term_scorer=LogOddsRatioSmoothed(0.00000001),\n", " metadata = rdf['movie_name'],\n", " grey_threshold=0,\n", ")\n", "file_name = 'output/rotten_fresh_lorsmooth.html'\n", "open(file_name, 'wb').write(html.encode('utf-8'))\n", "IFrame(src=file_name, width = 1300, height=700)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Accounting for variance\n", "How can we account for the certainty of the estimates? Less frequent terms will have higher variances.\n", "\n", "The standard error of the log-odds-ratio is:\n", "\n", "$$ \\mbox{SE-Smoothed Log-Odds-Ratio}(\\mbox{term}_i, \\mbox{category}_a, \\mbox{category}_b) = \\frac{1}{y_{ai} + \\alpha} + \\frac{1}{n_{a} + \\alpha \\cdot |y| - y_{ai} - \\alpha} + \\frac{1}{y_{bi} + \\alpha}+ \\frac{1}{n_{b} + \\alpha \\cdot |y| - y_{bi} - \\alpha} $$\n", "\n", "The plot below shows how terms used infrequently and especially those which only appear in one class have a very high variance." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "class SELogOddsRatioSmoothed:\n", " def __init__(self, alpha):\n", " self.alpha = alpha\n", " def get_scores(self, a, b): \n", " a += self.alpha\n", " b += self.alpha\n", " return 1./a + 1./(np.sum(a) - a) + 1./b + 1./(np.sum(b) - b)\n", " def get_name(self): \n", " return 'SE Smoothed Log-Odds-Ratio'\n", " \n", "html = st.produce_fightin_words_explorer(\n", " corpus,\n", " category='Positive',\n", " not_category_name='Negative',\n", " not_categories=['Negative'],\n", " term_scorer=SELogOddsRatioSmoothed(0.00000001),\n", " metadata = rdf['movie_name'],\n", " grey_threshold=0\n", ")\n", "file_name = 'output/rotten_fresh_lorsmoothse.html'\n", "open(file_name, 'wb').write(html.encode('utf-8'))\n", "IFrame(src=file_name, width = 1300, height=700)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Smoothed Log-Odds-Ratio Z-Score\n", "### Preferred in certain cases\n", "\n", "Finding the Z-Scores of log-odds-ratios allows us to gracefully account for variance.\n", "\n", "$$ \\mbox{LOR Smoothed Z-Score}(\\mbox{term}_i, \\mbox{category}_a, \\mbox{category}_b) = \\frac{\\mbox{LOR Smoothed}}{\\sqrt{\\mbox{SE LOR Smoothed}}} $$\n", "\n", "The chart below, given a smoothing $\\alpha$ of 0.01, dampens the score of infrequent words and allows for frequently words to rank highly. However, the words \"and\" and \"of\" appear to be over-weighted. \n", "\n", "Eccentric words like \"winning\" are down-weighted but still significant.\n", "\n", "MCQ refers to this at the Log-Odds-Ratio with an Uninformative Dirichlet Prior" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "class LogOddsRatioSmoothedZScorePrior:\n", " def __init__(self, prior):\n", " self.prior = prior\n", " def get_scores(self, a, b): \n", " a += self.prior\n", " b += self.prior\n", " lor = (np.log(a/(np.sum(a) - a)) - np.log(b/(np.sum(b) - b)))\n", " lorstd = 1./a + 1./(np.sum(a) - a) + 1./b + 1./(np.sum(b) - b)\n", " return lor/np.sqrt(lorstd)\n", " \n", " def get_name(self): \n", " return 'Smoothed Log-Odds-Ratio w/ Prior Z-Score'\n", "html = st.produce_fightin_words_explorer(\n", " corpus,\n", " category='Positive',\n", " not_category_name='Negative',\n", " not_categories=['Negative'],\n", " term_scorer=LogOddsRatioSmoothedZScorePrior(0.01),\n", " metadata = rdf['movie_name'],\n", " grey_threshold = 1.96\n", ")\n", "file_name = 'output/rotten_fresh_lorudp.html'\n", "open(file_name, 'wb').write(html.encode('utf-8'))\n", "IFrame(src=file_name, width = 1300, height=700)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Log-Odds-Ratio with Informative Dirichlet Prior Z-Score\n", "### Prefered, currently very popular in NLP and CSS literature, flaws exist\n", "### Note: this formulation is modified slightly from Monroe et al. 2008.\n", "\n", "We know, intuitively, words which which are used very frequently require much more evidence to be significantly associated with a class than less frequent words. For example, words like \"best\" and \"entertaining\" in the above example should be scored lower than \"and\" and \"of\". \n", "\n", "The approach developed by Monroe et. al is to more aggressively smooth terms based on their frequency in a background corpus.\n", "\n", "The downside is of this approach is that obtaining a background corpus may be difficult. Lists of background term frequencies, especially for 2+grams, can be very large and unwieldy. Moreover, term frequency lists can become outdated very quickly, especially wrt to politics, pop culture, and technology.\n", "\n", "We construct two vectors of background term counts for each category, $\\alpha_a$ and $\\alpha_b$. The priors are based on a background set of word frequencies, referred to as $y_{c}$ These are ideally in the same domain as the documents being studied, although could come from a general list of word frequencies. The background counts are normalized to the size of their respective categories. The hyperparameter $\\sigma$ scales the background counts to a multiple of their category size. In other words, as $\\sigma$ increases, the regularization increases, and the odds ratio tends more toward zero.\n", "\n", "The $\\sigma$ I've found most useful is 10, which I include in Scattertext.\n", "\n", "$$ \\alpha_a = \\sigma \\cdot n_a \\cdot \\frac{y_{ci}}{n_c} $$\n", "\n", "$$ \\alpha_b = \\sigma \\cdot n_b \\cdot \\frac{y_{ci}}{n_c} $$\n", "\n", "\n", "The $\\alpha$s can then be used analogously to find the Z-Score of the LOR.\n", "\n", "$$ \\mbox{LOR}(\\mbox{term}_i, \\mbox{category}_a, \\mbox{category}_b) = \\log \\frac{y_{ai} + \\alpha_a}{n_{a} + \\alpha_a \\cdot |y| - y_{ai} - \\alpha_a} - \\log \\frac{y_{bi} + \\alpha_b}{n_{b} + \\alpha_b \\cdot |y| - y_{bi} - \\alpha_b} $$\n", "\n", "$$ \\mbox{LOR-SE}(\\mbox{term}_i, \\mbox{category}_a, \\mbox{category}_b) = \\frac{1}{y_{ai} + \\alpha_a} + \\frac{1}{n_{a} + \\alpha_a \\cdot |y| - y_{ai} - \\alpha_a} + \\frac{1}{y_{bi} + \\alpha_b}+ \\frac{1}{n_{b} + \\alpha_b \\cdot |y| - y_{bi} - \\alpha_b} $$\n", "\n", "Monroe et al. used a different approach to finding $\\alpha$, scaling the sum of the peusdocounts to the mean number of words in a document.\n", "\n", "$$ \\alpha_a = \\alpha_b = \\overline{|d_{y}|} \\frac{y_{ci}}{\\sum n_{c}} $$\n", "\n", "Jurafsky et al. suggested using raw background corpus counts as $\\alpha$.\n", "\n", "$$ \\alpha_a = \\alpha_b = y_c $$\n", "\n", "In the following example, we use the plot description as a background corpus using the first method.\n", "\n", "* Dan Jurafsky, Victor Chahuneau, Bryan Routledge, and Noah Smith. Narrative framing of consumer sentiment in online restaurant reviews. First Monday. 2014." ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "priors = (st.PriorFactory(unigram_corpus, \n", " category='Positive', \n", " not_categories=['Negative'],\n", " starting_count=0.01)\n", " .use_neutral_categories()\n", " .get_priors())\n", "class LogOddsRatioSmoothedZScorePrior:\n", " def __init__(self, prior, prior_scale):\n", " self.prior = prior\n", " self.prior_scale = prior_scale\n", " def get_scores(self, a, b): \n", " ap = a + self.prior * self.prior_scale*sum(a)/sum(self.prior.values)\n", " bp = b + self.prior * self.prior_scale*sum(b)/sum(self.prior.values)\n", " lor = (np.log(ap/(np.sum(ap) - ap)) - np.log(bp/(np.sum(bp) - bp)))\n", " lorstd = 1./ap + 1./(np.sum(ap) - ap) + 1./bp + 1./(np.sum(bp) - bp)\n", " return lor/np.sqrt(lorstd)\n", " \n", " def get_name(self): \n", " return 'Log-Odds-Ratio w/ Informative Dirichlet Prior Z-Score'\n", "html = st.produce_fightin_words_explorer(\n", " unigram_corpus,\n", " category='Positive',\n", " not_category_name='Negative',\n", " not_categories=['Negative'],\n", " #term_scorer=LogOddsRatioSmoothedZScorePrior(priors, 10, ),\n", " # Equivalent:\n", " term_scorer=st.LogOddsRatioInformativeDirichletPrior(priors, 10, 'class-size'), \n", " metadata = rdf['movie_name']\n", ")\n", "file_name = 'output/rotten_fresh_loridp.html'\n", "open(file_name, 'wb').write(html.encode('utf-8'))\n", "IFrame(src=file_name, width = 1300, height=700)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Effect of $\\sigma$\n", "In the plot below, we can see how a changing $\\sigma$ alters the z-scores of low and high-frequency terms which are associated the Positive category.\n", "\n", "A high $\\sigma$ causes the infrequent word \"winning\", which only occurs in Positive documents, to dominate more frequent, associated terms. A very low $\\sigma$ causes rankings to revert to Smoothed LOR Z-Score." ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\nc0RERERkCWYFUB/ZUqkWCisBM18LnZWw6MoK1bq2kK+F1Ln76uqUc3WvsznKkzh6IP4AABN2SURB\nVFMUq/sLlAuzjz2jiYgWYkwtMM5dn1RWFz7D4dn76rYrAZa67fnqBru6iG7ZsnzfyypTCGxAtmz5\nwQP7SfW0sO3G/sUPEBERERGZhwkGMS0t0NJCIwzorA+ltlCY1Wt5Ugit7F9kXT6prHBynelpt11f\nPqfOUobvpu6+m3P+/DMr+C+1shQCG9C+nwwxcnia2993CcGgegFFREREZG2oD6WNxhaLC4fEOdvB\nFZyxdzUoBDYYW7b88IH9tPfFuej6Pr+bIyJL8Cd/8id+N0FERETOkAmFMKFQQwbU5aYQ2GD2PnOC\n0aNp3vwrlxJQL6BIU7npppv8boKIiIjIopQyGki5bPnRP+2nY0OCC69VL6BIs9m5cyc7d+70uxki\nIiIip6SewAbyyo8GGTue4Wf/w+UEApruVqTZfPzjHwf0nEARERFpbOoJbBDlUpkf/dN+us5JcsHV\nPX43R0RERERE1iiFwAax+weDTAzNcMM9WzDqBRQRERERkRWiENgASqUyP35wPz3ntrLlqm6/myMi\nIiIiImuYQmADeHnnMSaHs64X0KgXUEREREREVo4mhvFZqVDmxzsO0LclxXmXd/ndHBE5C5/97Gf9\nboKIiIjIohQCffbSU0eZHs1x27svVi+gSJPbvn27300QERERWZSGg/qoWCjx9I4DbLigjYFLOv1u\njoicpe9+97t897vf9bsZIiIiIqeknkAfvfjEUdITee745UvVCyiyBvzRH/0RAHfccYfPLRERERFZ\nmHoCfVLIl3j6oYOcc1E7my5WL6CIiIiIiKwOhUCfvPDYEWYm89xwz/l+N0VERERERNYRhUAf5LNF\ndn3nIAOXdLBxa7vfzRERERERkXVEIdAHzz96mJmpgnoBRURERERk1WlimFWWnymy619e49zLuug/\nv83v5ojIMvriF7/odxNEREREFqUQuMqee+QQuXSR171ti99NEZFltm3bNr+bICIiIrIoDQddRblM\ngWe/e4jNV3bTe17K7+aIyDJ74IEHeOCBB/xuhoiIiMgpqSdwFT37vUPkMkVuuEe9gCJr0Wc+8xkA\n7rnnHp9bIiIiIrIw9QSukmy6wE++d4gLru6hZ6DV7+aIiIiIiMg6pRC4Snb9y2sUciWuf6t6AUVE\nRERExD8KgatgZirPc48c5sJre+k6J+l3c0REREREZB1TCFwFu77zGqV8iRvUCygiIiIiIj7TxDAr\nLD2R4/lHD7P1hj46+hN+N0dEVtCXv/xlv5sgIiIisiiFwBW269uvUSpZrr9bvYAia93AwIDfTRAR\nERFZlIaDrqD0eI4XHj/Cthv7ae+L+90cEVlh999/P/fff7/fzRARERE5JfUErqCndxzAli3X373Z\n76aIyCr4whe+AMAv/uIv+twSERERkYWpJ3CFWGsply2X3rKRVHeL380REREREREB1BO4YowxvPH/\nuBhrrd9NERERERERqVJP4AozxvjdBBERERERkaqGCYHGmE3GmL82xhw1xuSMMQeMMZ81xnT43TYR\nEREREZG1oiGGgxpjLgB2Ar3At4CXgRuA/wS8xRhzs7V2xMcmiogs6utf/7rfTRARERFZVEOEQODz\nuAD4YWvt5yqFxpg/B34D+GPg13xqm4jIaenu7va7CSIiIiKL8n04qNcLeCdwAPjLObs/BaSB9xhj\nEqvcNBGRJbnvvvu47777/G6GiIiIyCn5HgKB27z1d6y15fod1top4CkgDty42g0TEVkKhUARERFp\nBo0QArd56z0L7H/FW1+0Cm0RERERERFZ0xohBLZ564kF9lfK2+fuMMa83xjzY2PMj4eGhlakcSIi\nIiIiImtJI4TAM2at/ZK19jpr7XU9PT1+N0dERERERKThNUIIrPT0tS2wv1I+vgptERERERERWdMa\n4RERu731Qvf8bfXWC90zKCLSEB588EG/myAiIiKyqEYIgY946zuNMYH6GUKNMa3AzUAG+L4fjRMR\nOV3xeNzvJoiIiIgsyvfhoNbaV4HvAJuBX5+z+/eBBPBla216lZsmIrIkn//85/n85z/vdzNERERE\nTqkRegIBPgTsBP7CGHM78FPgdbhnCO4BftfHtomInJavfe1rAHzoQx/yuSUiIiIiC/O9JxCqvYHX\nAffhwt9HgQuA/wrcaK0d8a91IiIiIiIia0ej9ARirT0E/LLf7RAREREREVnLGqInUERERERERFaH\nQqCIiIiIiMg6Yqy1frdhWRhjhoCDfrdjHt3AsN+NEF/o3K9POu/rl879+qVzv37p3K9fjXruz7PW\n9ixWac2EwEZljPmxtfY6v9shq0/nfn3SeV+/dO7XL5379Uvnfv1q9nOv4aAiIiIiIiLriEKgiIiI\n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\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "term_freqs = unigram_corpus.get_term_freq_df()\n", "data = []\n", "for sigma in np.linspace(0.01, 30, 40):\n", " scores = LogOddsRatioSmoothedZScorePrior(priors, sigma).get_scores(term_freqs['Positive freq'], term_freqs['Negative freq'])\n", " for term in ['and', 'best', 'winning', 'of', 'fun']:\n", " data.append({'term': term, 'score': scores[term], 'sigma': sigma})\n", "ax = (pd.DataFrame(data)\n", " .pivot(index='sigma', columns='term', values='score')\n", " .plot(figsize=(15, 8), fontsize=20))\n", "ax.legend(prop={'size': 20})\n", "ax.set_xlabel('$\\sigma_{class\\ size}$', size= 20)\n", "ax.axvline(10, color='k', linestyle='--')\n", "ax.axhline(1.96, color='k', linestyle='--')\n", "ax.set_ylabel('Z-Score', size= 20)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The method suggested in Monroe et al., on the other hand, is more restrictive when it comes to stop words, strongly minimizing the word \"and\", in particular. The words \"fun\" and \"entertaining\" end up being below the signficance threshold." ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mean_doc_length = rdf.text.apply(lambda x: len(st.whitespace_nlp(x).sents[0])).mean()\n", "html = st.produce_fightin_words_explorer(\n", " unigram_corpus,\n", " category='Positive',\n", " not_category_name='Negative',\n", " not_categories=['Negative'],\n", " term_scorer=st.LogOddsRatioInformativeDirichletPrior(priors, mean_doc_length, 'word'), \n", " metadata = rdf['movie_name']\n", ")\n", "file_name = 'output/rotten_fresh_loridp_monroe.html'\n", "open(file_name, 'wb').write(html.encode('utf-8'))\n", "IFrame(src=file_name, width = 1300, height=700)" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "html = st.produce_fightin_words_explorer(\n", " unigram_corpus,\n", " category='Positive',\n", " not_category_name='Negative',\n", " not_categories=['Negative'],\n", " term_scorer=st.LogOddsRatioInformativeDirichletPrior(priors, 1, 'none'), \n", " metadata = rdf['movie_name']\n", ")\n", "file_name = 'output/rotten_fresh_jurfsky.html'\n", "open(file_name, 'wb').write(html.encode('utf-8'))\n", "IFrame(src=file_name, width = 1300, height=700)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Dense rank differences\n", "### Prefered, but may under-score extremely frequent and infrequent terms\n", "\n", "Let $\\rho_\\mbox{category}$ be the dense rank function for a given category, with the most frequently occuring term(s) having a rank of 0.\n", "\n", "Let $d_\\mbox{category}$ be the number of distinct term frequencies in a category. The most infrequenty terms receive this rank.\n", "\n", "This metric takes the difference of the normalized category-specific dense ranks of term frequencies. \n", "\n", "Define the rank-difference score as the difference in \n", "$$\\mbox{Rank-Difference} = \\frac{\\rho_{a}(y_a)}{d_a} - \\frac{\\rho_{b}(y_b)}{d_b}$$\n", "\n", "We can see that the words which occur fairly frequently are favored. Frequent and very infrequent words are heavily regularized.\n", "\n", "No background frequencies are required!" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "class RankDifference:\n", " def get_scores(self, a, b): \n", " return rankdata(a,'dense')/np.max(rankdata(a,'dense')) - rankdata(b,'dense')/np.max(rankdata(b,'dense'))\n", " def get_name(self): \n", " return 'Rank Difference'\n", " \n", "html = st.produce_fightin_words_explorer(\n", " corpus,\n", " category='Positive',\n", " not_category_name='Negative',\n", " not_categories=['Negative'],\n", " term_scorer=RankDifference(),\n", " metadata = rdf['movie_name'],\n", " grey_threshold=0\n", ")\n", "file_name = 'output/rotten_fresh_rankdiff.html'\n", "open(file_name, 'wb').write(html.encode('utf-8'))\n", "IFrame(src=file_name, width = 1300, height=700)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Scaled F-Score\n", "### Associatied terms have a *relatively* high category-specific precision and category-specific term frequency (i.e., % of terms in category are term)\n", "### Take the harmonic mean of precision and frequency (both have to be high)\n", "\n", "Given a word $w_i \\in W$ and a category $c_j \\in C$, define the precision of the word $w_i$ wrt to a category as:\n", "$$ \\mbox{prec}(w_i, c_j) = \\frac{\\#(w_i, c_j)}{\\sum_{c \\in C} \\#(w_i, c)}. $$\n", "\n", "The function $\\#(w_i, c_j)$ represents either the number of times $w_i$ occurs in a document labeled with the category $c_j$ or the number of documents labeled $c_j$ which contain $w_i$.\n", "\n", "Similarly, define the frequency a word occurs in the category as:\n", "\n", "$$ \\mbox{freq}(w_i, c_j) = \\frac{\\#(w_i, c_j)}{\\sum_{w \\in W} \\#(w, c_j)}. $$\n", "\n", "The F-Score of these two values is defined as:\n", "\n", "$$ \\mathcal{F}_\\beta(\\mbox{prec}, \\mbox{freq}) = (1 + \\beta^2) \\frac{\\mbox{prec} \\cdot \\mbox{freq}}{\\beta^2 \\cdot \\mbox{prec} + \\mbox{freq}}. $$\n", "\n", "$\\beta \\in \\mathcal{R}^+$ is a scaling factor where frequency is favored if $\\beta < 1$, precision if $\\beta > 1$, and both are equally weighted if $\\beta = 1$. F-Score is equivalent to the harmonic mean where $\\beta = 1$." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Positive freqNegative freqpos_precisionpos_freq_pctpos_hmean
term
the234622880.5062580.0247350.047166
a177516130.5239080.0187150.036139
and163711790.5813210.0172600.033524
of148012350.5451200.0156040.030340
to94210100.4825820.0099320.019463
it8268010.5076830.0087090.017124
is8187260.5297930.0086250.016973
s8087490.5189470.0085190.016763
in6766220.5208010.0071270.014062
that6176020.5061530.0065050.012846
\n", "
" ], "text/plain": [ " Positive freq Negative freq pos_precision pos_freq_pct pos_hmean\n", "term \n", "the 2346 2288 0.506258 0.024735 0.047166\n", "a 1775 1613 0.523908 0.018715 0.036139\n", "and 1637 1179 0.581321 0.017260 0.033524\n", "of 1480 1235 0.545120 0.015604 0.030340\n", "to 942 1010 0.482582 0.009932 0.019463\n", "it 826 801 0.507683 0.008709 0.017124\n", "is 818 726 0.529793 0.008625 0.016973\n", "s 808 749 0.518947 0.008519 0.016763\n", "in 676 622 0.520801 0.007127 0.014062\n", "that 617 602 0.506153 0.006505 0.012846" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from scipy.stats import hmean\n", "\n", "term_freq_df = corpus.get_term_freq_df()[['Positive freq', 'Negative freq']]\n", "term_freq_df = term_freq_df[term_freq_df.sum(axis=1) > 0]\n", "\n", "term_freq_df['pos_precision'] = (term_freq_df['Positive freq'] * 1./\n", " (term_freq_df['Positive freq'] + term_freq_df['Negative freq']))\n", "\n", "term_freq_df['pos_freq_pct'] = (term_freq_df['Positive freq'] * 1.\n", " /term_freq_df['Positive freq'].sum())\n", "\n", "term_freq_df['pos_hmean'] = (term_freq_df\n", " .apply(lambda x: (hmean([x['pos_precision'], x['pos_freq_pct']])\n", " if x['pos_precision'] > 0 and x['pos_freq_pct'] > 0 \n", " else 0), axis=1))\n", "term_freq_df.sort_values(by='pos_hmean', ascending=False).iloc[:10]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Problem: harmonic means are dominated by the precision\n", "### Take the normal CDF of precision and frequency percentage scores, which will fall between 0 and 1, which scales and standardizes both scores.\n", "\n", "Define the the Normal CDF as:\n", "\n", "$$ \\Phi(z) = \\int_{-\\infty}^z \\mathcal{N}(x; \\mu, \\sigma^2)\\ \\mathrm{d}x.$$\n", "\n", "Where $ \\mathcal{N} $ is the PDF of the Normal distribution, $\\mu$ is the mean, and $\\sigma^2$ is the variance.\n", "\n", "$\\Phi$ is used to scale and standardize the precisions and frequencies, and place them on the same scale $[0,1]$.\n", "\n", "Now we can define Scaled F-Score as the harmonic mean of the Normal CDF transformed frequency and precision:\n", "\n", "$$ \\mbox{S-CAT}_{\\beta}(w_i, c_j) = \\mathcal{F}_{\\beta}(\\Phi(\\mbox{prec}(w_i, c_j)), \\Phi(\\mbox{freq}(w_i, c_j))).$$\n", "\n", "$\\mu$ and $\\sigma^2$ are defined separately as the mean and variance of precision and frequency.\n", "\n", "A $\\beta$ of 2 is recommended and is the default value in Scattertext.\n", "\n", "Note that any function with the range of $[0,1]$ (this includes the identity function) may be used in place of $\\Phi$. Also, when the precision is very small (e.g., of a tiny minority class) normalization may be foregone." ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Positive freqNegative freqpos_precisionpos_freq_pctpos_hmeanpos_precision_normcdfpos_freq_pct_normcdfpos_scaled_f_score
term
the best6570.9027780.0006850.0013700.8003290.9998760.889042
entertaining58130.8169010.0006120.0012220.7449740.9994430.853648
heart45110.8035710.0004740.0009480.7357150.9939810.845567
our42110.7924530.0004430.0008850.7278630.9903110.839043
ride2960.8285710.0003060.0006110.7529390.9439920.837710
summer2960.8285710.0003060.0006110.7529390.9439920.837710
flaws1910.9500000.0002000.0004010.8274140.8444830.835861
moore1910.9500000.0002000.0004010.8274140.8444830.835861
hilarious2860.8235290.0002950.0005900.7495140.9371880.832910
delivers2550.8333330.0002640.0005270.7561500.9128860.827159
\n", "
" ], "text/plain": [ " Positive freq Negative freq pos_precision pos_freq_pct \\\n", "term \n", "the best 65 7 0.902778 0.000685 \n", "entertaining 58 13 0.816901 0.000612 \n", "heart 45 11 0.803571 0.000474 \n", "our 42 11 0.792453 0.000443 \n", "ride 29 6 0.828571 0.000306 \n", "summer 29 6 0.828571 0.000306 \n", "flaws 19 1 0.950000 0.000200 \n", "moore 19 1 0.950000 0.000200 \n", "hilarious 28 6 0.823529 0.000295 \n", "delivers 25 5 0.833333 0.000264 \n", "\n", " pos_hmean pos_precision_normcdf pos_freq_pct_normcdf \\\n", "term \n", "the best 0.001370 0.800329 0.999876 \n", "entertaining 0.001222 0.744974 0.999443 \n", "heart 0.000948 0.735715 0.993981 \n", "our 0.000885 0.727863 0.990311 \n", "ride 0.000611 0.752939 0.943992 \n", "summer 0.000611 0.752939 0.943992 \n", "flaws 0.000401 0.827414 0.844483 \n", "moore 0.000401 0.827414 0.844483 \n", "hilarious 0.000590 0.749514 0.937188 \n", "delivers 0.000527 0.756150 0.912886 \n", "\n", " pos_scaled_f_score \n", "term \n", "the best 0.889042 \n", "entertaining 0.853648 \n", "heart 0.845567 \n", "our 0.839043 \n", "ride 0.837710 \n", "summer 0.837710 \n", "flaws 0.835861 \n", "moore 0.835861 \n", "hilarious 0.832910 \n", "delivers 0.827159 " ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from scipy.stats import norm\n", "\n", "def normcdf(x):\n", " return norm.cdf(x, x.mean(), x.std())\n", "\n", "term_freq_df['pos_precision_normcdf'] = normcdf(term_freq_df['pos_precision'])\n", "\n", "term_freq_df['pos_freq_pct_normcdf'] = normcdf(term_freq_df['pos_freq_pct'])\n", "\n", "term_freq_df['pos_scaled_f_score'] = hmean([term_freq_df['pos_precision_normcdf'], term_freq_df['pos_freq_pct_normcdf']])\n", "\n", "term_freq_df.sort_values(by='pos_scaled_f_score', ascending=False).iloc[:10]\n" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Positive freqNegative freqpos_precisionpos_freq_pctpos_hmeanpos_precision_normcdfpos_freq_pct_normcdfpos_scaled_f_score
term
bill perfectly010.00.00.00.1374360.4674720.212421
shoplifts010.00.00.00.1374360.4674720.212421
encyclopedia010.00.00.00.1374360.4674720.212421
homo eroticism010.00.00.00.1374360.4674720.212421
trade homo010.00.00.00.1374360.4674720.212421
rough trade020.00.00.00.1374360.4674720.212421
on rough010.00.00.00.1374360.4674720.212421
machismo in010.00.00.00.1374360.4674720.212421
gang machismo010.00.00.00.1374360.4674720.212421
teen gang010.00.00.00.1374360.4674720.212421
\n", "
" ], "text/plain": [ " Positive freq Negative freq pos_precision pos_freq_pct \\\n", "term \n", "bill perfectly 0 1 0.0 0.0 \n", "shoplifts 0 1 0.0 0.0 \n", "encyclopedia 0 1 0.0 0.0 \n", "homo eroticism 0 1 0.0 0.0 \n", "trade homo 0 1 0.0 0.0 \n", "rough trade 0 2 0.0 0.0 \n", "on rough 0 1 0.0 0.0 \n", "machismo in 0 1 0.0 0.0 \n", "gang machismo 0 1 0.0 0.0 \n", "teen gang 0 1 0.0 0.0 \n", "\n", " pos_hmean pos_precision_normcdf pos_freq_pct_normcdf \\\n", "term \n", "bill perfectly 0.0 0.137436 0.467472 \n", "shoplifts 0.0 0.137436 0.467472 \n", "encyclopedia 0.0 0.137436 0.467472 \n", "homo eroticism 0.0 0.137436 0.467472 \n", "trade homo 0.0 0.137436 0.467472 \n", "rough trade 0.0 0.137436 0.467472 \n", "on rough 0.0 0.137436 0.467472 \n", "machismo in 0.0 0.137436 0.467472 \n", "gang machismo 0.0 0.137436 0.467472 \n", "teen gang 0.0 0.137436 0.467472 \n", "\n", " pos_scaled_f_score \n", "term \n", "bill perfectly 0.212421 \n", "shoplifts 0.212421 \n", "encyclopedia 0.212421 \n", "homo eroticism 0.212421 \n", "trade homo 0.212421 \n", "rough trade 0.212421 \n", "on rough 0.212421 \n", "machismo in 0.212421 \n", "gang machismo 0.212421 \n", "teen gang 0.212421 " ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "term_freq_df.sort_values(by='pos_scaled_f_score', ascending=True).iloc[:10]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## A second problem: low-scores are infrequent, low precision words.\n", "### Solution: compute SFS of negative class. If that score has a higher magnitude than the positive SFS, keep that, but as a negative score.\n", "\n", "Define the Scaled F-Score for category $j$ as\n", "$$ \\mbox{S-CAT}^{j} = \\mbox{S-CAT}_{\\beta}(w_i, c_j). $$\n", "\n", "Define a class $\\neg j$ which includes all categories other than $j$.\n", "\n", "and the Scaled F-Score for all other categories as\n", "$$ \\mbox{S-CAT}^{\\neg j} = \\mbox{S-CAT}_{\\beta}(w_i, c_{\\neg j}). $$\n", "\n", "Let the corrected version of Scaled F-Score be:\n", "\n", "$$\\mathcal{S}_{\\beta} = 2 \\cdot \\big(-0.5 + \\begin{cases}\n", " \\mbox{S-CAT}^{j} & \\text{if}\\ \\mbox{S-CAT}^{j} > \\mbox{S-CAT}^{\\neg j}, \\\\\n", " 1 - \\mbox{S-CAT}^{\\neg j} & \\text{if}\\ \\mbox{S-CAT}^{j} < \\mbox{S-CAT}^{\\neg j}, \\\\\n", " 0 & \\text{otherwise}.\n", " \\end{cases} \\big).$$\n", " \n", "Note that the range of $\\mathcal{S}$ is now $[-1, 1]$, where $\\mathcal{S} < 0$ indicates a term less associated with the category is question than average, and a positive score being more associated." ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Positive freqNegative freqpos_precisionpos_freq_pctpos_hmeanpos_precision_normcdfpos_freq_pct_normcdfpos_scaled_f_scoreneg_precisionneg_freq_pctneg_scaled_f_scorescaled_f_score
term
the best6570.9027780.0006850.0013700.8003290.9998760.8890420.0972220.0000770.3040900.778085
entertaining58130.8169010.0006120.0012220.7449740.9994430.8536480.1830990.0001420.3826660.707296
heart45110.8035710.0004740.0009480.7357150.9939810.8455670.1964290.0001200.3875290.691134
our42110.7924530.0004430.0008850.7278630.9903110.8390430.2075470.0001200.3959040.678086
summer2960.8285710.0003060.0006110.7529390.9439920.8377100.1714290.0000660.3523120.675421
ride2960.8285710.0003060.0006110.7529390.9439920.8377100.1714290.0000660.3523120.675421
flaws1910.9500000.0002000.0004010.8274140.8444830.8358610.0500000.0000110.2553880.671722
moore1910.9500000.0002000.0004010.8274140.8444830.8358610.0500000.0000110.2553880.671722
hilarious2860.8235290.0002950.0005900.7495140.9371880.8329100.1764710.0000660.3557810.665820
delivers2550.8333330.0002640.0005270.7561500.9128860.8271590.1666670.0000550.3450300.654317
\n", "
" ], "text/plain": [ " Positive freq Negative freq pos_precision pos_freq_pct \\\n", "term \n", "the best 65 7 0.902778 0.000685 \n", "entertaining 58 13 0.816901 0.000612 \n", "heart 45 11 0.803571 0.000474 \n", "our 42 11 0.792453 0.000443 \n", "summer 29 6 0.828571 0.000306 \n", "ride 29 6 0.828571 0.000306 \n", "flaws 19 1 0.950000 0.000200 \n", "moore 19 1 0.950000 0.000200 \n", "hilarious 28 6 0.823529 0.000295 \n", "delivers 25 5 0.833333 0.000264 \n", "\n", " pos_hmean pos_precision_normcdf pos_freq_pct_normcdf \\\n", "term \n", "the best 0.001370 0.800329 0.999876 \n", "entertaining 0.001222 0.744974 0.999443 \n", "heart 0.000948 0.735715 0.993981 \n", "our 0.000885 0.727863 0.990311 \n", "summer 0.000611 0.752939 0.943992 \n", "ride 0.000611 0.752939 0.943992 \n", "flaws 0.000401 0.827414 0.844483 \n", "moore 0.000401 0.827414 0.844483 \n", "hilarious 0.000590 0.749514 0.937188 \n", "delivers 0.000527 0.756150 0.912886 \n", "\n", " pos_scaled_f_score neg_precision neg_freq_pct \\\n", "term \n", "the best 0.889042 0.097222 0.000077 \n", "entertaining 0.853648 0.183099 0.000142 \n", "heart 0.845567 0.196429 0.000120 \n", "our 0.839043 0.207547 0.000120 \n", "summer 0.837710 0.171429 0.000066 \n", "ride 0.837710 0.171429 0.000066 \n", "flaws 0.835861 0.050000 0.000011 \n", "moore 0.835861 0.050000 0.000011 \n", "hilarious 0.832910 0.176471 0.000066 \n", "delivers 0.827159 0.166667 0.000055 \n", "\n", " neg_scaled_f_score scaled_f_score \n", "term \n", "the best 0.304090 0.778085 \n", "entertaining 0.382666 0.707296 \n", "heart 0.387529 0.691134 \n", "our 0.395904 0.678086 \n", "summer 0.352312 0.675421 \n", "ride 0.352312 0.675421 \n", "flaws 0.255388 0.671722 \n", "moore 0.255388 0.671722 \n", "hilarious 0.355781 0.665820 \n", "delivers 0.345030 0.654317 " ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "term_freq_df['neg_precision'] = (term_freq_df['Negative freq'] * 1./\n", " (term_freq_df['Negative freq'] + term_freq_df['Positive freq']))\n", "\n", "term_freq_df['neg_freq_pct'] = (term_freq_df['Negative freq'] * 1.\n", " /term_freq_df['Negative freq'].sum())\n", "\n", "term_freq_df['neg_scaled_f_score'] = hmean([normcdf(term_freq_df['neg_precision']), normcdf(term_freq_df['neg_freq_pct'])])\n", "\n", "term_freq_df['scaled_f_score'] = 0\n", "term_freq_df.loc[term_freq_df['pos_scaled_f_score'] > term_freq_df['neg_scaled_f_score'], \n", " 'scaled_f_score'] = term_freq_df['pos_scaled_f_score']\n", "term_freq_df.loc[term_freq_df['pos_scaled_f_score'] < term_freq_df['neg_scaled_f_score'], \n", " 'scaled_f_score'] = 1-term_freq_df['neg_scaled_f_score']\n", "term_freq_df['scaled_f_score'] = 2 * (term_freq_df['scaled_f_score'] - 0.5)\n", "term_freq_df.sort_values(by='scaled_f_score', ascending=False).iloc[:10]" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Positive freqNegative freqpos_precisionpos_freq_pctpos_hmeanpos_precision_normcdfpos_freq_pct_normcdfpos_scaled_f_scoreneg_precisionneg_freq_pctneg_scaled_f_scorescaled_f_score
term
bad171050.1393440.0001790.0003580.2138050.8153600.3387760.8606560.0011490.880301-0.760603
worst2230.0800000.0000210.0000420.1786650.5134040.2650810.9200000.0002520.864072-0.728143
nor3250.1071430.0000320.0000630.1942620.5363410.2852180.8928570.0002740.863366-0.726733
a bad2220.0833330.0000210.0000420.1805370.5134040.2671360.9166670.0002410.858329-0.716659
neither3240.1111110.0000320.0000630.1966100.5363410.2877410.8888890.0002630.858224-0.716448
instead7320.1794870.0000740.0001480.2397030.6261520.3466870.8205130.0003500.853228-0.706456
obvious3230.1153850.0000320.0000630.1991580.5363410.2904600.8846150.0002520.852596-0.705191
mess1190.0500000.0000110.0000210.1623720.4904220.2439690.9500000.0002080.850818-0.701637
boring0170.0000000.0000000.0000000.1374360.4674720.2124211.0000000.0001860.848754-0.697508
the only3220.1200000.0000320.0000630.2019310.5363410.2933980.8800000.0002410.846446-0.692891
\n", "
" ], "text/plain": [ " Positive freq Negative freq pos_precision pos_freq_pct \\\n", "term \n", "bad 17 105 0.139344 0.000179 \n", "worst 2 23 0.080000 0.000021 \n", "nor 3 25 0.107143 0.000032 \n", "a bad 2 22 0.083333 0.000021 \n", "neither 3 24 0.111111 0.000032 \n", "instead 7 32 0.179487 0.000074 \n", "obvious 3 23 0.115385 0.000032 \n", "mess 1 19 0.050000 0.000011 \n", "boring 0 17 0.000000 0.000000 \n", "the only 3 22 0.120000 0.000032 \n", "\n", " pos_hmean pos_precision_normcdf pos_freq_pct_normcdf \\\n", "term \n", "bad 0.000358 0.213805 0.815360 \n", "worst 0.000042 0.178665 0.513404 \n", "nor 0.000063 0.194262 0.536341 \n", "a bad 0.000042 0.180537 0.513404 \n", "neither 0.000063 0.196610 0.536341 \n", "instead 0.000148 0.239703 0.626152 \n", "obvious 0.000063 0.199158 0.536341 \n", "mess 0.000021 0.162372 0.490422 \n", "boring 0.000000 0.137436 0.467472 \n", "the only 0.000063 0.201931 0.536341 \n", "\n", " pos_scaled_f_score neg_precision neg_freq_pct neg_scaled_f_score \\\n", "term \n", "bad 0.338776 0.860656 0.001149 0.880301 \n", "worst 0.265081 0.920000 0.000252 0.864072 \n", "nor 0.285218 0.892857 0.000274 0.863366 \n", "a bad 0.267136 0.916667 0.000241 0.858329 \n", "neither 0.287741 0.888889 0.000263 0.858224 \n", "instead 0.346687 0.820513 0.000350 0.853228 \n", "obvious 0.290460 0.884615 0.000252 0.852596 \n", "mess 0.243969 0.950000 0.000208 0.850818 \n", "boring 0.212421 1.000000 0.000186 0.848754 \n", "the only 0.293398 0.880000 0.000241 0.846446 \n", "\n", " scaled_f_score \n", "term \n", "bad -0.760603 \n", "worst -0.728143 \n", "nor -0.726733 \n", "a bad -0.716659 \n", "neither -0.716448 \n", "instead -0.706456 \n", "obvious -0.705191 \n", "mess -0.701637 \n", "boring -0.697508 \n", "the only -0.692891 " ] }, "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], "source": [ "term_freq_df.sort_values(by='scaled_f_score', ascending=True).iloc[:10]" ] }, { "cell_type": "code", "execution_count": 62, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 62, "metadata": {}, "output_type": "execute_result" } ], "source": [ "html = st.produce_fightin_words_explorer(\n", " corpus,\n", " category='Positive',\n", " not_category_name='Negative',\n", " not_categories=['Negative'],\n", " term_scorer=st.ScaledFScorePresets(beta=1, one_to_neg_one=True),\n", " metadata = rdf['movie_name'],\n", " grey_threshold=0\n", ")\n", "file_name = 'output/rotten_fresh_scaled_f_score.html'\n", "open(file_name, 'wb').write(html.encode('utf-8'))\n", "IFrame(src=file_name, width = 1300, height=700)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### The Semiotic Square using RankDiff and phrases from PhraseMachine" ] }, { "cell_type": "code", "execution_count": 67, "metadata": {}, "outputs": [], "source": [ "nlp = spacy.load('en')" ] }, { "cell_type": "code", "execution_count": 68, "metadata": {}, "outputs": [], "source": [ "rdf['parse'] = rdf.text.apply(nlp)" ] }, { "cell_type": "code", "execution_count": 72, "metadata": {}, "outputs": [], "source": [ "phrase_corpus = (st.CorpusFromParsedDocuments(rdf, \n", " category_col='category_name', \n", " parsed_col='parse',\n", " feats_from_spacy_doc = st.PhraseMachinePhrases())\n", " .build())" ] }, { "cell_type": "code", "execution_count": 86, "metadata": {}, "outputs": [], "source": [ "phrase_corpus_compact = phrase_corpus.compact(st.CompactTerms(minimum_term_count=2))" ] }, { "cell_type": "code", "execution_count": 98, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 98, "metadata": {}, "output_type": "execute_result" } ], "source": [ "semiotic_square = st.SemioticSquare(\n", " phrase_corpus_compact,\n", " category_a='Positive',\n", " category_b='Negative',\n", " neutral_categories=['Plot'],\n", " scorer=st.RankDifference(),\n", " labels = {'a_and_b': 'Reviews',\n", " 'not_a_and_not_b': 'Plot Descriptions',\n", " 'a_and_not_b': 'Positive/Plot',\n", " 'b_and_not_a': 'Negative/Plot',\n", " }\n", ")\n", "\n", "html = st.produce_semiotic_square_explorer(semiotic_square,\n", " category_name='Positive',\n", " not_category_name='Negative',\n", " x_label='Positive-Negative',\n", " y_label='Review-Plot',\n", " minimum_term_frequency=2,\n", " pmi_threshold_coefficient=0,\n", " neutral_category_name='Plot Description',\n", " metadata=rdf['movie_name'])\n", "\n", "fn = 'semiotic_square_rankdiff.html'\n", "open(fn, 'wb').write(html.encode('utf-8'))\n", "IFrame(src=fn, width = 1600, height=900)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "unigram_corpus_compact = unigram_corpus.compact(st.ClassPercentageCompactor(term_count=2))" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1730993" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "unigram_semiotic_square = st.SemioticSquare(\n", " unigram_corpus_compact,\n", " category_a='Positive',\n", " category_b='Negative',\n", " neutral_categories=['Plot'],\n", " scorer=st.RankDifference(),\n", " labels = {'a_and_b': 'Reviews',\n", " 'not_a_and_not_b': 'Plot Descriptions',\n", " 'a_and_not_b': 'Positive/Plot',\n", " 'b_and_not_a': 'Negative/Plot',\n", " }\n", ")\n", "\n", "html = st.produce_semiotic_square_explorer(unigram_semiotic_square,\n", " category_name='Positive',\n", " not_category_name='Negative',\n", " x_label='Positive-Negative',\n", " y_label='Review-Plot',\n", " minimum_term_frequency=2,\n", " pmi_threshold_coefficient=0,\n", " neutral_category_name='Plot Description',\n", " metadata=rdf['movie_name'])\n", "\n", "fn = 'semiotic_square_rankdiff_unigram.html'\n", "open(fn, 'wb').write(html.encode('utf-8'))" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "IFrame(src=fn, width = 1600, height=900)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "anaconda-cloud": { "attach-environment": true, "environment": "Root", "summary": "Class Association Scores with Scattertext" }, "kernelspec": { "display_name": "Python [py36]", "language": "python", "name": "Python [py36]" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.1" } }, "nbformat": 4, "nbformat_minor": 2 }