| 268 |      1 | % !TEX program = xelatex
 | 
| 6 |      2 | \documentclass{article}
 | 
| 39 |      3 | \usepackage{../style}
 | 
| 166 |      4 | \usepackage{disclaimer}
 | 
| 202 |      5 | \usepackage{../langs}
 | 
| 6 |      6 | 
 | 
|  |      7 | \begin{document}
 | 
|  |      8 | 
 | 
|  |      9 | 
 | 
| 329 |     10 | %% should ask to lower case the words.
 | 
|  |     11 | 
 | 
| 306 |     12 | \section*{Part 7 (Scala)}
 | 
| 6 |     13 | 
 | 
| 264 |     14 | \mbox{}\hfill\textit{``What one programmer can do in one month,}\\
 | 
|  |     15 | \mbox{}\hfill\textit{two programmers can do in two months.''}\smallskip\\
 | 
| 276 |     16 | \mbox{}\hfill\textit{ --- Frederick P.~Brooks (author of The Mythical Man-Month)}\bigskip\medskip
 | 
| 264 |     17 | 
 | 
|  |     18 | \noindent
 | 
| 284 |     19 | You are asked to implement Scala programs for measuring similarity in
 | 
|  |     20 | texts, and for recommending movies according to a ratings list. The
 | 
|  |     21 | preliminary part~(4\%) is due on \cwSEVEN{} at 4pm; the core part is due
 | 
|  |     22 | on \cwSEVENa{} at 4pm.   Note the core part might include material you
 | 
|  |     23 | have not yet seen in the first two lectures. \bigskip
 | 
| 50 |     24 | 
 | 
| 166 |     25 | \IMPORTANT{}
 | 
| 202 |     26 | 
 | 
|  |     27 | \noindent
 | 
| 144 |     28 | Also note that the running time of each part will be restricted to a
 | 
| 202 |     29 | maximum of 30 seconds on my laptop.
 | 
| 39 |     30 | 
 | 
| 166 |     31 | \DISCLAIMER{}
 | 
| 39 |     32 | 
 | 
|  |     33 | 
 | 
| 202 |     34 | \subsection*{Reference Implementation}
 | 
| 45 |     35 | 
 | 
| 306 |     36 | Like the C++ part, the Scala part works like this: you
 | 
| 202 |     37 | push your files to GitHub and receive (after sometimes a long delay) some
 | 
| 306 |     38 | automated feedback. In the end we will take a snapshot of the submitted files and
 | 
| 268 |     39 | apply an automated marking script to them.\medskip
 | 
| 45 |     40 | 
 | 
| 268 |     41 | \noindent
 | 
| 306 |     42 | In addition, the Scala part comes with reference
 | 
|  |     43 | implementations in form of \texttt{jar}-files. This allows you to run
 | 
| 202 |     44 | any test cases on your own computer. For example you can call Scala on
 | 
|  |     45 | the command line with the option \texttt{-cp docdiff.jar} and then
 | 
|  |     46 | query any function from the template file. Say you want to find out
 | 
| 203 |     47 | what the function \texttt{occurrences} produces: for this you just need
 | 
| 202 |     48 | to prefix it with the object name \texttt{CW7a} (and \texttt{CW7b}
 | 
|  |     49 | respectively for \texttt{danube.jar}).  If you want to find out what
 | 
|  |     50 | these functions produce for the list \texttt{List("a", "b", "b")},
 | 
|  |     51 | you would type something like:
 | 
| 6 |     52 | 
 | 
| 202 |     53 | \begin{lstlisting}[language={},numbers=none,basicstyle=\ttfamily\small]
 | 
|  |     54 | $ scala -cp docdiff.jar
 | 
|  |     55 |   
 | 
| 203 |     56 | scala> CW7a.occurrences(List("a", "b", "b"))
 | 
| 202 |     57 | ...
 | 
|  |     58 | \end{lstlisting}%$
 | 
|  |     59 | 
 | 
|  |     60 | \subsection*{Hints}
 | 
| 6 |     61 | 
 | 
| 203 |     62 | \noindent
 | 
| 306 |     63 | \textbf{For Preliminary Part:} useful operations involving regular
 | 
