{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Understanding Movies Using LSA" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import numpy as np\n", "import pandas as pd\n", "import math\n", "import random\n", "import seaborn as sns\n", "import matplotlib.pyplot as plt\n", "from matplotlib import colors\n", "from sklearn.decomposition import NMF\n", "from sklearn.feature_extraction.text import CountVectorizer\n", "from sklearn.feature_extraction.text import TfidfTransformer\n", "from sklearn.metrics.pairwise import cosine_similarity\n", "\n", "% matplotlib inline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Loading the Data\n", "\n", "I've left the data in single files for each year, so that an enthusiastic person of the future could add extra years' data easily." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [], "source": [ "dfs = []\n", "for year in range(1940, 2018):\n", " dfs.append(pd.read_csv('scraped_movies/top_movies_of_%d.csv' % year, encoding = 'cp1252'))\n", "movie_data = pd.concat(dfs)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [], "source": [ "dfs = []\n", "for year in range(1940, 2018):\n", " dfs.append(pd.read_csv('scraped_movies/keywords_for_top_movies_of_%d.csv' % year, encoding = 'cp1252'))\n", "keywords = pd.concat(dfs)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": true }, "outputs": [], "source": [ "movie_data.index = range(len(movie_data))\n", "keywords.index = range(len(keywords))" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "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", "
IMDbIdIMDb_scorebox_office_rankgenre_listrelease_yeartitle
0tt00329107.51.0Animation, Comedy, Family1940Pinocchio (1940)
1tt00324557.82.0Animation, Family, Fantasy1940Fantasia (1940)
2tt00323846.83.0Drama1940De Mayerling � Sarajevo (1940)
3tt00329768.24.0Drama, Mystery, Romance1940Rebecca (1940)
4tt00325538.55.0Comedy, Drama, War1940The Great Dictator (1940)
\n", "
" ], "text/plain": [ " IMDbId IMDb_score box_office_rank genre_list \\\n", "0 tt0032910 7.5 1.0 Animation, Comedy, Family \n", "1 tt0032455 7.8 2.0 Animation, Family, Fantasy \n", "2 tt0032384 6.8 3.0 Drama \n", "3 tt0032976 8.2 4.0 Drama, Mystery, Romance \n", "4 tt0032553 8.5 5.0 Comedy, Drama, War \n", "\n", " release_year title \n", "0 1940 Pinocchio (1940) \n", "1 1940 Fantasia (1940) \n", "2 1940 De Mayerling � Sarajevo (1940) \n", "3 1940 Rebecca (1940) \n", "4 1940 The Great Dictator (1940) " ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "movie_data.head()" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "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", "
IMDbIdkeywords
0tt0032910italy|pinocchio|jiminy-cricket|puppet|conscien...
1tt0032455classical-music|disney|mouse|orchestra|apprent...
2tt0032384NaN
3tt0032976bride|widower|estate|monte-carlo|housekeeper|w...
4tt0032553invented-language|dual-role|fictitious-country...
\n", "
" ], "text/plain": [ " IMDbId keywords\n", "0 tt0032910 italy|pinocchio|jiminy-cricket|puppet|conscien...\n", "1 tt0032455 classical-music|disney|mouse|orchestra|apprent...\n", "2 tt0032384 NaN\n", "3 tt0032976 bride|widower|estate|monte-carlo|housekeeper|w...\n", "4 tt0032553 invented-language|dual-role|fictitious-country..." ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "keywords.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can see there's some movies missing keywords, and probably a bunch of missing data elsewhere too. I'm basically ignoring that at the moment." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Lookup objects\n", "\n", "For convenience, I'm defining here a bunch of lookups I'll later use to filter the datasets - they're mostly boolean series, or dicts." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [], "source": [ "marvel_lookup = keywords.keywords.fillna('').str.contains('marvel-cinematic-universe')" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [], "source": [ "title_lookup = pd.Series(movie_data.title)\n", "title_lookup.index = movie_data.IMDbId\n", "title_lookup = title_lookup.to_dict()\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [], "source": [ "furiouses = ['The Fast and the Furious (2001)',\n", "'2 Fast 2 Furious (2003)',\n", "'The Fast and the Furious: Tokyo Drift (2006)',\n", "'Fast & Furious (2009)',\n", "'Fast Five (2011)',\n", "'Furious 6 (2013)',\n", "'Furious Seven (2015)',\n", "'The Fate of the Furious (2017)']\n", "\n", "furious_lookup = keywords.IMDbId.map(title_lookup).isin(furiouses)" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [], "source": [ "aliens = ['Alien (1979)',\n", " 'Aliens (1986)',\n", " 'Alien³ (1992)',\n", " 'Alien Resurrection (1997)',\n", " 'AVP: Alien vs. Predator (2004)',\n", " 'Prometheus (I) (2012)',\n", " 'Alien: Covenant (2017)'\n", "]\n", "\n", "aliens_lookup = keywords.IMDbId.map(title_lookup).isin(aliens)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": true }, "outputs": [], "source": [ "princess_lookup = keywords.keywords.fillna('').str.contains('disney-princess')" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": true }, "outputs": [], "source": [ "jaws_lookup = keywords.IMDbId.map(title_lookup).apply(lambda x: 'jaws' in x.lower())" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": true }, "outputs": [], "source": [ "\n", "\n", "decade_lookup = pd.DataFrame(movie_data.release_year.apply(lambda x: math.floor(x/10)*10))\n", "decade_lookup.index = movie_data.IMDbId\n", "decade_lookup = decade_lookup.to_dict()['release_year']" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": true }, "outputs": [], "source": [ "chocula = CountVectorizer(tokenizer = lambda x: x.split(', '))\n", "genres = chocula.fit_transform(movie_data.genre_list.fillna('xxx')).toarray()\n", "genre_lookup = pd.DataFrame(genres, columns = chocula.get_feature_names())\n", "genre_lookup.index = movie_data.IMDbId" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": true }, "outputs": [], "source": [ "rank_lookup = pd.Series(movie_data.box_office_rank)\n", "rank_lookup.index = movie_data.IMDbId\n", "rank_lookup = rank_lookup.to_dict()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Process Example\n", "\n", "I'm running through the whole transformation with a sample of the data, to demonstrate what it looks like at each stage.\n", "\n", "We're transforming our list of movies and keywords into a list of movies and a set of columns storing an abstract numeric represenation of that movie, derived from its similarity to other movies, in terms of their shared keywords. The steps are:\n", "\n", "- \"Bag of words\" encoding - making a table with a column for each keyword\n", "- \"TFIDF\" weighting - weighting each movie's keyword entries to account for movies with more or fewer total keywords\n", "- Taking the \"Dot Product\" - Creating a matrix comparing each movie to each other movie, in terms of keyword similarity.\n", "- Applying \"Dimensionality Reduction\" - reducing the number of columns in the matrix from one per movie to a smaller number of abstract values." ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": false }, "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", "
IMDbIdkeywords
4608Ruthless People (1986)kidnapping, ransom, extramarital-affair, polic...
1408Dial M for Murder (1954)blackmail, murder, letter, writer, marriage, e...
602Duel in the Sun (1946)half-breed, infidelity, extramarital-affair, b...
903White Heat (1949)psychopath, transmitter, oscillator, trojan-ho...
3306Lost Horizon (1973)based-on-novel, shangri-la, diplomat, remake, ...
7203Skyfall (2012)007, terrorist-cell, intelligence-agency, comp...
5703Liar Liar (1997)birthday-wish, female-stockinged-feet, pantyho...
2406Kissin' Cousins (1964)foot-fetish, lookalike, identical-cousins, son...
604The Yearling (1946)boy, deer, fawn, pet, death, children, florida...
4304WarGames (1983)game, computer, norad, teenager, high-school, ...
\n", "
" ], "text/plain": [ " IMDbId \\\n", "4608 Ruthless People (1986) \n", "1408 Dial M for Murder (1954) \n", "602 Duel in the Sun (1946) \n", "903 White Heat (1949) \n", "3306 Lost Horizon (1973) \n", "7203 Skyfall (2012) \n", "5703 Liar Liar (1997) \n", "2406 Kissin' Cousins (1964) \n", "604 The Yearling (1946) \n", "4304 WarGames (1983) \n", "\n", " keywords \n", "4608 kidnapping, ransom, extramarital-affair, polic... \n", "1408 blackmail, murder, letter, writer, marriage, e... \n", "602 half-breed, infidelity, extramarital-affair, b... \n", "903 psychopath, transmitter, oscillator, trojan-ho... \n", "3306 based-on-novel, shangri-la, diplomat, remake, ... \n", "7203 007, terrorist-cell, intelligence-agency, comp... \n", "5703 birthday-wish, female-stockinged-feet, pantyho... \n", "2406 foot-fetish, lookalike, identical-cousins, son... \n", "604 boy, deer, fawn, pet, death, children, florida... \n", "4304 game, computer, norad, teenager, high-school, ... " ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sample = keywords[(keywords.IMDbId.map(rank_lookup) < 10)& # Make sure they're movies someone has heard of\n", " (keywords.IMDbId.map(title_lookup).apply(len) < 25) # Make sure the title's aren't too long to display nicely\n", " ].sample(10)\n", "\n", "pd.DataFrame([sample.IMDbId.map(title_lookup), \n", " sample.keywords.apply(lambda x: ', '.join(str(x).split('|')))]).transpose()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Bag of Words" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Creating a \"Countvectoriser\" object, and a \"TfidifTransformer\", which will do bag-of-words encoding and TFIDF weighting, respectively." ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": false }, "outputs": [], "source": [ "vlad = CountVectorizer(tokenizer = lambda x: x.split('|'), min_df = 0)\n", "\n", "megatron = TfidfTransformer()\n", "\n", "sparse = vlad.fit_transform(pd.Series(keywords.keywords.fillna('').values))\n", "sample_sparse = vlad.fit_transform(pd.Series(sample.keywords.fillna('').values))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Choose some random columns to show" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ns = random.sample(range(len(vlad.get_feature_names())), 5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The sample dataset has been \"bag of words\" encoded - we've created a column for each keyword, and each movie gets a flag for whether it has that keyword or not." ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": false }, "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", "
save-the-worldwarriorhaunted-by-the-pastwriting-on-a-mirrorshoot-the-lock
IMDbId
Ruthless People (1986)00000
Dial M for Murder (1954)00000
Duel in the Sun (1946)00000
White Heat (1949)00011
Lost Horizon (1973)00000
Skyfall (2012)11100
Liar Liar (1997)00000
Kissin' Cousins (1964)00000
The Yearling (1946)00000
WarGames (1983)00000
\n", "
" ], "text/plain": [ " save-the-world warrior haunted-by-the-past \\\n", "IMDbId \n", "Ruthless People (1986) 0 0 0 \n", "Dial M for Murder (1954) 0 0 0 \n", "Duel in the Sun (1946) 0 0 0 \n", "White Heat (1949) 0 0 0 \n", "Lost Horizon (1973) 0 0 0 \n", "Skyfall (2012) 1 1 1 \n", "Liar Liar (1997) 0 0 0 \n", "Kissin' Cousins (1964) 0 0 0 \n", "The Yearling (1946) 0 0 0 \n", "WarGames (1983) 0 0 0 \n", "\n", " writing-on-a-mirror shoot-the-lock \n", "IMDbId \n", "Ruthless People (1986) 0 0 \n", "Dial M for Murder (1954) 0 0 \n", "Duel in the Sun (1946) 0 0 \n", "White Heat (1949) 1 1 \n", "Lost Horizon (1973) 0 0 \n", "Skyfall (2012) 0 0 \n", "Liar Liar (1997) 0 0 \n", "Kissin' Cousins (1964) 0 0 \n", "The Yearling (1946) 0 0 \n", "WarGames (1983) 0 0 " ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pd.DataFrame(sample_sparse.toarray()[:,ns], index=sample.IMDbId.map(title_lookup), columns=[vlad.get_feature_names()[i] for i in ns])" ] }, { "cell_type": "markdown", "metadata": { "collapsed": false }, "source": [ " ### TFIDF Weighting\n", " \n", " Now we apply the TFIDF weighting." ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "collapsed": false }, "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", "
save-the-worldwarriorhaunted-by-the-pastwriting-on-a-mirrorshoot-the-lock
IMDbId
Ruthless People (1986)0.000.000.000.000.00
Dial M for Murder (1954)0.000.000.000.000.00
Duel in the Sun (1946)0.000.000.000.000.00
White Heat (1949)0.000.000.000.070.07
Lost Horizon (1973)0.000.000.000.000.00
Skyfall (2012)0.060.060.060.000.00
Liar Liar (1997)0.000.000.000.000.00
Kissin' Cousins (1964)0.000.000.000.000.00
The Yearling (1946)0.000.000.000.000.00
WarGames (1983)0.000.000.000.000.00
\n", "
" ], "text/plain": [ " save-the-world warrior haunted-by-the-past \\\n", "IMDbId \n", "Ruthless People (1986) 0.00 0.00 0.00 \n", "Dial M for Murder (1954) 0.00 0.00 0.00 \n", "Duel in the Sun (1946) 0.00 0.00 0.00 \n", "White Heat (1949) 0.00 0.00 0.00 \n", "Lost Horizon (1973) 0.00 0.00 0.00 \n", "Skyfall (2012) 0.06 0.06 0.06 \n", "Liar Liar (1997) 0.00 0.00 0.00 \n", "Kissin' Cousins (1964) 0.00 0.00 0.00 \n", "The Yearling (1946) 0.00 0.00 0.00 \n", "WarGames (1983) 0.00 0.00 0.00 \n", "\n", " writing-on-a-mirror shoot-the-lock \n", "IMDbId \n", "Ruthless People (1986) 0.00 0.00 \n", "Dial M for Murder (1954) 0.00 0.00 \n", "Duel in the Sun (1946) 0.00 0.00 \n", "White Heat (1949) 0.07 0.07 \n", "Lost Horizon (1973) 0.00 0.00 \n", "Skyfall (2012) 0.00 0.00 \n", "Liar Liar (1997) 0.00 0.00 \n", "Kissin' Cousins (1964) 0.00 0.00 \n", "The Yearling (1946) 0.00 0.00 \n", "WarGames (1983) 0.00 0.00 " ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "weighted = megatron.fit_transform(sample_sparse)\n", "\n", "pd.DataFrame(weighted.toarray()[:,ns], index=sample.IMDbId.map(title_lookup), columns=[vlad.get_feature_names()[i] for i in ns]).apply(round, args=(2,))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Dot Product\n", "\n", "Now we take the \"Dot product\" of the weighted values - every movie's correlation with every other movie, in terms of keywords in common." ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Cheeky wee method for shading the dataframe nicely. Pretty sure I nicked this offa StackOverflow.\n", "\n", "def background_gradient(s, m, M, cmap='PuBu', low=0, high=0):\n", " rng = M - m\n", " norm = colors.Normalize(m - (rng * low),\n", " M + (rng * high))\n", " normed = norm(s.values)\n", " c = [colors.rgb2hex(x) for x in plt.cm.get_cmap(cmap)(normed)]\n", " return ['background-color: %s' % color for color in c]" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "collapsed": false }, "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", "
IMDbIdRuthless People (1986)Dial M for Murder (1954)Duel in the Sun (1946)White Heat (1949)Lost Horizon (1973)Skyfall (2012)Liar Liar (1997)Kissin' Cousins (1964)The Yearling (1946)WarGames (1983)
IMDbId
Ruthless People (1986)10.030.030.030.010.020.0100.010.04
Dial M for Murder (1954)0.0310.020.030.010.020.010.020.020
Duel in the Sun (1946)0.030.0210.010.0100.020.020.010
White Heat (1949)0.030.030.01100.060.010.010.010.01
Lost Horizon (1973)0.010.010.01010.010.010.040.020.02
Skyfall (2012)0.020.0200.060.0110.0100.020.02
Liar Liar (1997)0.010.010.020.010.010.01100.010
Kissin' Cousins (1964)00.020.020.010.040010.020.02
The Yearling (1946)0.010.020.010.010.020.020.010.0210
WarGames (1983)0.04000.010.020.0200.0201
" ], "text/plain": [ "" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "weighted = megatron.fit_transform(sparse)\n", "the_matrix = weighted.dot(weighted.T)\n", "the_matrix = the_matrix[:,sample.index][sample.index,:]\n", "dot = pd.DataFrame(the_matrix.toarray(), index=sample.IMDbId.map(title_lookup), columns=sample.IMDbId.map(title_lookup)).apply(round, args=(2,))\n", "dot.style.apply(background_gradient,\n", " cmap=sns.light_palette(\"grey\", as_cmap=True),\n", " m=dot.min().min(),\n", " M=0.05,\n", " low=0)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Dimensionality Reduction\n", "\n", "Finally, we apply dimensionality reduction. I'm using \"Non-negative matrix factorization\", because the similarity calculations I'm doing later - cosine similarity - get weird if there are negative values. I'm not convinced this is the right approach though." ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "collapsed": false }, "outputs": [], "source": [ "shrinky = NMF(n_components = 2)\n", "\n", "shrunk_sample = shrinky.fit_transform(the_matrix.toarray())" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "collapsed": false }, "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", "
01
IMDbId
Ruthless People (1986)0.460
Dial M for Murder (1954)0.360.04
Duel in the Sun (1946)0.090.3
White Heat (1949)0.520
Lost Horizon (1973)00.69
Skyfall (2012)0.480
Liar Liar (1997)0.130.17
Kissin' Cousins (1964)00.68
The Yearling (1946)00.58
WarGames (1983)0.260.04
" ], "text/plain": [ "" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\n", "reduced = pd.DataFrame(shrunk_sample, index=sample.IMDbId.map(title_lookup)).apply(round, args=(2,))\n", "\n", "reduced.style.apply(background_gradient,\n", " cmap=sns.light_palette(\"grey\", as_cmap=True),\n", " m=reduced.min().min(),\n", " M=reduced.max().max(),\n", " low=0)\n" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "## Apply to the Whole Dataset" ] }, { "cell_type": "code", "execution_count": 94, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Throwing the whole process into a little method\n", "\n", "def make_matrix(df, countvectoriser, tfidf): \n", " sparse = countvectoriser.fit_transform(pd.Series(df.keywords.fillna('').values))\n", " weighted = tfidf.fit_transform(sparse) \n", " matrix = weighted.dot(weighted.T)\n", " movies = pd.Series(countvectoriser.get_feature_names())\n", " return matrix, movies, weighted" ] }, { "cell_type": "code", "execution_count": 95, "metadata": { "collapsed": false }, "outputs": [], "source": [ "\n", "vlad_ = CountVectorizer(tokenizer = lambda x: x.split('|'), min_df = 10)\n", "megatron_ = TfidfTransformer()\n", "matrix, words, weighted = make_matrix(keywords, vlad_, megatron_)" ] }, { "cell_type": "code", "execution_count": 96, "metadata": { "collapsed": false }, "outputs": [], "source": [ "target = matrix[10].toarray()" ] }, { "cell_type": "code", "execution_count": 97, "metadata": { "collapsed": false }, "outputs": [], "source": [ "vector = megatron_.transform(vlad_.transform([keywords.loc[10].keywords]))" ] }, { "cell_type": "code", "execution_count": 98, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "<1x10635 sparse matrix of type ''\n", "\twith 83 stored elements in Compressed Sparse Row format>" ] }, "execution_count": 98, "metadata": {}, "output_type": "execute_result" } ], "source": [ "vector" ] }, { "cell_type": "code", "execution_count": 107, "metadata": { "collapsed": false }, "outputs": [], "source": [ "new = weighted.dot(vector.T).toarray().T" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "I'm keeping the dimensionality reduction seperate, 'cause it's SLOW." ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "collapsed": false }, "outputs": [], "source": [ "shrinky = NMF(n_components = 100)\n", "\n", "shrunk_100 = shrinky.fit_transform(matrix.toarray())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Visualising the sample from above, but with the full set of columns." ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "collapsed": false }, "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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
01234567891011121314...
IMDbId
Up (2009)0.0300.110.020.0100.020000.02000.010
The Yearling (1946)000.0300.01000.03000.08000.010
Vivere in pace (1947)000.01000000000.19000
Romeo and Juliet (1968)0000.130.010.040000.110.1000.040
The Da Vinci Code (2006)0.0600.030.010.02000.020.0200000.010
Platoon (1986)0000.030.0300000000.030.070
Batman (1943)0000.010.030.030.1600000.0500.070
G.I. Blues (1960)0.0100.01000000000.0100.010.01
Pinky (1949)000000.0400.040000000
The Killer Shrews (1959)0000000.0100000000
" ], "text/plain": [ "" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "reduced = pd.DataFrame(shrunk_100[sample.index,:15], index=sample.IMDbId.map(title_lookup)).apply(round, args=(2,))\n", "reduced['...'] = pd.Series(['']*10, index = sample.IMDbId.map(title_lookup))\n", "reduced.style.apply(background_gradient,\n", " subset=pd.IndexSlice[:,range(0, 15)],\n", " cmap=sns.light_palette(\"grey\", as_cmap=True),\n", " m=reduced[[i for i in range(0,10)]].min().min(),\n", " M=reduced[[i for i in range(0,10)]].max().max(),\n", " )\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Doing some Movie Maths!" ] }, { "cell_type": "code", "execution_count": 80, "metadata": { "collapsed": false }, "outputs": [], "source": [ "movie_one = list(keywords.IMDbId.map(title_lookup)).index(\"Universal Soldier (1992)\")" ] }, { "cell_type": "code", "execution_count": 81, "metadata": { "collapsed": false }, "outputs": [], "source": [ "movie_two = list(keywords.IMDbId.map(title_lookup)).index(\"Piranha (1978)\")" ] }, { "cell_type": "code", "execution_count": 82, "metadata": { "collapsed": true }, "outputs": [], "source": [ "avg_movie = shrunk_100.mean(axis=0).reshape(1, -1)" ] }, { "cell_type": "code", "execution_count": 83, "metadata": { "collapsed": true }, "outputs": [], "source": [ "targets = [movie_one, movie_two] #, 4225]" ] }, { "cell_type": "code", "execution_count": 84, "metadata": { "collapsed": false }, "outputs": [], "source": [ "target = shrunk_100[movie_one].reshape(1, -1) + shrunk_100[movie_two].reshape(1, -1)" ] }, { "cell_type": "code", "execution_count": 85, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "['Missing in Action (1984)',\n", " 'First Blood (1982)',\n", " 'Rambo (2008)',\n", " 'Scanners (1981)',\n", " 'The Crazies (1973)',\n", " 'Predator (1987)',\n", " 'Doom (2005)',\n", " 'Broken Arrow (1996)',\n", " 'Night of the Living Dead (1968)',\n", " 'The Incredible Hulk (2008)']" ] }, "execution_count": 85, "metadata": {}, "output_type": "execute_result" } ], "source": [ "best_list = [i for i in np.argsort(cosine_similarity(target, shrunk_100))[0][::-1] if i not in targets][:10]\n", "\n", "[keywords.IMDbId.map(title_lookup)[i] for i in best_list]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Plotting movies" ] }, { "cell_type": "code", "execution_count": 276, "metadata": { "collapsed": false }, "outputs": [], "source": [ "plotting_matrix = matrix\n", "\n", "similar_matrix = plotting_matrix[aliens_lookup[aliens_lookup == True].index]\n", "\n", "axis_1_title = 'Bambi (1942)'\n", "axis_2_title = \"Showgirls (1995)\"\n", "\n", "axis_1_movie = list(keywords.IMDbId.map(title_lookup)).index(axis_1_title)\n", "\n", "axis_2_movie = list(keywords.IMDbId.map(title_lookup)).index(axis_2_title)\n" ] }, { "cell_type": "code", "execution_count": 277, "metadata": { "collapsed": false }, "outputs": [], "source": [ "titles = keywords[aliens_lookup].IMDbId.map(title_lookup)" ] }, { "cell_type": "code", "execution_count": 119, "metadata": { "collapsed": false }, "outputs": [ { "ename": "NameError", "evalue": "name 'titles' is not defined", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\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[0mtitles\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mNameError\u001b[0m: name 'titles' is not defined" ] } ], "source": [ "titles" ] }, { "cell_type": "code", "execution_count": 279, "metadata": { "collapsed": false }, "outputs": [], "source": [ "axis_1 = cosine_similarity(plotting_matrix[axis_1_movie], similar_matrix)[0]\n", "axis_2 = cosine_similarity(plotting_matrix[axis_2_movie], similar_matrix)[0]" ] }, { "cell_type": "code", "execution_count": 280, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# An extremely janky method for stopping the titles from overlapping each other.\n", "\n", "def avoid_overlap(axis_1, \n", " axis_2,\n", " x_tolerance = 0.05,\n", " y_tolerance = 0.02,\n", " increment = 0.01):\n", " fixed = []\n", " for x, y in zip(axis_1, axis_2):\n", " Xs = pd.Series([i[0] for i in fixed])\n", " Ys = pd.Series([i[1] for i in fixed])\n", " while ((Xs < x+x_tolerance) & (Xs > x-x_tolerance) & (Ys < y+y_tolerance) & (Ys > y-y_tolerance)).any():\n", " y += y_tolerance\n", " fixed.append((x, y))\n", " return fixed\n", " \n", "\n" ] }, { "cell_type": "code", "execution_count": 287, "metadata": { "collapsed": false }, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjEAAAF9CAYAAAD1HTl+AAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAAPYQAAD2EBqD+naQAAIABJREFUeJzs3Xl8VdW5//HPA2gQEKTlAi0XayuTw68oEYvADagovQlo\nFatGkitQrf6UirG2UvHWiiBq6wAXEOrPGmIgYCmCSGqsFIlePAIJilQwA5NMAiJBHAM8vz/OyTEJ\nSUg4mQ5836/XeZm99tprP/tomydrr8HcHREREZFo06ShAxARERE5HkpiREREJCopiREREZGopCRG\nREREopKSGBEREYlKSmJEREQkKimJERERkaikJEZERESikpIYERERiUpKYkRERCQqNdokxszuNLNN\nZvalmQXMrHcVdQeY2ZFyn8Nm1r5cvZ+b2fpQm++Z2X/W/ZOIiIhIXWiUSYyZ3QA8ATwIXAi8B2SZ\nWbsqLnOgK9Ax9Pmeu+8u1WZfYA7wLHABsAhYaGbn1slDiIiISJ2yxrgBpJkFgHfcfUzo2ICPgCnu\n/ngF9QcA/wTauvuBStqcC7Rw96tKlb0NrHH3O+rgMURERKQONbqeGDM7BYgFlpaUeTDTeh24pKpL\ngXfNbIeZvRbqeSntklAbpWUdo00RERFppBpdEgO0A5oCH5cr/5jga6KK7ARuA4YB1xLstXnDzC4o\nVadjDdsUERGRRqxZQwdQG9w9D8grVRQws7OBFODm42nTzFoQHDtzFrAZ+CqyKEVERE4qzUP/fNfd\nv6iLGzTGJGYvcBjoUK68A7CrBu2sBPqVOt5VwzZ7AP9bg/uJiIjI0WKB3LpouNElMe5ebGY5wOXA\nyxAe2Hs5MKUGTV1A8DVTibcraOOKUHlFNhD80nulp6dzzjnn1ODWJ56UlBSeeuqphg6jwel7+Ja+\niyB9D9/SdxGk7yHolltuYc2aNXV6j0aXxIQ8CaSGkpmVBF8LtQBSAcxsEvB9d785dDwG2AT8i2D3\n1a3ApQSTlBKTCY6TuQdYAiQSzA5vrSgAd//CzA4CnHPOOfTq1auWHzG6tGnT5qT/DkDfQ2n6LoL0\nPXxL30WQvoeg008/vc7v0SiTGHd/MbQmzHiCr3zeBQa7+55QlY5A51KXnEpwXZnvA18Aa4HL3T27\nVJtvm9lNwMTQJx+42t0/qOvnERERkdrXKJMYAHefDkyv5NzIcsd/BP5YjTb/BvytVgIUERGRBtUY\np1iLiIiIHJOSmKq92tABNBaJiYkNHUKjoO/hW/ougvQ9fEvfRZC+h6DBgwfX+T0a5bYDjYWZ9QJy\ncnJyNEhLRESkBnJzc4mNjQWIdfc6mWKtnhgRERGJSkpiREREJCopiREREZGopCRGREREopKSGBER\nEYlKSmJEREQkKimJERERkaikJEZERESikpIYERERiUpKYkRERCQqKYkRERGRqKQkRkRERKKSkhgR\nERGJSkpiREREJCopiREREZGopCRGREREopKSGBEREYlKSmJEREQkKimJERERkaikJEZERE4YSUlJ\nbNiwIXx84MABrr76aiZMmABAVlYWycnJdXb/vXv3cu+99/LEE0/w4osvMm3aNIYPH84nn3xSZ/c8\nmTVr6ABERERqw8GDB2nWrBkzZszg6aefBqB169ZcddVVbN++HYBBgwZx8cUX18n9d+7cydChQ5k5\ncyaxsbHh8nPOOYeCggK++93v1sl9T2bqiRERkRNCfn4+U6dOZfHixRQXF4fLmzX79u/1pk2b0rp1\n6zq5/5gxYxgwYECZBAbgsssu4yc/+UmV13799dd1ElNNrFmzhjfffLOhw6gRJTEiInJCiImJoVWr\nVgwZMoTFixdXWCc7O5tbb701fJyZmclLL73ECy+8wKOPPgrA/v37ue2225g+fTo33ngjPXr0YN26\ndQDMmzePIUOGHNXup59+yvz58ys8V9qbb77JY489xquvvsrUqVN58803cXcyMjJo0aIFc+bM4fPP\nP+fpp59m+PDh/Otf/+KLL75g1qxZLFy4kIcffphAIADAq6++yoQJExg7dix9+/bl2muvDd9n1apV\nPPbYY9x0003Mnj07XP7AAw/wwgsvcOedd3Luuefy2GOPAcEEMDk5mWeeeYb777+fzz//vDpfeYNT\nEiMiIlFv27ZtBAIBFi1aRKdOnUhLS6uw3pYtW9i8eTMAa9euZdmyZbRs2ZL27duzY8cOioqKmDRp\nEkuXLuW6665j7ty5pKSkkJqaCsCPf/xjfv7znx/VbkFBAWZG586dK41x586djB07lvvuu4+f/vSn\njB49mt/+9rds27aNESNGcNlll3HWWWfRsmVLevfuzeOPP855553HxIkTadOmDS1atKBLly5kZ2ez\na9cuxo4dy65du3j00UdZsWIFO3bsYN26dXz55Zf84Q9/4L777uORRx5h1qxZAKxYsYLp06fTvHlz\n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\n3W1RVDfV2NVV1f37PE8/dq1ae+21X4tdb++99lrH1L93+eWXc/HFFwPJ00hdu3alc+fOm7SxZs2a\n+t/vuusuevTowYEHHsjYsWNZuXIld9xxR6P733HHHTn55JN59NFHOf3003PWmzVrFpWVldx9991A\ncntp/fr1vPfee6xYsYKRI0eyatUqRo0axSGHHMLs2bNZsGABDz30EAceeOBGbc2dO5dBgwYxdOhQ\nhg8fzpw5c7jzzjsZNWoU3bp148EHH2Trrbdm2bJljBs3brMxjAjWrFnDtttuyzvvvMPUqVO56aab\niAgmTJhAp06dGD9+PH379uX6669nypQpbLfddrz11lu0b9++fs6eGTNmsHr1aj766CMWLlxY3+/a\n2loefPBB/va3v9G9e3dGjBjByy+/zLnnnkvPnj354x//yLXXXsvSpUuZOnUqvXr1YsWKFey6664M\nHDiwwX6YmTU3JzHWYupm7q2traV79+5A8gj2OeecA0C/fv345JNPAPjyl7/ME088kTPJ2LBhA089\n9RQPP/ww2223HTNnzqSiooI+ffrw3nvvNbj/5cuXM3fuXGpra+nSpQuTJ09uMIl59dVXWb58OZBc\nEp0zZw4nnHAC2223HTU1Nbz//vvceOONzJw5kzFjxnDppZcyYcIE7r///vpbXXWOOeYYvve971FR\nUcHOO+9Mv379ADjuuOMYO3Ys++yzD4MGDaKmpibvWP79739n8uTJLFq0iFtvvZUzzjiDl19+mcrK\nSg466CCqqqpo164dDz30EK+//jqHHHIIXbt25eGHH+bCCy/krrvu4p133uGaa64B4He/+11925dc\ncglPPvkk69ev59BDD2XEiBEccMABHH744QwbNozevXuzdu1azj//fKqrq2nfvj0Ap512GnvvvTfb\nb7/9Jv0wMysEJzHWYjJn7h04cCCTJk3isssuIyI49thjmT17NqtWrWK33XZj//33p6amhp122iln\nW3379qVv374blR199NGN7n/ZsmX1g3UHDhxIjx49WL16dc5xL8cccwxVVVVAMnvwgAED6NOnD7Dx\n7MELFy5kt912A5LZgxuaoO+CCy7g+OOPZ+TIkfz5z3+ub2PAgAEMGDCAmpoaRo0a1Wj/M+26665c\nffXVG5UdcMABHHfccXTv3p2uXbsCcNVVV3HbbbfRpUsXAE4++WQAxo4dyzPPPFO/7aGHHgokCeJt\nt90GwAsvvLBR+5nj52bOnMlee+1Vn8AA9O7dmwceeIBbbrllk36YmRWCx8RYi8meuXfo0KHsueee\nDO6ljx8AABUVSURBVBkyhL333pshQ4bQp08fIoLZs2c3++PWq1at2uj1aaedVp+oNKY5Zg/eY489\nOPjgg/nTn/7Exx9/zDbbbANAjx49eOmll+jSpQsDBgzI4yga165du/q26/r+4osvblLvzTffZOut\nt65/XTceqF27dnTr1o1rr72WHXbYYaMVxzPVXTHL1KFDh/ry7H6YmRWCkxhrEblm7j3zzDPp2LEj\nS5Ys4bHHHmPJkiVMnz6dc889l9GjR/O1r30tZ1uZA3AzzZs3j6effjrnezNnzuSggw7aqCxz5mDY\ndObfzNmDx40bV58ELVmyhBUrVgBNmz142LBhjBw5kn333be+7Le//S1bbbUVV155Jb169eKDDz5g\n2bJlPPjggznbqGu/oRhkD9A95phjuOGGG1i3bh0ANTU1rF27lqOOOorf//73Odv49re/zeDBg9ll\nl1349NNP6xOzrbbaqj6p6devH6+99tpGSc6sWbMYMmRIzn6YmRWCkxgrqHxm7q2qquLII4+sv/Jy\n0UUX1d9uybRw4ULmzp3Ls88+S1VV1SYJxKOPPsojjzyyUdn69euZPn06P/rRj6itra1PRN544436\nR7xvvPFG5s2bx6xZs6iurqampobJkyezdOlSnnvuOc4++2xOO+00Dj/8cL75zW+yYMECdtllF/7w\nhz9QW1vL9OnTWblyJVOnTuX5559n/vz5OWNx/PHH07NnTw4++OD6st13351hw4Yxbdo0+vXrR8eO\nHamurs55hWjdunU88MADSGL8+PEsXrx4o/fnz59PTU0Njz/+OA8//DAAI0aMYL/99qNHjx4MGTKE\nd999lw4dOvCrX/2KyZMnc9lllzFlyhRee+01Zs6cyapVq1i+fDkTJ05kwYIF7Ljjjtxwww0AnHji\niYwfP55JkyZRUVHBvffey6233spTTz3FPffcw3e/+1169uyZsx9mZoXgeWIa4XlirK3YsGGDB+Ca\nWbNqiXliPLDXrAla6xIJTmDMrByV7O0kSZdKelXSR5KqJeUeIJHUPUrSM5LelvShpEWSLs9RbydJ\nEyS9KWmdpMWSTi7skVhrkGuJhFNPPZXVq1cXu2tmZm1WSSYxks4CbgNGA4cBLwAzJHVqYJO1wJ3A\n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\nAAAASUVORK5CYII=\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "pd.DataFrame(list(zip(axis_1, axis_2))).plot(kind='scatter', x=0, y=1, c='w')\n", "\n", "# Cause I'm lazy, you gotta fiddle with the values to get the titles to show up in the right spots.\n", "\n", "for label, (x, y) in zip(list(titles.values), avoid_overlap(axis_1, \n", " axis_2, \n", " y_tolerance = 0.0006,\n", " increment = 0.00002,\n", " x_tolerance = 1)):\n", " label = label[:label.find('(')-1]\n", " plt.annotate(label,\n", " fontsize=10,\n", " fontname='Garamond',\n", " xy=(x - (len(label)*0.0007), y + 0.0025))\n", "plt.xlabel(axis_1_title, fontname='Garamond', fontsize = 14)\n", "plt.ylabel(axis_2_title, fontname='Garamond', fontsize = 14)\n", "# plt.xlim(0.55, 0.78)\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": 240, "metadata": { "collapsed": false }, "outputs": [ { "data": { "image/png": "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\nM7+ZdTpE3xvN7GMz22RmhWY218yuqc16RUREJDJEXDAys9uBF4DHgQuBr4FMM2tWzpCuwMfAb4FY\nYA4w3cw61kK5IiIiEkEiLhgBqcBo59w459xSYADwX6B/WZ2dc6nOueedcwHn3HLn3BAgH+hdeyWL\niIhIJIioYGRm9YA4YFZRm3POAZ8Cl1ZyHQacAGytiRpFREQkckVUMAKaAXWAjaXaNwItK7mOvwKN\ngPeqsS4RERE5CtQNdwG1ycx+DzwGXOec2xLuekREROTIEmnBaAuwH2hRqr0FsOFQA83sd8AbwC3O\nuTmVebPU1FSio6NLtCUlJZGUlFTpgkVERI5W6enppKenl2grLCwMUzXVw4Kn6EQOM/MD85xzg7xl\nA1YDI5xzz5UzJgl4E7jdOTejEu8RCwQCgQCxsbHVV7yIiMhRLjc3l7i4OIA451xuuOupqkibMQJ4\nEUgzswAwn+BVascDaQBmNgw4zTl3h7f8e++1+4EFZlY027TLObejdksXERGRI1nEBSPn3HvePYuG\nEjyEthDo6Zzb7HVpCbQqNuQugidsv+b9FBlLOZf4i4iIyLEp4oIRgHNuJDCynNf6lVq+qlaKEhER\nkYgXaZfri4iIiNQYBSMRERERj4KRiIiIiEfBSERERMSjYCQiIiLiUTASERER8SgYiYiIiHgUjERE\nREQ8CkYiIiIiHgUjEREREY+CkVRKcnIyS5cuDS1nZWXRtWtXzj77bD766CN27Ag+j3f9+vWMGDGC\nyy67jOzsbMaMGcO5555L7969mThxIsOGDePyyy8nMzOzzPXu2LGD66+/nqeeegqAzMxMUlJS+N3v\nfkd+fn6JmrZv307v3r155plnAJg5cyb9+pV4IgyLFy/m6aefplevXuzevbv6N0wFli5dSmpqKlFR\nUYwbN4633nqLpKQk+vfvz549ew57vd988w2dO3dmwoQJ1VitiByJKrufrClbtmzhoYce4oUXXuC9\n997jtddeo0+fPvz444819p5h5ZzTT6kfIBZwgUDAiXP/+c9/3B133OEGDRpUon3p0qUuOjra7dy5\ns0T7tGnT3BdffBFaHjJkiHvkkUdCy9OnT3cnnHCCW7t2bZnrffPNN90TTzzhnHNu3759buXKlS4l\nJcX95S9/Oai2UaNGuaeffjrUd+vWrSVe37Nnj9uxY4f729/+dhifvHrs3bvXRUVFuV27djnngnV2\n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\nKv8BRmi0B2S027sAAAAASUVORK5CYII=\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "two_shrunk_two_furious = NMF(n_components = 4)\n", "\n", "shrunk_2 = two_shrunk_two_furious.fit_transform(matrix[aliens_lookup[aliens_lookup == True].index].toarray())\n", "\n", "alien_df = pd.DataFrame(shrunk_2)\n", "\n", "alien_df.plot(kind='scatter', x=0, y=1, c='w')\n", "for label, x, y in zip(list(titles.values), alien_df[0], alien_df[1]):\n", " label = label[:label.find('(')-1]\n", " plt.annotate(label,\n", " fontsize=10,\n", " fontname='Garamond',\n", " xy =(x - (len(label)*0.007), y + 0.06))\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": 241, "metadata": { "collapsed": false, "scrolled": false }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "C:\\Users\\simon.carryer\\AppData\\Local\\Continuum\\Anaconda3\\lib\\site-packages\\pandas\\io\\formats\\style.py:511: FutureWarning: \n", "Passing list-likes to .loc or [] with any missing label will raise\n", "KeyError in the future, you can use .reindex() as an alternative.\n", "\n", "See the documentation here:\n", "http://pandas.pydata.org/pandas-docs/stable/indexing.html#deprecate-loc-reindex-listlike\n", " data = self.data.loc[subset]\n", "C:\\Users\\simon.carryer\\AppData\\Local\\Continuum\\Anaconda3\\lib\\site-packages\\pandas\\core\\indexing.py:1367: FutureWarning: \n", "Passing list-likes to .loc or [] with any missing label will raise\n", "KeyError in the future, you can use .reindex() as an alternative.\n", "\n", "See the documentation here:\n", "http://pandas.pydata.org/pandas-docs/stable/indexing.html#deprecate-loc-reindex-listlike\n", " return self._getitem_tuple(key)\n", "C:\\Users\\simon.carryer\\AppData\\Local\\Continuum\\Anaconda3\\lib\\site-packages\\matplotlib\\colors.py:581: RuntimeWarning: invalid value encountered in less\n", " cbook._putmask(xa, xa < 0.0, -1)\n", "C:\\Users\\simon.carryer\\AppData\\Local\\Continuum\\Anaconda3\\lib\\site-packages\\pandas\\io\\formats\\style.py:528: FutureWarning: \n", "Passing list-likes to .loc or [] with any missing label will raise\n", "KeyError in the future, you can use .reindex() as an alternative.\n", "\n", "See the documentation here:\n", "http://pandas.pydata.org/pandas-docs/stable/indexing.html#deprecate-loc-reindex-listlike\n", " expected_shape = self.data.loc[subset].shape\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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
0123
Title
Alien (1979)01.0746600.817128
Aliens (1986)0.05701341.203130.4953280.442488
Alien³ (1992)000.3227121.52612
Alien Resurrection (1997)01.507430.5217390
AVP: Alien vs. Predator (2004)001.938560.045044
Prometheus (I) (2012)0.8575680.76322700.0738052
Alien: Covenant (2017)1.8369500.1134770
" ], "text/plain": [ "" ] }, "execution_count": 241, "metadata": {}, "output_type": "execute_result" } ], "source": [ "alien_df.index = titles\n", "alien_df.index.name = 'Title'\n", "alien_df.style.apply(background_gradient,\n", " subset=pd.IndexSlice[:,range(0, 5)],\n", " cmap=sns.light_palette(\"grey\", as_cmap=True),\n", " m=0.05,\n", " M=2,\n", " )" ] }, { "cell_type": "code", "execution_count": 214, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[('Alien (1979)', 0.0, 0.32869353140190521),\n", " ('Aliens (1986)', 0.92728398608709284, 1.1366325863284708),\n", " ('Alien³ (1992)', 0.0, 0.48841624022684982),\n", " ('AVP: Alien vs. Predator (2004)', 1.0498352089732472, 0.10050532081262395)]" ] }, "execution_count": 214, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[(label, x, y) for label, x, y in zip(list(titles.values), shrunk_2[0], shrunk_2[1])]" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [] } ], "metadata": { "anaconda-cloud": {}, "kernelspec": { "display_name": "Python [default]", "language": "python", "name": "python3" }, "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.5.2" } }, "nbformat": 4, "nbformat_minor": 1 }