Skip to content

Full Graph

Please remember that this class is a subclass of NXTripartiteGraph, so it inherits all its methods. You can check their documentation as well!

NXFullGraph(source_frame=None, item_exo_properties=None, item_contents_dir=None, user_exo_properties=None, user_contents_dir=None, link_label=None)

Bases: NXTripartiteGraph, FullDiGraph

Class that implements a Full graph through networkx library.

Info

A Full Graph is a graph which doesn't impose any particular restriction

It creates a graph from an initial Rating object.

Consider the following matrix representation of the Rating object

    +------+-----------+-------+
    | User |   Item    | Score |
    +------+-----------+-------+
    | u1   | Tenet     |     4 |
    | u2   | Inception |     5 |
    | ...  | ...       |   ... |
    +------+-----------+-------+

The graph will be created with the following interactions:

             4
        u1 -----> Tenet
             5
        u2 -----> Inception

where u1 and u2 become User nodes and Tenet and Inception become Item nodes, with the edge weighted depending on the score given

If the link_label parameter is specified, then each link between users and items will be labeled with the label specified (e.g. link_label='score'):

        (4, 'score')
    u1 -------------> Tenet
        (5, 'score')
    u2 -------------> Inception

Then the framework tries to load 'Tenet' and 'Inception' from the item_contents_dir and 'u1' and 'u2' from user_contents_dir if they are specified and if it succeeds, adds in the graph their loaded properties as specified in the item_exo_properties parameter and user_exo_properties.

Load exogenous properties

In order to load properties in the graph, we must specify where users (and/or) items are serialized and which properties to add (the following is the same for item_exo_properties):

  • If user_exo_properties is specified as a set, then the graph will try to load all properties from said exogenous representation
{'my_exo_id'}
  • If user_exo_properties is specified as a dict, then the graph will try to load said properties from said exogenous representation
{'my_exo_id': ['my_prop1', 'my_prop2']]}
PARAMETER DESCRIPTION
source_frame

The initial Ratings object needed to create the graph

TYPE: Ratings DEFAULT: None

item_exo_properties

Set or Dict which contains representations to load from items. Use a Set if you want to load all properties from specific representations, or use a Dict if you want to choose which properties to load from specific representations

TYPE: Union[Dict, set] DEFAULT: None

item_contents_dir

The path containing items serialized with the Content Analyzer

TYPE: str DEFAULT: None

user_exo_properties

Set or Dict which contains representations to load from items. Use a Set if you want to load all properties from specific representations, or use a Dict if you want to choose which properties to load from specific representations

TYPE: Union[Dict, set] DEFAULT: None

user_contents_dir

The path containing users serialized with the Content Analyzer

TYPE: str DEFAULT: None

link_label

If specified, each link will be labeled with the given label. Default is None

TYPE: str DEFAULT: None

Source code in clayrs/recsys/graphs/nx_implementation/nx_full_graphs.py
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
def __init__(self, source_frame: Ratings = None,
             item_exo_properties: Union[Dict, set] = None,
             item_contents_dir: str = None,
             user_exo_properties: Union[Dict, set] = None,
             user_contents_dir: str = None,
             link_label: str = None):

    NXTripartiteGraph.__init__(self, source_frame, item_exo_properties, item_contents_dir, link_label)

    if user_exo_properties and not user_contents_dir:
        logger.warning("`user_exo_properties` parameter set but `user_contents_dir` is None! "
                       "No property will be loaded")
    elif not user_exo_properties and user_contents_dir:
        logger.warning("`user_contents_dir` parameter set but `user_exo_properties` is None! "
                       "No property will be loaded")

    if source_frame is not None and user_contents_dir is not None and user_exo_properties is not None:
        self.add_node_with_prop([UserNode(user_id) for user_id in source_frame.unique_user_id_column],
                                user_exo_properties,
                                user_contents_dir)

Creates a weighted link connecting the 'start_node' to the 'final_node' Both nodes must be present in the graph before calling this method

'weight' and 'label' are optional parameters, if not specified default values will be used.

PARAMETER DESCRIPTION
start_node

starting node of the link

TYPE: object

final_node

ending node of the link

TYPE: object

weight

weight of the link, default is 0.5

TYPE: float DEFAULT: None

label

label of the link, default is 'score_label'

TYPE: str DEFAULT: None

Source code in clayrs/recsys/graphs/nx_implementation/nx_full_graphs.py
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
def add_link(self, start_node: Union[Node, List[Node]], final_node: Union[Node, List[Node]],
             weight: float = None, label: str = None, timestamp: str = None):
    """
    Creates a weighted link connecting the 'start_node' to the 'final_node'
    Both nodes must be present in the graph before calling this method

    'weight' and 'label' are optional parameters, if not specified default values
    will be used.

    Args:
        start_node (object): starting node of the link
        final_node (object): ending node of the link
        weight (float): weight of the link, default is 0.5
        label (str): label of the link, default is 'score_label'
    """
    if not isinstance(start_node, list):
        start_node = [start_node]

    if not isinstance(final_node, list):
        final_node = [final_node]

    self.add_node(start_node)
    self.add_node(final_node)

    not_none_dict = {}
    if label is not None:
        not_none_dict['label'] = label
    if weight is not None:
        not_none_dict['weight'] = weight
    if timestamp is not None:
        not_none_dict['timestamp'] = timestamp

    self._graph.add_edges_from(zip(start_node, final_node),
                               **not_none_dict)

add_node(node)

