{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import gzip\n",
    "import math\n",
    "import matplotlib.pyplot as plt\n",
    "import random\n",
    "import scipy\n",
    "import tensorflow as tf\n",
    "from collections import defaultdict\n",
    "from implicit import bpr\n",
    "from scipy.spatial import distance"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Data is available at http://cseweb.ucsd.edu/~jmcauley/pml/data/. Download and save to your own directory"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "dataDir = \"/home/jmcauley/pml_data/\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Basic fairness measurements - interaction and recommendation distributions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Goodreads graphic novel data. Train a standard recommender, and compare generated recommendations to historical interactions, in terms of frequency distributions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "def parse(path):\n",
    "    g = gzip.open(path, 'r')\n",
    "    for l in g:\n",
    "        yield eval(l)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = []\n",
    "for x in parse(dataDir + \"goodreads_reviews_comics_graphic.json.gz\"):\n",
    "    del x['review_text']\n",
    "    data.append(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "random.shuffle(data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "userIDs,itemIDs = {},{}\n",
    "revUIDs,revIIDs = {},{}\n",
    "for d in data:\n",
    "    u,i = d['user_id'],d['book_id']\n",
    "    if not u in userIDs:\n",
    "        userIDs[u] = len(userIDs)\n",
    "        revUIDs[userIDs[u]] = u\n",
    "    if not i in itemIDs:\n",
    "        itemIDs[i] = len(itemIDs)\n",
    "        revIIDs[itemIDs[i]] = i"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(59347, 89311)"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "nUsers,nItems = len(userIDs),len(itemIDs)\n",
    "nUsers,nItems"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Problem setup follows the BPR library from \"implicit\" (see Chapter 5), but could be completed with any recommender"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "Xiu = scipy.sparse.lil_matrix((nItems, nUsers))\n",
    "for d in data:\n",
    "    Xiu[itemIDs[d['book_id']],userIDs[d['user_id']]] = 1\n",
    "    \n",
    "Xui = scipy.sparse.csr_matrix(Xiu.T)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "model = bpr.BayesianPersonalizedRanking(factors = 5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Fit the model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "5c7896ac21b1498eae78b5760e314600",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "HBox(children=(FloatProgress(value=0.0), HTML(value='')))"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "model.fit(Xiu)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Extract recommendations (example)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[(3056, 5.0516043),\n",
       " (24859, 5.0356407),\n",
       " (10783, 5.030104),\n",
       " (5767, 4.985171),\n",
       " (769, 4.9555626),\n",
       " (2193, 4.9438295),\n",
       " (3191, 4.919392),\n",
       " (1388, 4.907593),\n",
       " (14141, 4.9072804),\n",
       " (3324, 4.898348)]"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model.recommend(0, Xui)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next we extract recommendations for all users, and compare these to their interaction histories. First collect histories from historical trends:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "interactionTuples = []\n",
    "itemsPerUser = defaultdict(list)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "for d in data:\n",
    "    u,i = d['user_id'],d['book_id']\n",
    "    interactionTuples.append((userIDs[u],itemIDs[i]))\n",
    "    itemsPerUser[userIDs[u]].append(itemIDs[i])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next build similar data structures containing recommendations for each user"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "recommendationTuples = []"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For each user, generate a set of recommendations equivalent in size to their number of interactions used for training"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "for u in range(len(userIDs)):\n",
    "    A = model.recommend(u, Xui, N = len(itemsPerUser[u]))\n",
    "    for i, sc in A:\n",
    "        recommendationTuples.append((u,i))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So far our data structures just contain lists of historical interactions and recommendations for each user. Convert these into counts for each item. This is done both for interactions (I) and recommendations (R)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "countsPerItemI = defaultdict(int)\n",
    "countsPerItemR = defaultdict(int)\n",
    "for u,i in interactionTuples:\n",
    "    countsPerItemI[i] += 1\n",
    "\n",
    "for u,i in recommendationTuples:\n",
    "    countsPerItemR[i] += 1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Sort counts by popularity to generate plots"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "sortPopularI = [(countsPerItemI[i], i) for i in countsPerItemI]\n",
    "sortPopularR = [(countsPerItemR[i], i) for i in countsPerItemR]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "sortPopularI.sort(reverse=True)\n",
    "sortPopularR.sort(reverse=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Collect the information for interactions and recommendations for plotting"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "YI = [x[0] for x in sortPopularI[:300]] # Interaction frequency\n",
    "Ys = [x[1] for x in sortPopularI[:300]] # Associated items\n",
    "YR = [countsPerItemR[x] for x in Ys] # Recommendation frequency for those items"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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xhtvMHPmTGbNmmU4MeOLL77IyJEjOfvss/nPf/5DaWkp06dPZ9y4cdGBiStWrODCCy9k2rRp0fmxxo4dy6JFi2ytr66uZt68eUycOJG5c+cSCoVYsWIFF1xwAVOnTmXatGlAeCqVCRMmMHXqVN577z0Avv32WyZMmBAdzAjw6KOPsnLlSiZNmsSuXbv4/ve/D8CqVasYOXIkY8aMibZHI0eOZNGiRZx22mm89tprBINBpk2bxoQJE7jooouSfv2bBZFox5K4DbaDN0Sibxyakw/uKxJn8InEGYLBIC+++CL33HMP69atY926ddx5551IKbn99tt54403+Pe//83o0aN59NFHueiii1i/fj2VlZXRBv2EE05g9erV9OjRg9raWt544w2+/vrr6LxdVusfe+wxZs6cydq1axk7dizPP/88EB6HtmbNGrp27crWrVt57LHH+OEPf8iaNWuiL87r2LEja9euZcOGDRw9epQdO3awaNEiFixYwL/+9S/g2LRODz74IG+99RarVq3ihhtuAODw4cPccccdrFmzhkceeYQ9e/bQqVMnXn/9df785z8n/dondWS7EOJxYBqwX0o5OLKsLfAXoAewE5gtpTwaWfcAMAWoAC6VUv4vsvwS4NeABG6TUj5lccyYh84rawtg8ODBLFu2zFEZPZqztRUKhTL+HL1EphLJkiVLHJe5+eab425z2mmncfDgwQYTMx48eJAePXqQm5sLhJ+9HTt2MHXqVABOP/10vvjiC+DYBItdunSJ+b+kpCTu+k8//ZSnn36a5cuXU11dzdy5cykqKorZ7siRI3z55ZdMnz49WmcITyj5ox/9iCNHjrBr1y6++eYbw3NU55KVlUWPHj0oLS0FwkTUvn17AI4ePUqvXr0YNGgQ3//+9znttNO45pprbF9rN0j2FClPAg8C2ob/V8BaKeWdQojrgOuBXwkhpgB9pJR9hRAjgEeAMyLE8xvgNEAAm4UQ/1Dko4cda0u/nV2ccsopfPrpp9TV1ZGT4/7SNdfZf31ryxkyNf3XDim4QVZWVnRixldffRUhBKFQiKysLHbv3k1NTQ15eXlIKTnxxBPZtGkTJ510Eps2beKHP/whn376qeHcWNqJI63WDxgwgIkTJ3L++ecDUFdXx9NPP93g2e7duzf/+9//GDBgAFu2bGHy5Mk888wznH/++SxYsID5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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fc40167e828>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(range(1,301),YI, color='k', label = \"interactions\")\n",
    "plt.xlim(1,200)\n",
    "plt.ylim(0,1150)\n",
    "plt.plot(range(1,301),YR, color='grey', label = \"recommendations\")\n",
    "plt.xlabel(\"popularity rank (interactions)\")\n",
    "plt.ylabel(\"interaction freq.\")\n",
    "plt.legend(loc=\"best\",  prop={'size': 8})\n",
    "plt.title(\"Recommendation diversity\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "YR = [x[0] for x in sortPopularR[:300]]\n",
    "Ys = [x[1] for x in sortPopularR[:300]]\n",
    "YI = [countsPerItemI[x] for x in Ys]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fc3dbb76ef0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(range(1,301),YI, color='k', label = \"interactions\")\n",
    "plt.xlim(1,200)\n",
    "plt.ylim(0,1150)\n",
    "plt.plot(range(1,301),YR, color='grey', label = \"recommendations\")\n",
    "plt.xlabel(\"popularity rank (recommendations)\")\n",
    "plt.ylabel(\"interaction freq.\")\n",
    "plt.legend(loc=\"best\",  prop={'size': 8})\n",
    "plt.title(\"Recommendation diversity\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Gini goefficient"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The two implementations below compute the gini coefficient either by comparing all pairs, or by doing so for a given number of samples"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "def gini(z, samples=1000000):\n",
    "    m = sum(z) / len(z)\n",
    "    denom = 2 * samples * m\n",
    "    numer = 0\n",
    "    for _ in range(samples):\n",
    "        i = random.choice(z)\n",
    "        j = random.choice(z)\n",
    "        numer += math.fabs(i - j)\n",
    "    return numer / denom\n",
    "\n",
    "def giniExact(z):\n",
    "    m = sum(z) / len(z)\n",
    "    denom = 2 * len(z)**2 * m\n",
    "    numer = 0\n",
    "    for i in range(len(z)):\n",
    "        for j in range(len(z)):\n",
    "            numer += math.fabs(z[i] - z[j])\n",
    "    return numer / denom"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Compute the gini coefficients of interactions versus distributions for the two distributions computed in the experiments above"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.7190767454225961"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "gini([x[0] for x in sortPopularI])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.7722158419767746"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "gini([x[0] for x in sortPopularR])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Average cosine similarity between interactions versus recommendations"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Given a set of items, measure the average cosine distance between them (by taking a sample of pairs, similar to our implementation of the gini coefficient). This can be used as a rough measure of the diversity of a set of recommendations."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "def avCosine(z, samples = 100):\n",
    "    av = []\n",
    "    while len(av) < samples:\n",
    "        i = random.choice(z)\n",
    "        j = random.choice(z)\n",
    "        d = 1 - distance.cosine(i, j)\n",
    "        if not math.isnan(d) and d > 0:\n",
    "            av.append(d)\n",
    "    return sum(av) / len(av)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "itemFactors = model.item_factors"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Compute the average cosine similarity among interactions from a particular user"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.6911979604884982"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "avCosine([itemFactors[i] for i in itemsPerUser[0]])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Compute the same quantity across a large sample of recommendations and interactions for several users, as an aggregate measure of recommendation versus interaction diversity"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [],
   "source": [
    "avavI = []\n",
    "avavR = []\n",
    "\n",
    "while len(avavI) < 1000:\n",
    "    u = random.choice(range(len(userIDs)))\n",
    "    if len(itemsPerUser[u]) < 10:\n",
    "        continue\n",
    "    aI = avCosine([itemFactors[i] for i in itemsPerUser[u]])\n",
    "    A = model.recommend(u, Xui, N = len(itemsPerUser[u]))\n",
    "    aR = avCosine([itemFactors[i[0]] for i in A])\n",
    "    avavI.append(aI)\n",
    "    avavR.append(aR)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A lower average cosine similarity among interactions indicates that they are more diverse compared to recommendations"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.6248067230512387"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sum(avavI) / len(avavI)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.8639710014653086"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sum(avavR) / len(avavR)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Fair recommendation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "First, implement a latent factor model. Our basic implementation follows the Tensorflow latent factor model from Chapter 5."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "userIDs = {}\n",
    "itemIDs = {}\n",
    "itemsPerUser = defaultdict(set)\n",
    "styles = {}\n",
    "beerNames = {}\n",
    "interactions = []\n",
    "\n",
    "for d in parse(dataDir + \"beer.json.gz\"):\n",
    "    if not 'user/gender' in d: continue\n",
    "    g = d['user/gender'] == 'Male'\n",
    "    u = d['user/profileName']\n",
    "    i = d['beer/beerId']\n",
    "    r = d['review/overall']\n",
    "    styles[i] = d['beer/style']\n",
    "    beerNames[i] = d['beer/name']\n",
    "    if not u in userIDs: userIDs[u] = len(userIDs)\n",
    "    if not i in itemIDs: itemIDs[i] = len(itemIDs)\n",
    "    itemsPerUser[u].add(i)\n",
    "    interactions.append((g,u,i,r))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "637221"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(interactions)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Data structures to organize interactions by gender"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [],
   "source": [
    "interactionsPerItemG = defaultdict(list) # Male interactions\n",
    "interactionsPerItemGneg = defaultdict(list) # Other interactions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [],
   "source": [
    "for g,u,i,r in interactions:\n",
    "    if g: interactionsPerItemG[i].append((u,r)) # Male interactions for this item\n",
    "    else: interactionsPerItemGneg[i].append((u,r))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [],
   "source": [
    "itemsG = set(interactionsPerItemG.keys()) # Set of items associated with male interactions\n",
    "itemsGneg = set(interactionsPerItemGneg.keys())\n",
    "itemsBoth = itemsG.intersection(itemsGneg)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [],
   "source": [
    "mu = sum([r for _,_,_,r in interactions]) / len(interactions) # For initialization"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [],
   "source": [
    "optimizer = tf.keras.optimizers.Adam(0.1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Latent factor model. The \"absoluteFairness\" function is new; others are equivalent to our model from Chapter 5."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [],
   "source": [
    "class LatentFactorModel(tf.keras.Model):\n",
    "    def __init__(self, mu, K, lamb, lambFair):\n",
    "        super(LatentFactorModel, self).__init__()\n",
    "        self.alpha = tf.Variable(mu)\n",
    "        self.betaU = tf.Variable(tf.random.normal([len(userIDs)],stddev=0.001))\n",
    "        self.betaI = tf.Variable(tf.random.normal([len(itemIDs)],stddev=0.001))\n",
    "        self.gammaU = tf.Variable(tf.random.normal([len(userIDs),K],stddev=0.001))\n",
    "        self.gammaI = tf.Variable(tf.random.normal([len(itemIDs),K],stddev=0.001))\n",
    "        self.lamb = lamb\n",
    "        self.lambFair = lambFair\n",
    "\n",
    "    def predict(self, u, i):\n",
    "        p = self.alpha + self.betaU[u] + self.betaI[i] +\\\n",
    "            tf.tensordot(self.gammaU[u], self.gammaI[i], 1)\n",
    "        return p\n",
    "\n",
    "    def reg(self):\n",
    "        return self.lamb * (tf.reduce_sum(self.betaU**2) +\\\n",
    "                            tf.reduce_sum(self.betaI**2) +\\\n",
    "                            tf.reduce_sum(self.gammaU**2) +\\\n",
    "                            tf.reduce_sum(self.gammaI**2))\n",
    "    \n",
    "    def predictSample(self, sampleU, sampleI):\n",
    "        u = tf.convert_to_tensor(sampleU, dtype=tf.int32)\n",
    "        i = tf.convert_to_tensor(sampleI, dtype=tf.int32)\n",
    "        beta_u = tf.nn.embedding_lookup(self.betaU, u)\n",
    "        beta_i = tf.nn.embedding_lookup(self.betaI, i)\n",
    "        gamma_u = tf.nn.embedding_lookup(self.gammaU, u)\n",
    "        gamma_i = tf.nn.embedding_lookup(self.gammaI, i)\n",
    "        pred = self.alpha + beta_u + beta_i +\\\n",
    "               tf.reduce_sum(tf.multiply(gamma_u, gamma_i), 1)\n",
    "        return pred\n",
    "    \n",
    "    # For a single item. This score should be averaged over several items\n",
    "    def absoluteUnfairness(self, i):\n",
    "        G = interactionsPerItemG[i]\n",
    "        Gneg = interactionsPerItemGneg[i]\n",
    "        # interactions take the form (u,r)\n",
    "        rG    = tf.reduce_mean(tf.convert_to_tensor([r for _,r in G]))\n",
    "        rGneg = tf.reduce_mean(tf.convert_to_tensor([r for _,r in Gneg]))\n",
    "        pG    = tf.reduce_mean(\n",
    "            self.predictSample([userIDs[u] for u,_ in G], [itemIDs[i]]*len(G)))\n",
    "        pGneg = tf.reduce_mean(\n",
    "            self.predictSample([userIDs[u] for u,_ in Gneg], [itemIDs[i]]*len(Gneg)))\n",
    "        Uabs = tf.abs(tf.abs(pG - rG) - tf.abs(pGneg - rGneg))\n",
    "        return self.lambFair * Uabs\n",
    "    \n",
    "    def call(self, sampleU, sampleI, sampleR):\n",
    "        pred = self.predictSample(sampleU, sampleI)\n",
    "        r = tf.convert_to_tensor(sampleR, dtype=tf.float32)\n",
    "        return tf.nn.l2_loss(pred - r) / len(sampleR)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [],
   "source": [
    "modelFair = LatentFactorModel(mu, 10, 0.000001, 0.000001)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [],
   "source": [
    "def trainingStep(model, interactions):\n",
    "    Nsamples = 5000\n",
    "    Nfair = 50\n",
    "    with tf.GradientTape() as tape:\n",
    "        sampleU, sampleI, sampleR = [], [], []\n",
    "        for _ in range(Nsamples):\n",
    "            _,u,i,r = random.choice(interactions)\n",
    "            sampleU.append(userIDs[u])\n",
    "            sampleI.append(itemIDs[i])\n",
    "            sampleR.append(r)\n",
    "\n",
    "        loss = model(sampleU,sampleI,sampleR)\n",
    "        loss += sum([model.absoluteUnfairness(i)\n",
    "                     for i in random.sample(itemsBoth,Nfair)])/Nfair\n",
    "        loss += model.reg()\n",
    "    gradients = tape.gradient(loss, model.trainable_variables)\n",
    "    optimizer.apply_gradients((grad, var) for\n",
    "                              (grad, var) in zip(gradients, model.trainable_variables)\n",
    "                              if grad is not None)\n",
    "    return loss.numpy()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "iteration 50, objective = 0.2573108\n",
      "iteration 100, objective = 0.27134913\n"
     ]
    }
   ],
   "source": [
    "for i in range(100):\n",
    "    obj = trainingStep(modelFair, interactions)\n",
    "    if (i % 50 == 49): print(\"iteration \" + str(i+1) + \", objective = \" + str(obj))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Same model, but not including the fairness terms"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [],
   "source": [
    "modelUnfair = LatentFactorModel(mu, 10, 0.000001, 0.000001)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [],
   "source": [
    "def trainingStepUnfair(model, interactions):\n",
    "    Nsamples = 5000\n",
    "    with tf.GradientTape() as tape:\n",
    "        sampleU, sampleI, sampleR = [], [], []\n",
    "        for _ in range(Nsamples):\n",
    "            _,u,i,r = random.choice(interactions)\n",
    "            sampleU.append(userIDs[u])\n",
    "            sampleI.append(itemIDs[i])\n",
    "            sampleR.append(r)\n",
    "\n",
    "        loss = model(sampleU,sampleI,sampleR)\n",
    "        loss += model.reg()\n",
    "    gradients = tape.gradient(loss, model.trainable_variables)\n",
    "    optimizer.apply_gradients((grad, var) for\n",
    "                              (grad, var) in zip(gradients, model.trainable_variables)\n",
    "                              if grad is not None)\n",
    "    return loss.numpy()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "iteration 50, objective = 0.573847\n",
      "iteration 100, objective = 0.419454\n"
     ]
    }
   ],
   "source": [
    "for i in range(100):\n",
    "    obj = trainingStepUnfair(modelUnfair, interactions)\n",
    "    if (i % 50 == 49): print(\"iteration \" + str(i+1) + \", objective = \" + str(obj))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Maximal marginal relevance (MMR)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Similarity between items (in this case cosine)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [],
   "source": [
    "def cosine(v,w):\n",
    "    return float(tf.tensordot(v, w, 1) / (tf.norm(v) * tf.norm(w)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [],
   "source": [
    "def sim(itemEmbeddings,i,j):\n",
    "    gamma_i = itemEmbeddings[i]\n",
    "    gamma_j = itemEmbeddings[j]\n",
    "    return cosine(gamma_i, gamma_j)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Find the most similar item among a candidate set"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [],
   "source": [
    "def maxSim(itemEmbeddings,i,seq):\n",
    "    if len(seq) == 0: return 0\n",
    "    return max([sim(itemEmbeddings,i,j) for j in seq])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Select a random user to receive recommendations"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [],
   "source": [
    "u = random.choice(list(userIDs.keys()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [],
   "source": [
    "itemSet = set(itemIDs.keys())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Define a function to get the next recommendation given an initial list, i.e., the mamimal marginally relevant item. Lambda (lamb) controls the tradeoff between compatibility an diversity."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [],
   "source": [
    "def getNextRec(model,compatScores,itemEmbeddings,seq,lamb):\n",
    "    scores = [(lamb * s - (1 - lamb) * maxSim(itemEmbeddings,i,seq), i)\n",
    "        for (s,i) in compatScores if not i in seq]\n",
    "    (maxScore,maxItem) = max(scores)\n",
    "    return maxItem"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Before re-ranking, generate a list of compatibility scores, i.e., a ranked list of items for a particular user"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [],
   "source": [
    "candidates = list(itemSet.difference(itemsPerUser[u]))\n",
    "compatScores = list(zip([float(f)\n",
    "    for f in modelUnfair.predictSample([userIDs[u]]*len(candidates),\n",
    "        [itemIDs[i] for i in candidates])], candidates))\n",
    "itemEmbeddings = dict(zip(candidates,\n",
    "    tf.nn.embedding_lookup(modelUnfair.gammaI, [itemIDs[i] for i in candidates])))\n",
    "\n",
    "compatScores.sort(reverse=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Generate a list of recommendations for the user by repeatedly calling the retrieving the maximal marginally relevant recommendation. First, just get the most relevant items (lambda=1) without encouraging diversity."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that this implementation is not particularly optimized, and takes several seconds to generate a list of recommendations."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Moonflower ESB',\n",
       " 'Augustiner BrÃ¤u MÃ¤rzen Bier',\n",
       " 'Perseguidor (Batch 5)',\n",
       " 'Saint Arnold Divine Reserve #3',\n",
       " 'Beer Hunter Brown Porter',\n",
       " 'Kuhnhenn Vintage Ale',\n",
       " 'Oktoberfest',\n",
       " 'Witte Noire',\n",
       " 'Golden Nugget',\n",
       " 'Spring Sour']"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "recs = []\n",
    "while len(recs) < 10:\n",
    "    i = getNextRec(modelUnfair,compatScores[:1000],itemEmbeddings,recs,1.0)\n",
    "    recs.append(i)\n",
    "[beerNames[i] for i in recs]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "More Recommendations for different relevance/diversity tradeoffs. Note that the tradeoff parameter is quite sensitive to the specific scale of the model parameters."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Moonflower ESB',\n",
       " \"He'Brew Jewbelation Bar Mitzvah (13) Barrel-Aged On Rye\",\n",
       " 'Perseguidor (Batch 5)',\n",
       " 'Houblon Noir',\n",
       " 'Bourbon Barrel Imperial Stout',\n",
       " 'Pumpkin Ale',\n",
       " 'The Great Dismal Black IPA',\n",
       " 'Leireken Wild Berries Belgian Ale',\n",
       " 'Saint Arnold Divine Reserve #3',\n",
       " 'California Rollin Plum Brew']"
      ]
     },
     "execution_count": 54,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "recs = []\n",
    "while len(recs) < 10:\n",
    "    i = getNextRec(model,compatScores[:1000],itemEmbeddings,recs,0.25)\n",
    "    recs.append(i)\n",
    "[beerNames[i] for i in recs]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['Moonflower ESB',\n",
       " \"He'Brew Jewbelation Bar Mitzvah (13) Barrel-Aged On Rye\",\n",
       " \"MacLean's Pale Ale\",\n",
       " 'Leireken Wild Berries Belgian Ale',\n",
       " 'Pumpkin Ale',\n",
       " 'Allagash FOUR - Bourbon Barrel Aged',\n",
       " 'Mount Desert Island Ginger',\n",
       " 'Brooklyn High Line Elevated Wheat',\n",
       " 'Oak Barrel Reverend',\n",
       " 'Saison De Lente, 100% Brett']"
      ]
     },
     "execution_count": 55,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "recs = []\n",
    "while len(recs) < 10:\n",
    "    i = getNextRec(model,compatScores[:1000],itemEmbeddings,recs,0.1)\n",
    "    recs.append(i)\n",
    "[beerNames[i] for i in recs]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Exercises"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 10.1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "First just try out a different similarity function (based on the inner product)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [],
   "source": [
    "def sim(itemEmbeddings,i,j):\n",
    "    gamma_i = itemEmbeddings[i]\n",
    "    gamma_j = itemEmbeddings[j]\n",
    "    return tf.sigmoid(-tf.tensordot(gamma_i, gamma_j, 1)).numpy()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [],
   "source": [
    "def maxSim(itemEmbeddings,i,seq):\n",
    "    if len(seq) == 0: return 0\n",
    "    return max([sim(itemEmbeddings,i,j) for j in seq])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [],
   "source": [
    "def getNextRec(model,compatScores,itemEmbeddings,seq,lamb):\n",
    "    rels = dict([(i,s) for (s,i) in compatScores if not i in seq])\n",
    "    divs = dict([(i,maxSim(itemEmbeddings,i,seq)) for (s,i) in compatScores if not i in seq])\n",
    "    scores = [(lamb * rels[i] - (1 - lamb) * divs[i], i) for (s,i) in compatScores if not i in seq]\n",
    "    (maxScore,maxItem) = max(scores)\n",
    "    return rels[maxItem],divs[maxItem],maxItem"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [],
   "source": [
    "u = random.choice(list(userIDs.keys()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {},
   "outputs": [],
   "source": [
    "candidates = list(itemSet.difference(itemsPerUser[u]))\n",
    "compatScores = list(zip([float(f)\n",
    "    for f in modelUnfair.predictSample([userIDs[u]]*len(candidates),\n",
    "        [itemIDs[i] for i in candidates])], candidates))\n",
    "itemEmbeddings = dict(zip(candidates,\n",
    "    tf.nn.embedding_lookup(modelUnfair.gammaI, [itemIDs[i] for i in candidates])))\n",
    "\n",
    "compatScores.sort(reverse=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Experiment with different relevance/diversity tradeoffs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {},
   "outputs": [],
   "source": [
    "xs = []\n",
    "ys = []\n",
    "for lamb in [1.0,0.99,0.9,0.75,0.5,0.25,0.1,0.01,0.001]:\n",
    "    rels = []\n",
    "    divs = []\n",
    "    recs = []\n",
    "    while len(recs) < 5:\n",
    "        r,d,i = getNextRec(modelUnfair,compatScores[:1000],itemEmbeddings,recs,lamb)\n",
    "        rels.append(r)\n",
    "        divs.append(d)\n",
    "        recs.append(i)\n",
    "    xs.append(sum(rels)/len(rels))\n",
    "    ys.append(sum(divs)/len(divs))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And plot the results"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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7t27c9lll3HUUUchNejWfWtCWSeInsD7OfMfkCQNM2vjvvzyS+666y6uueYaevXqxc9//nMGDRrkxFBGWlQndWVl5drpiooKKioqShaLmTXMsmXLuO2227juuuvo168f99xzDwMHDix1WK1GVVUVVVVVTbKvTIfakDQAqIyIwen8GCAi4poCZe8EHo6ISbXsy0NtmLVgixcv5pZbbmH8+PEccMABXHzxxexmyGEBAAAMkElEQVS7776lDqvVK+cXBs0A+kjqLakjMAx4qI7yrluatTILFizg0ksvZaeddmL27Nk89dRTTJo0ycmhBcg0QUTEauBsYCrwBjAxIuZKGivpKABJX5f0PnA88AtJs7OMycyax8cff8wFF1zALrvswt/+9jemTZvGfffdxx577FHq0KxIHs3VzJrUe++9x3XXXcd9993HSSedxPnnn0+vXr1KHVabVc5NTGbWRrz99tuceuqp7LXXXmy88cbMmTOHn//8504OLZgThJk1yhtvvMFJJ53EgAED6NmzJ2+99RbXXnst2267balDs0ZygjCzBnnllVc49thjOeSQQ+jXrx/z5s1j7NixbLnllqUOzZqIE4SZbZAXX3yRI444giFDhvCtb32LefPmMWbMGLp3717q0KyJtagH5cysNCKCp59+miuuuIL58+czevRoJk+eTKdOnUodmmXICcLMalUzsuoVV1zB559/zoUXXsgJJ5zARhttVOrQrBk4QZjZelavXs2DDz7IlVdeCcDFF1/Mscce65FV2xgnCDNbq2Zk1SuvvJLNNtuMyy+/nCOPPNID6LVRThBmtnZk1auvvpodd9yRm2++mUMOOcSJoY1zgjBrw5YuXcptt93GuHHj2HPPPbn33ns58MADSx2WlQknCLM2aPHixdx8882MHz+egQMHMmXKFA+eZ+txgjBrYVauXMkXX3yx9rNkyZKi52um3377bQ4//HCeeeYZdt9991J/JStTHqzPLEOrVq1i6dKlDbqI1za/atUqNtlkE7p167b2kztf17qa+R122IGePXuW+vRYM2jMYH1OEGap1atXs3Tp0ia5iNfMr1ixoqgLdzEX9ZrpTp06ufPYiuYEYW3OmjVrWLZsWYMu2rXNL1++nK5duxZ9oS7mIt+5c2dfzK2knCCsrEUEy5Yta9Qv8fz5ZcuW0aVLlw2+iNc1v/HGG9Ou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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fc3d0500358>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(xs,ys,color='k')\n",
    "plt.xlabel(\"average relevance\")\n",
    "plt.ylabel(\"average diversity\")\n",
    "plt.title(\"relevance/diversity tradeoff\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 10.2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Concentration effects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = []\n",
    "for x in parse(dataDir + \"goodreads_reviews_comics_graphic.json.gz\"):\n",
    "    del x['review_text']\n",
    "    data.append(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "metadata": {},
   "outputs": [],
   "source": [
    "random.shuffle(data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {},
   "outputs": [],
   "source": [
    "userIDs,itemIDs = {},{}\n",
    "revUIDs,revIIDs = {},{}\n",
    "for d in data:\n",
    "    u,i = d['user_id'],d['book_id']\n",
    "    if not u in userIDs:\n",
    "        userIDs[u] = len(userIDs)\n",
    "        revUIDs[userIDs[u]] = u\n",
    "    if not i in itemIDs:\n",
    "        itemIDs[i] = len(itemIDs)\n",
    "        revIIDs[itemIDs[i]] = i"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(59347, 89311)"
      ]
     },
     "execution_count": 66,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "nUsers,nItems = len(userIDs),len(itemIDs)\n",
    "nUsers,nItems"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {},
   "outputs": [],
   "source": [
    "Xiu = scipy.sparse.lil_matrix((nItems, nUsers))\n",
    "for d in data:\n",
    "    Xiu[itemIDs[d['book_id']],userIDs[d['user_id']]] = 1\n",
    "    \n",
    "Xui = scipy.sparse.csr_matrix(Xiu.T)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "40c3240a726747818a2b9c407135569c",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "HBox(children=(FloatProgress(value=0.0), HTML(value='')))"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "model = bpr.BayesianPersonalizedRanking(factors = 5)\n",
    "model.fit(Xiu)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Measure concentration in terms of the Gini coefficient"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {},
   "outputs": [],
   "source": [
    "interactionTuples = []\n",
    "itemsPerUser = defaultdict(list)\n",
    "usersPerItem = defaultdict(list)\n",
    "\n",
    "for d in data:\n",
    "    u,i = d['user_id'],d['book_id']\n",
    "    interactionTuples.append((userIDs[u],itemIDs[i]))\n",
    "    itemsPerUser[userIDs[u]].append(itemIDs[i])\n",
    "    usersPerItem[itemIDs[i]].append(userIDs[u])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "metadata": {},
   "outputs": [],
   "source": [
    "recommendationTuples1 = []\n",
    "for u in range(len(userIDs)):\n",
    "    A = model.recommend(u, Xui, N = len(itemsPerUser[u]))\n",
    "    for i, sc in A:\n",
    "        recommendationTuples1.append((u,i))    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "metadata": {},
   "outputs": [],
   "source": [
    "countsPerItemI = defaultdict(int)\n",
    "countsPerItemR1 = defaultdict(int)\n",
    "\n",
    "for u,i in interactionTuples:\n",
    "    countsPerItemI[i] += 1\n",
    "\n",
    "for u,i in recommendationTuples1:\n",
    "    countsPerItemR1[i] += 1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Aplly a small penalty for items that are highly recommended (resulting a reduction of concentration). This is essentially just a simple re-ranking strategy."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {},
   "outputs": [],
   "source": [
    "recommendationTuples2 = []\n",
    "for u in range(len(userIDs)):\n",
    "    N = len(itemsPerUser[u])\n",
    "    A = model.recommend(u, Xui, max(N,100))\n",
    "    Aadj = []\n",
    "    for i, sc in A:\n",
    "        Aadj.append((sc - 0.1*countsPerItemR1[i], i)) # Rerank by penalizing highly-recommended items\n",
    "    Aadj.sort(reverse=True)\n",
    "    for sc, i in Aadj[:N]:\n",
    "        recommendationTuples2.append((u,i))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "metadata": {},
   "outputs": [],
   "source": [
    "countsPerItemR2 = defaultdict(int)\n",
    "\n",
    "for u,i in recommendationTuples2:\n",
    "    countsPerItemR2[i] += 1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Compare interaction data, recommendations, and \"corrected\" recommendations in terms of concentration"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "metadata": {},
   "outputs": [],
   "source": [
    "sortPopularI = [(countsPerItemI[i], i) for i in countsPerItemI]\n",
    "sortPopularR1 = [(countsPerItemR1[i], i) for i in countsPerItemR1]\n",
    "sortPopularR2 = [(countsPerItemR2[i], i) for i in countsPerItemR2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {},
   "outputs": [],
   "source": [
    "sortPopularI.sort(reverse=True)\n",
    "sortPopularR1.sort(reverse=True)\n",
    "sortPopularR2.sort(reverse=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.7249546705882677"
      ]
     },
     "execution_count": 76,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "gini([x[0] for x in sortPopularI])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.7873034048204256"
      ]
     },
     "execution_count": 77,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "gini([x[0] for x in sortPopularR1])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.6873886143871535"
      ]
     },
     "execution_count": 78,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "gini([x[0] for x in sortPopularR2])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ultimately we got a model with lower concentration than the original data. Could adjust the penalty term to control the concentration amount."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 10.3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Measure parity in terms of beer ABV (alcohol level). Do high- (or low-) alcohol items tend to get recommended more than we would expect from interaction data?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "metadata": {},
   "outputs": [],
   "source": [
    "userIDs = {}\n",
    "itemIDs = {}\n",
    "itemsPerUser = defaultdict(list)\n",
    "usersPerItem = defaultdict(list)\n",
    "styles = {}\n",
    "beerNames = {}\n",
    "interactions = []\n",
    "\n",
    "atr = dict()\n",
    "\n",
    "for d in parse(dataDir + \"beer.json.gz\"):\n",
    "    if not 'user/gender' in d: continue\n",
    "    try:\n",
    "        a = d['beer/ABV'] > 7.5\n",
    "    except Exception as e:\n",
    "        continue\n",
    "    u = d['user/profileName']\n",
    "    i = d['beer/beerId']\n",
    "    r = d['review/overall']\n",
    "    styles[i] = d['beer/style']\n",
    "    beerNames[i] = d['beer/name']\n",
    "    if not u in userIDs: userIDs[u] = len(userIDs)\n",
    "    if not i in itemIDs: itemIDs[i] = len(itemIDs)\n",
    "    itemsPerUser[userIDs[u]].append(i)\n",
    "    usersPerItem[itemIDs[i]].append(u)\n",
    "    interactions.append((u,i,r))\n",
    "    atr[itemIDs[i]] = a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(8402, 34866)"
      ]
     },
     "execution_count": 80,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "nUsers,nItems = len(userIDs),len(itemIDs)\n",
    "nUsers,nItems"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Start by training a BPR model (using the implicit library)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "metadata": {},
   "outputs": [],
   "source": [
    "Xiu = scipy.sparse.lil_matrix((nItems, nUsers))\n",
    "for (u,i,r) in interactions:\n",
    "    Xiu[itemIDs[i],userIDs[u]] = 1\n",
    "    \n",
    "Xui = scipy.sparse.csr_matrix(Xiu.T)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "a514d610f8cf4678b8c89fc867fde48c",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "HBox(children=(FloatProgress(value=0.0), HTML(value='')))"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "model = bpr.BayesianPersonalizedRanking(factors = 5)\n",
    "model.fit(Xiu)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Frequency of positive (high alcohol) versus negative (low alcohol) among interactions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(216825, 395700)"
      ]
     },
     "execution_count": 83,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "apos,aneg = 0,0\n",
    "\n",
    "for (u,i,r) in interactions:\n",
    "    if atr[itemIDs[i]]:\n",
    "        apos += 1\n",
    "    else:\n",
    "        aneg += 1\n",
    "\n",
    "apos,aneg"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Frequency among recommendations (low alcohol items end up being slightly over-recommended)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(197201, 415324)"
      ]
     },
     "execution_count": 84,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "apos,aneg = 0,0\n",
    "\n",
    "for u in range(len(userIDs)):\n",
    "    A = model.recommend(u, Xui, N = len(itemsPerUser[u]))\n",
    "    for i, sc in A:\n",
    "        if atr[i]:\n",
    "            apos += 1\n",
    "        else:\n",
    "            aneg += 1\n",
    "            \n",
    "apos,aneg"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As in 10.2, correct using a re-ranking strategy with a simple penalty term (in this case encouraging high-alcohol items to be recommended). Again this could be adjusted to achieve the desired calibration."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 85,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(233451, 379074)"
      ]
     },
     "execution_count": 85,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "apos,aneg = 0,0\n",
    "\n",
    "for u in range(len(userIDs)):\n",
    "    N = len(itemsPerUser[u])\n",
    "    A = model.recommend(u, Xui, N*2)\n",
    "    Aadj = []\n",
    "    for i, sc in A:\n",
    "        Aadj.append((sc + 0.1*atr[i], i))\n",
    "    Aadj.sort(reverse=True)\n",
    "    for sc, i in Aadj[:N]:\n",
    "        if atr[i]:\n",
    "            apos += 1\n",
    "        else:\n",
    "            aneg += 1\n",
    "    \n",
    "apos,aneg"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