| 203 |     64 | expressions:
 | 
|  |     65 | \[\texttt{reg.findAllIn(s).toList}\]
 | 
|  |     66 | \noindent finds all
 | 
|  |     67 | substrings in \texttt{s} according to a regular regular expression
 | 
|  |     68 | \texttt{reg}; useful list operations: \texttt{.distinct}
 | 
|  |     69 | removing duplicates from a list, \texttt{.count} counts the number of
 | 
|  |     70 | elements in a list that satisfy some condition, \texttt{.toMap}
 | 
|  |     71 | transfers a list of pairs into a Map, \texttt{.sum} adds up a list of
 | 
|  |     72 | integers, \texttt{.max} calculates the maximum of a list.\bigskip
 | 
| 45 |     73 | 
 | 
| 203 |     74 | \noindent
 | 
| 306 |     75 | \textbf{For Core Part:} use \texttt{.split(",").toList} for splitting
 | 
| 203 |     76 | strings according to commas (similarly $\backslash$\texttt{n}),
 | 
| 301 |     77 | \texttt{.getOrElse(..,..)} allows to query a Map, but also gives a
 | 
| 203 |     78 | default value if the Map is not defined, a Map can be `updated' by
 | 
|  |     79 | using \texttt{+}, \texttt{.contains} and \texttt{.filter} can test whether
 | 
|  |     80 | an element is included in a list, and respectively filter out elements in a list,
 | 
|  |     81 | \texttt{.sortBy(\_.\_2)} sorts a list of pairs according to the second
 | 
|  |     82 | elements in the pairs---the sorting is done from smallest to highest,
 | 
|  |     83 | \texttt{.take(n)} for taking some elements in a list (takes fewer if the list
 | 
|  |     84 | contains less than \texttt{n} elements).
 | 
| 39 |     85 | 
 | 
|  |     86 | 
 | 
| 202 |     87 | \newpage
 | 
| 284 |     88 | \subsection*{Preliminary Part (4 Marks, file docdiff.scala)}
 | 
| 6 |     89 | 
 | 
| 203 |     90 | It seems source code plagiarism---stealing and submitting someone
 | 
|  |     91 | else's code---is a serious problem at other
 | 
|  |     92 | universities.\footnote{Surely, King's students, after all their
 | 
|  |     93 |   instructions and warnings, would never commit such an offence. Yes?}
 | 
|  |     94 | Detecting such plagiarism is time-consuming and disheartening for
 | 
|  |     95 | lecturers at those universities. To aid these poor souls, let's
 | 
|  |     96 | implement in this part a program that determines the similarity
 | 
|  |     97 | between two documents (be they source code or texts in English). A
 | 
|  |     98 | document will be represented as a list of strings.
 | 
| 6 |     99 | 
 | 
|  |    100 | 
 | 
| 202 |    101 | \subsection*{Tasks}
 | 
| 45 |    102 | 
 | 
|  |    103 | \begin{itemize}
 | 
| 203 |    104 | \item[(1)] Implement a function that `cleans' a string by finding all
 | 
|  |    105 |   (proper) words in this string. For this use the regular expression
 | 
| 276 |    106 |   \texttt{\textbackslash{}w+} for recognising words and the library function
 | 
|  |    107 |   \texttt{findAllIn}. The function should return a document (a list of
 | 
| 203 |    108 |   strings).\\
 | 
| 202 |    109 |   \mbox{}\hfill [1 Mark]
 | 
| 45 |    110 | 
 | 
| 203 |    111 | \item[(2)] In order to compute the overlap between two documents, we
 | 
| 202 |    112 |   associate each document with a \texttt{Map}. This Map represents the
 | 
| 203 |    113 |   strings in a document and how many times these strings occur in the
 | 
| 202 |    114 |   document. A simple (though slightly inefficient) method for counting
 | 
| 203 |    115 |   the number of string-occurrences in a document is as follows: remove
 | 
| 202 |    116 |   all duplicates from the document; for each of these (unique)
 | 
|  |    117 |   strings, count how many times they occur in the original document.
 | 