Adds one or multiple Node objects to the graph. Since this is a Full Graph, any category of node is allowed

No duplicates are allowed, but different category nodes with same id are (e.g. ItemNode('1') and UserNode('1'))

PARAMETER DESCRIPTION
node

Node(s) object(s) that needs to be added to the graph

TYPE: Union[Node, List[Node]]

Source code in clayrs/recsys/graphs/nx_implementation/nx_full_graphs.py
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
def add_node(self, node: Union[Node, List[Node]]):
    """
    Adds one or multiple Node objects to the graph.
    Since this is a Full Graph, any category of node is allowed

    No duplicates are allowed, but different category nodes with same id are (e.g. `ItemNode('1')` and
    `UserNode('1')`)

    Args:
        node: Node(s) object(s) that needs to be added to the graph
    """
    if not isinstance(node, list):
        node = [node]

    self._graph.add_nodes_from(node)

add_node_with_prop(node, exo_properties, contents_dir, content_filename=None)

Adds one or multiple Node objects and its/their properties to the graph Since this is a Full Graph, no restriction are imposed and you can add any category of node together with its properties.

In order to load properties in the graph, we must specify where contents are serialized and which properties to add (the following is the same for item_exo_properties):

  • If exo_properties is specified as a set, then the graph will try to load all properties from said exogenous representation
{'my_exo_id'}
  • If exo_properties is specified as a dict, then the graph will try to load said properties from said exogenous representation
{'my_exo_id': ['my_prop1', 'my_prop2']]}

In case you want your node to have a different id from serialized contents, via the content_filename parameter you can specify what is the filename of the node that you are adding, e.g.

item_to_add = ItemNode('different_id')

# content_filename is 'item_serialized_1.xz'

graph.add_node_with_prop(item_to_add, ..., content_filename='item_serialized_1')

In case you are adding a list of nodes, you can specify the filename for each node in the list.

PARAMETER DESCRIPTION
node

Node(s) object(s) that needs to be added to the graph along with their properties

TYPE: Union[Node, List[Node]]

exo_properties

Set or Dict which contains representations to load from items. Use a Set if you want to load all properties from specific representations, or use a Dict if you want to choose which properties to load from specific representations

TYPE: Union[Dict, set]

contents_dir

The path containing items serialized with the Content Analyzer

TYPE: str

content_filename

Filename(s) of the node(s) to add

TYPE: Union[str, List[str]] DEFAULT: None

RAISES DESCRIPTION
ValueError

Exception raised when one of the node to add to the graph with their properties is not an ItemNode

Source code in clayrs/recsys/graphs/nx_implementation/nx_full_graphs.py
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
def add_node_with_prop(self, node: Union[Node, List[Node]], exo_properties: Union[Dict, set],
                       contents_dir: str,
                       content_filename: Union[str, List[str]] = None):
    """
    Adds one or multiple Node objects and its/their properties to the graph
    Since this is a Full Graph, no restriction are imposed and you can add any category of node together with its
    properties.

    In order to load properties in the graph, we must specify where contents are serialized and ***which
    properties to add*** (the following is the same for *item_exo_properties*):

    *   If *exo_properties* is specified as a **set**, then the graph will try to load **all properties**
    from **said exogenous representation**

    ```python
    {'my_exo_id'}
    ```

    *   If *exo_properties* is specified as a **dict**, then the graph will try to load **said properties**
    from **said exogenous representation**

    ```python
    {'my_exo_id': ['my_prop1', 'my_prop2']]}
    ```

    In case you want your node to have a different id from serialized contents, via the `content_filename` parameter
    you can specify what is the filename of the node that you are adding, e.g.

    ```
    item_to_add = ItemNode('different_id')

    # content_filename is 'item_serialized_1.xz'

    graph.add_node_with_prop(item_to_add, ..., content_filename='item_serialized_1')
    ```

    In case you are adding a list of nodes, you can specify the filename for each node in the list.

    Args:
        node: Node(s) object(s) that needs to be added to the graph along with their properties
        exo_properties: Set or Dict which contains representations to load from items. Use a `Set` if you want
            to load all properties from specific representations, or use a `Dict` if you want to choose which
            properties to load from specific representations
        contents_dir: The path containing items serialized with the Content Analyzer
        content_filename: Filename(s) of the node(s) to add

    Raises:
        ValueError: Exception raised when one of the node to add to the graph with their properties is not
            an ItemNode
    """
    def node_prop_link_generator():
        for n, id in zip(progbar, content_filename):
            item: Content = loaded_items.get(id)

            if item is not None:
                exo_props = self._get_exo_props(exo_properties, item)

                single_item_prop_edges = [(n,
                                           PropertyNode(prop_dict[prop]),
                                           {'label': prop})
                                          for prop_dict in exo_props for prop in prop_dict]
            else:
                single_item_prop_edges = []

            yield from single_item_prop_edges

    if not isinstance(node, list):
        node = [node]

    if isinstance(exo_properties, set):
        exo_properties = dict.fromkeys(exo_properties, None)

    if content_filename is None:
        content_filename = [n.value for n in node]

    if not isinstance(content_filename, list):
        content_filename = [content_filename]

    loaded_items = LoadedContentsDict(contents_dir, contents_to_load=set(content_filename))
    with get_progbar(node) as progbar:
        progbar.set_description("Creating Node->Properties links")

        self._graph.add_edges_from((tuple_to_add for tuple_to_add in node_prop_link_generator()))