02 — Feature Types in Recommender Systems
“Garbage in, garbage out — but in RecSys, the right features are 80% of the lift.”
Introduction
Features are the language your model uses to describe users, items, and context. Get them right and even a logistic regression can outperform a transformer. Get them wrong and no architecture will save you. This post is a taxonomy of the feature families used in modern recommenders, the engineering tricks each requires, and the gotchas they hide.
1. The Feature Taxonomy
flowchart TD
F[Recommender Features] --> U[User Features]
F --> I[Item Features]
F --> C[Context Features]
F --> X[Cross / Interaction Features]
F --> S[Sequence Features]
U --> U1[Demographic]
U --> U2[Behavioral / Historical]
U --> U3[Profile / Preference]
U --> U4[Social / Graph]
I --> I1[Content / Metadata]
I --> I2[Visual / Audio Embeddings]
I --> I3[Text / NLP Embeddings]
I --> I4[Statistical / Popularity]
C --> C1[Temporal]
C --> C2[Location / Device]
C --> C3[Session]
X --> X1[User x Item history]
X --> X2[User x Category affinity]
S --> S1[Last-N clicks]
S --> S2[Time-decayed actions]
2. User Features
2.1 Demographic
Age, gender, country, language. Pros: stable, cheap. Cons: privacy-sensitive (GDPR/CCPA), sparse, often missing or self-reported.
Production note: demographic features often add little lift once behavioral features are present — but they’re crucial for cold start (Blog 18).
2.2 Behavioral / Historical
Counts and aggregates over user actions: clicks, purchases, watch time, ratings, skips.
| Feature | Definition |
|---|---|
n_clicks_7d |
Clicks in last 7 days |
purchase_amount_30d |
dollars spent in last 30 days |
avg_dwell_time |
Mean seconds per view |
last_session_gap_hr |
Hours since last session |
category_affinity_score |
Fraction of clicks in each category |
These are typically computed in a feature store (Blog 22) with online/offline parity guaranteed by point-in-time joins.
2.3 Profile / Preference
Explicit signals — followed creators, saved searches, “thumbs up”, muted categories.
2.4 Social / Graph
Friends, follows, co-views. Powers graph models (Blog 10). Features: number of friends who liked item, friend-of-friend signals, embedding from node2vec on the social graph.
3. Item Features
3.1 Content / Metadata
Title, brand, category, price, tags. Categorical features are typically hashed + embedded.
3.2 Text Embeddings
Title and description → embeddings via BERT, sentence-transformers, or fastText.
3.3 Visual / Audio
Image embeddings (ResNet, CLIP) for fashion/visual products. Audio embeddings (VGGish, MERT) for music.
3.4 Statistical / Popularity
CTR, CVR, average rating, click count over various time windows. Beware of leakage — must be computed as of the prediction time.
4. Context Features
| Type | Examples |
|---|---|
| Temporal | hour-of-day, day-of-week, is-weekend, days-since-launch |
| Location | country, city, geohash, store-locality |
| Device | iOS/Android, app version, screen size, connection type |
| Session | session position, scroll depth, in-app referrer, query (for search) |
import pandas as pd
df["hour"] = pd.to_datetime(df["ts"], unit="s").dt.hour
df["is_weekend"] = pd.to_datetime(df["ts"], unit="s").dt.dayofweek >= 5
5. Cross & Interaction Features
The deep learning revolution started by automating cross features (Wide & Deep, DCN — Blog 06/07). But hand-crafted crosses still beat neural ones in many shops:
user_country x item_categorydevice x ad_creative_sizequery_intent x item_brand
In TensorFlow:
crossed = tf.feature_column.crossed_column(
["user_country", "item_category"], hash_bucket_size=10000)
6. Sequence Features
Users are sequences, not bag-of-actions. Modern systems feed last-N item IDs + dwell times into transformer encoders (Blog 08).
# 50 most recent item ids, padded with 0
seq = user_history[-50:]
seq = [0] * (50 - len(seq)) + seq
7. Engineering Tricks
7.1 Categorical Encoding
- One-hot: only for low-cardinality
- Hashing trick: fast, no vocab maintenance, allows collisions on purpose
- Embeddings: $d \approx \min(50, \lceil n^{0.25} \rceil)$
- Target encoding: replace category with smoothed mean target (careful with leakage — use out-of-fold)
7.2 Numerical
- Log-transform heavy-tailed features (revenue, view-count)
- Bucketize then embed for non-monotone effects
- Standardize for neural nets
7.3 Missing Values
- Use explicit “missing” embedding, not zero — zero collides with informative inputs.
7.4 Freshness
Cache hot features in Redis; tier the rest in offline parquet + DynamoDB.
8. Feature Importance & Selection
# Permutation importance with LightGBM
import lightgbm as lgb
from sklearn.inspection import permutation_importance
model = lgb.LGBMClassifier(n_estimators=200)
model.fit(X_train, y_train)
r = permutation_importance(model, X_val, y_val, n_repeats=5, random_state=0)
imp = pd.Series(r.importances_mean, index=X_val.columns).sort_values(ascending=False)
print(imp.head(20))
For deep models, use integrated gradients or SHAP sparingly — they’re expensive at scale.
9. End-to-End Code: Build a Feature Table on RetailRocket
# pip install pandas pyarrow
import pandas as pd
# RetailRocket events: timestamp, visitorid, event, itemid, transactionid
events = pd.read_csv("events.csv")
events["ts"] = pd.to_datetime(events["timestamp"], unit="ms")
def user_features(events, as_of):
past = events[events["ts"] < as_of]
g = past.groupby("visitorid")
return pd.DataFrame({
"n_views_7d": g["event"].apply(lambda x: (x == "view").sum()),
"n_carts_7d": g["event"].apply(lambda x: (x == "addtocart").sum()),
"n_purch_7d": g["event"].apply(lambda x: (x == "transaction").sum()),
"last_seen_hr": (as_of - g["ts"].max()).dt.total_seconds() / 3600,
}).reset_index()
def item_features(events, as_of):
past = events[events["ts"] < as_of]
g = past.groupby("itemid")
return pd.DataFrame({
"item_ctr_30d": g["event"].apply(lambda x: (x == "addtocart").sum() / max(1, len(x))),
"item_popularity": g.size(),
}).reset_index()
snapshot = pd.Timestamp("2015-09-01")
u_feat = user_features(events, snapshot)
i_feat = item_features(events, snapshot)
print(u_feat.head(), i_feat.head())
This is essentially what a feature store does at scale, with point-in-time correctness guarantees.
10. Feature Engineering Mistakes (and Fixes)
| Mistake | Fix |
|---|---|
| Computing features over the full dataset → leakage | Always join “as of” prediction time |
| One-hot on millions of categories | Hash trick + embedding |
| Forgetting timezone | Store epoch + explicit tz |
| Train/serve skew | Use same code via a feature store |
| Stale features in real time | Tiered caching, TTLs |
| Logging predictions but not features | Log full feature vector |
11. Public Datasets
- MovieLens — has tag genome (rich item features) — https://grouplens.org/datasets/movielens/
- Amazon Reviews 2018 — title, brand, image, price — https://nijianmo.github.io/amazon/
- H&M Personalized Fashion — demographic + images — https://www.kaggle.com/c/h-and-m-personalized-fashion-recommendations
- Criteo Display Advertising — 26 categorical + 13 numerical anonymized features — https://www.kaggle.com/c/criteo-display-ad-challenge/data
- Avazu CTR — 24 features for mobile ads — https://www.kaggle.com/c/avazu-ctr-prediction
12. Further Reading
- Cheng et al., Wide & Deep Learning for Recommender Systems (DLRS 2016)
- He & Chua, NFM: Neural Factorization Machines (SIGIR 2017)
- Covington et al., Deep Neural Networks for YouTube Recommendations (RecSys 2016) — masterclass in features
- Tecton & Feast docs on feature stores (Blog 22)