| 203 |    118 |   Return a Map associating strings with occurrences. For example
 | 
| 6 |    119 | 
 | 
| 45 |    120 |   \begin{center}
 | 
| 203 |    121 |   \pcode{occurrences(List("a", "b", "b", "c", "d"))}
 | 
| 45 |    122 |   \end{center}
 | 
|  |    123 | 
 | 
| 202 |    124 |   produces \pcode{Map(a -> 1, b -> 2, c -> 1, d -> 1)} and
 | 
| 45 |    125 | 
 | 
|  |    126 |   \begin{center}
 | 
| 203 |    127 |   \pcode{occurrences(List("d", "b", "d", "b", "d"))}
 | 
| 48 |    128 |   \end{center}
 | 
| 45 |    129 | 
 | 
| 202 |    130 |   produces \pcode{Map(d -> 3, b -> 2)}.\hfill[1 Mark]
 | 
| 6 |    131 | 
 | 
| 203 |    132 | \item[(3)] You can think of the Maps calculated under (2) as memory-efficient
 | 
| 202 |    133 |   representations of sparse ``vectors''. In this subtask you need to
 | 
| 203 |    134 |   implement the \emph{product} of two such vectors, sometimes also called
 | 
|  |    135 |   \emph{dot product} of two vectors.\footnote{\url{https://en.wikipedia.org/wiki/Dot_product}}
 | 
| 148 |    136 | 
 | 
| 203 |    137 |   For this dot product, implement a function that takes two documents
 | 
| 202 |    138 |   (\texttt{List[String]}) as arguments. The function first calculates
 | 
|  |    139 |   the (unique) strings in both. For each string, it multiplies the
 | 
| 203 |    140 |   corresponding occurrences in each document. If a string does not
 | 
|  |    141 |   occur in one of the documents, then the product for this string is zero. At the end
 | 
|  |    142 |   you need to add up all products. For the two documents in (2) the dot
 | 
|  |    143 |   product is 7, because
 | 
| 45 |    144 | 
 | 
|  |    145 |   \[
 | 
| 202 |    146 |     \underbrace{1 * 0}_{"a"} \;\;+\;\;
 | 
|  |    147 |     \underbrace{2 * 2}_{"b"} \;\;+\;\;
 | 
|  |    148 |     \underbrace{1 * 0}_{"c"} \;\;+\;\;
 | 
| 203 |    149 |     \underbrace{1 * 3}_{"d"} \qquad = 7
 | 
| 202 |    150 |   \]  
 | 
|  |    151 |   
 | 
|  |    152 |   \hfill\mbox{[1 Mark]}
 | 
|  |    153 | 
 | 
|  |    154 | \item[(4)] Implement first a function that calculates the overlap
 | 
|  |    155 |   between two documents, say $d_1$ and $d_2$, according to the formula
 | 
|  |    156 | 
 | 
|  |    157 |   \[
 | 
|  |    158 |   \texttt{overlap}(d_1, d_2) = \frac{d_1 \cdot d_2}{max(d_1^2, d_2^2)}  
 | 
| 45 |    159 |   \]
 | 
|  |    160 | 
 | 
| 316 |    161 |   where $d_1^2$ means $d_1 \cdot d_1$ and so on.
 | 
| 203 |    162 |   You can expect this function to return a \texttt{Double} between 0 and 1. The
 | 
| 202 |    163 |   overlap between the lists in (2) is $0.5384615384615384$.
 | 
|  |    164 | 
 | 
| 203 |    165 |   Second, implement a function that calculates the similarity of
 | 
|  |    166 |   two strings, by first extracting the substrings using the clean
 | 
|  |    167 |   function from (1)
 | 
|  |    168 |   and then calculating the overlap of the resulting documents.\\
 | 
|  |    169 |   \mbox{}\hfill\mbox{[1 Mark]}
 | 
|  |    170 | \end{itemize}\bigskip
 | 
|  |    171 | 
 | 
|  |    172 | 
 | 
| 284 |    173 | \subsection*{Core Part (6 Marks, file danube.scala)}
 | 
| 203 |    174 | 
 | 
|  |    175 | You are creating Danube.co.uk which you hope will be the next big thing
 | 
|  |    176 | in online movie renting. You know that you can save money by
 | 
|  |    177 | anticipating what movies people will rent; you will pass these savings
 | 
|  |    178 | on to your users by offering a discount if they rent movies that
 | 
|  |    179 | Danube.co.uk recommends.  
 | 
| 48 |    180 | 
 | 
| 203 |    181 | Your task is to generate \emph{two} movie recommendations for every
 | 
|  |    182 | movie a user rents. To do this, you calculate what other
 | 
|  |    183 | renters, who also watched this movie, suggest by giving positive ratings.
 | 
|  |    184 | Of course, some suggestions are more popular than others. You need to find
 | 
|  |    185 | the two most-frequently suggested movies. Return fewer recommendations,
 | 
|  |    186 | if there are fewer movies suggested.
 | 
|  |    187 | 
 | 
|  |    188 | The calculations will be based on the small datasets which the research lab
 | 
|  |    189 | GroupLens provides for education and development purposes.
 | 
|  |    190 | 
 | 
|  |    191 | \begin{center}
 | 
|  |    192 | \url{https://grouplens.org/datasets/movielens/}
 | 
|  |    193 | \end{center}
 | 
|  |    194 | 
 | 
|  |    195 | \noindent
 | 
|  |    196 | The slightly adapted CSV-files should be downloaded in your Scala
 | 
|  |    197 | file from the URLs:
 | 
| 148 |    198 | 
 | 
|  |    199 | 
 | 
| 203 |    200 | \begin{center}
 | 
|  |    201 | \begin{tabular}{ll}  
 | 
|  |    202 |   \url{https://nms.kcl.ac.uk/christian.urban/ratings.csv} & (940 KByte)\\
 | 
|  |    203 |   \url{https://nms.kcl.ac.uk/christian.urban/movies.csv}  & (280 KByte)\\
 | 
|  |    204 | \end{tabular}
 | 
|  |    205 | \end{center}
 | 
|  |    206 | 
 | 
|  |    207 | \noindent
 | 
|  |    208 | The ratings.csv file is organised as userID, 
 | 
|  |    209 | movieID, and rating (which is between 0 and 5, with \emph{positive} ratings
 | 
|  |    210 | being 4 and 5). The file movie.csv is organised as
 | 
|  |    211 | movieID and full movie name. Both files still contain the usual
 | 
|  |    212 | CSV-file header (first line). In this part you are asked
 | 
|  |    213 | to implement functions that process these files. If bandwidth
 | 
|  |    214 | is an issue for you, download the files locally, but in the submitted
 | 
|  |    215 | version use \texttt{Source.fromURL} instead of \texttt{Source.fromFile}.
 | 
|  |    216 | 
 | 
|  |    217 | \subsection*{Tasks}
 | 
| 45 |    218 | 
 | 
| 203 |    219 | \begin{itemize}
 | 
|  |    220 | \item[(1)] Implement the function \pcode{get_csv_url} which takes an
 | 
|  |    221 |   URL-string as argument and requests the corresponding file. The two
 | 
|  |    222 |   URLs of interest are \pcode{ratings_url} and \pcode{movies_url},
 | 
|  |    223 |   which correspond to CSV-files mentioned above.  The function should
 | 
|  |    224 |   return the CSV-file appropriately broken up into lines, and the
 | 
|  |    225 |   first line should be dropped (that is omit the header of the CSV-file).
 | 
|  |    226 |   The result is a list of strings (the lines in the file). In case
 | 
|  |    227 |   the url does not produce a file, return the empty list.\\
 | 
|  |    228 |   \mbox{}\hfill [1 Mark]
 | 
|  |    229 | 
 | 
|  |    230 | \item[(2)] Implement two functions that process the (broken up)
 | 
|  |    231 |   CSV-files from (1). The \pcode{process_ratings} function filters out all
 | 
|  |    232 |   ratings below 4 and returns a list of (userID, movieID) pairs. The
 | 
|  |    233 |   \pcode{process_movies} function returns a list of (movieID, title) pairs.\\
 | 
|  |    234 |   \mbox{}\hfill [1 Mark]
 | 
| 268 |    235 | %\end{itemize}  
 | 
|  |    236 | %  
 | 
|  |    237 | %
 | 
|  |    238 | %\subsection*{Part 3 (4 Marks, file danube.scala)}
 | 
|  |    239 | %
 | 
|  |    240 | %\subsection*{Tasks}
 | 
|  |    241 | %
 | 
|  |    242 | %\begin{itemize}
 | 
| 203 |    243 | \item[(3)] Implement a kind of grouping function that calculates a Map
 | 
|  |    244 |   containing the userIDs and all the corresponding recommendations for
 | 
| 259 |    245 |   this user (list of movieIDs). This should be implemented in a
 | 
|  |    246 |   tail-recursive fashion using a Map as accumulator. This Map is set to
 | 
| 203 |    247 |   \pcode{Map()} at the beginning of the calculation. For example
 | 
|  |    248 | 
 | 
|  |    249 | \begin{lstlisting}[numbers=none]
 | 
|  |    250 | val lst = List(("1", "a"), ("1", "b"),
 | 
|  |    251 |                ("2", "x"), ("3", "a"),
 | 
|  |    252 |                ("2", "y"), ("3", "c"))
 | 
|  |    253 | groupById(lst, Map())
 | 
|  |    254 | \end{lstlisting}
 | 
|  |    255 | 
 | 
|  |    256 | returns the ratings map
 | 
|  |    257 | 
 | 
|  |    258 | \begin{center}
 | 
|  |    259 |   \pcode{Map(1 -> List(b, a), 2 -> List(y, x), 3 -> List(c, a))}.
 | 
|  |    260 | \end{center}
 | 
|  |    261 | 
 | 
|  |    262 | \noindent
 | 
|  |    263 | In which order the elements of the list are given is unimportant.\\
 | 
|  |    264 | \mbox{}\hfill [1 Mark]
 | 
| 45 |    265 | 
 | 
| 203 |    266 | \item[(4)] Implement a function that takes a ratings map and a movieID
 | 
| 210 |    267 |   as arguments.  The function calculates all suggestions containing the
 | 
|  |    268 |   given movie in its recommendations. It returns a list of all these
 | 
| 203 |    269 |   recommendations (each of them is a list and needs to have the given
 | 
|  |    270 |   movie deleted, otherwise it might happen we recommend the same movie
 | 
|  |    271 |   ``back''). For example for the Map from above and the movie
 | 
|  |    272 |   \pcode{"y"} we obtain \pcode{List(List("x"))}, and for the movie
 | 
|  |    273 |   \pcode{"a"} we get \pcode{List(List("b"), List("c"))}.\\
 | 
|  |    274 |   \mbox{}\hfill [1 Mark]
 | 
| 148 |    275 | 
 | 
| 203 |    276 | \item[(5)] Implement a suggestions function which takes a ratings map
 | 
|  |    277 |   and a movieID as arguments. It calculates all the recommended movies
 | 
|  |    278 |   sorted according to the most frequently suggested movie(s) sorted
 | 
|  |    279 |   first. This function returns \emph{all} suggested movieIDs as a list of
 | 
|  |    280 |   strings.\\
 | 
|  |    281 |   \mbox{}\hfill [1 Mark]
 | 
| 148 |    282 | 
 | 
| 203 |    283 | \item[(6)]  
 | 
|  |    284 |   Implement then a recommendation function which generates a maximum
 | 
|  |    285 |   of two most-suggested movies (as calculated above). But it returns
 | 
|  |    286 |   the actual movie name, not the movieID. If fewer movies are recommended,
 | 
|  |    287 |   then return fewer than two movie names.\\
 | 
|  |    288 |   \mbox{}\hfill [1 Mark]
 | 
|  |    289 | \end{itemize}
 | 
| 6 |    290 | 
 | 
| 268 |    291 | \end{document} 
 | 
| 6 |    292 | 
 | 
|  |    293 | %%% Local Variables: 
 | 
|  |    294 | %%% mode: latex
 | 
|  |    295 | %%% TeX-master: t
 | 
|  |    296 | %%% End: 
 |