As it stands, this question is pretty vague. You should at least try to define your own shared-interest function. But since you alluded to Graph functions, we could use the built-in FindGraphPartition function. Applying it to a Graph weighted by a shared-interest function might give you what you want. Let's start by putting together the basic data:
prefData =
Sort@Flatten@{{"john" -> "physics"}, {"john" ->
"chemistry"}, {"jane" -> "physics"}, {"jane" ->
"biology"}, {"peter" -> "biology"}, {"peter" ->
"chemistry"}, {"peter" -> "mathematics"}, {"david" ->
"mathematics"}, {"paul" -> "chemistry"}, {"liz" ->
"chemistry"}, {"liz" -> "mathematics"}, {"liz" -> "physics"}};
prefsByPerson = Merge[prefData, Identity];
Now let's define an interest function to use to weight the Graph edges (I just used the size of the overlap):
InterestOverlapWeight[map_][a_, b_] :=
Length[Intersection @@ Lookup[map, {a, b}, {}]]
Testing this out:
InterestOverlapWeight[prefsByPerson]["david", "liz"]
(* gives 1 *)
Let's create pairs of persons (to eventually become edges):
personPairs =
DeleteCases[Tuples[Keys@prefsByPerson, 2], {name_, name_}]
From this, let's create the edges and weights:
rawEdges = UndirectedEdge @@@ personPairs;
rawWeights = InterestOverlapWeight[prefsByPerson] @@@ personPairs;
sharedInterestEdges = Pick[rawEdges, rawWeights, _?(GreaterThan[0])];
sharedInterestWeights = DeleteCases[rawWeights, 0];
And now we can create a Graph:
graphWeightedBySharedInterests =
Graph[sharedInterestEdges, EdgeWeight -> sharedInterestWeights]
With this, we can find groups that should give higher interest weights:
FindGraphPartition[graphWeightedBySharedInterests]
Many tweaks could be made to this basic strategy to get something that you're hopefully satisfied with.