Background
I'm trying to analyse source code to look for ways of improving modularity. To that end I have constructed a directed graph of identifiers (functions, types etc) with edges indicating a reference relationship. So if function foo calls bar then there will be a corresponding edge in the graph from foo to bar.
I've identified the strongly connected components (i.e. mutually recursive functions and types) in this graph, which yields a DAG of components.
I want to try to cluster these components according to the modules that use them. For instance if function foo is called from code in Module_A and Module_B then its module user list will be those two modules. (Note that this is distinct from any import lists: I'm tracking actual use, not importation).
My theory is that in a well-designed module all the components will have similar lists of user modules. But in a badly designed module the user module lists will be very different.
I'm hoping that it will be possible to cluster components according to user lists, so that components with similar lists can then be refactored into modules with higher cohesion.
The Question
Given a set of components, each with a set of associated discrete tag values (user modules in this case), is there a good algorithm for identifying clusters of components that share similar sets of tags?
I've googled clustering algorithms, but all the ones I have seen seem to be about clustering by ordinal or real values (e.g. k-means).