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