Guides
Composite Features
In many cases, it's important to write features about a composite of two or more objects. For instance, in content recommendation, there are some features which may only be defined for (user, content) pair e.g. affinity of user with content creator. Even these complex features can be naturally modeled using Fennel featuresets. Here is a relatively complex example:
1@featureset
2class User:
3 id: int = feature(id=1)
4 name: str = feature(id=2)
5 ..
6
7@featureset
8class Post:
9 id: int = feature(id=1)
10 creator_uid: int = feature(id=2)
11 ...
12
13 @extractor
14 def creator(cls, ts: pd.Series[datetime], pids: pd.Series[id]) -> Series[creator_uid]:
15 <some code here>
16 ...
17
18@featureset
19class UserCreator:
20 # describes features for (uid, creator_uid) pairs
21 viewer: int = feature(id=1)
22 creator: int = feature(id=2)
23 affinity: float = feature(id=3)
24 ...
25
26 @extractor
27 def affinity_fn(cls, ts: pd.Series[datetime], viewers: pd.Series[viewer],
28 creators: pd.Series[creator]) -> Series[affinity]:
29 <some code here>
30 ...
31
32@featureset
33class UserPost:
34 # describes features for (uid, pid) pairs
35 uid: int = feature(id=1)
36 pid: int = feature(id=2)
37 viewer_author_affinity = feature(id=3)
38 ...
39
40 @extractor
41 def fn(cls, ts: pd.Series[datetime], uids: pd.Series[uid], pids: pd.Series[pid]):
42 creators = Post.creator(ts, pids)
43 return UserCreator.affinity_fn(ts, uids, creators)
44
45
A lot is happening here. In addition to featureset for User
, and Post
, this example also has a couple of composite featuresets -- UserCreator
to capture features for (uid, creator) pairs and UserPost
for capturing features of (uid, post) pairs.
Further, extractors can depend on extractors of other featuresets - here is line 42, the extractor first uses an extractor of Post
featureset to get creators for the posts and then users an extractor of UserCreator
to get the affinity between those users and creators.
This way, it's possible to build very complex features by reusing machinery built for other existing features. And Fennel is smart enough to figure out the best way to resolve dependencies across featuresets (or throw an error if they can't be satisfied).